Blame winpr/libwinpr/sspi/Kerberos/kerberos.c

Packit Service fa4841
/**
Packit Service fa4841
 * FreeRDP: A Remote Desktop Protocol Client
Packit Service fa4841
 * Kerberos Auth Protocol
Packit Service fa4841
 *
Packit Service fa4841
 * Copyright 2015 ANSSI, Author Thomas Calderon
Packit Service fa4841
 * Copyright 2017 Dorian Ducournau <dorian.ducournau@gmail.com>
Packit Service fa4841
 *
Packit Service fa4841
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit Service fa4841
 * you may not use this file except in compliance with the License.
Packit Service fa4841
 * You may obtain a copy of the License at
Packit Service fa4841
 *
Packit Service fa4841
 * http://www.apache.org/licenses/LICENSE-2.0
Packit Service fa4841
 *
Packit Service fa4841
 * Unless required by applicable law or agreed to in writing, software
Packit Service fa4841
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service fa4841
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service fa4841
 * See the License for the specific language governing permissions and
Packit Service fa4841
 * limitations under the License.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
#ifdef HAVE_CONFIG_H
Packit Service fa4841
#include "config.h"
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
#include <stdio.h>
Packit Service fa4841
#include <stdlib.h>
Packit Service fa4841
#include <string.h>
Packit Service fa4841
#include <errno.h>
Packit Service fa4841
#include <fcntl.h>
Packit Service fa4841
Packit Service fa4841
#include <winpr/crt.h>
Packit Service fa4841
#include <winpr/sspi.h>
Packit Service fa4841
#include <winpr/print.h>
Packit Service fa4841
#include <winpr/sysinfo.h>
Packit Service fa4841
#include <winpr/registry.h>
Packit Service fa4841
Packit Service fa4841
#include "kerberos.h"
Packit Service fa4841
Packit Service fa4841
#ifdef WITH_GSSAPI_HEIMDAL
Packit Service fa4841
#include <krb5-protos.h>
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
#include "../sspi.h"
Packit Service fa4841
#include "../../log.h"
Packit Service fa4841
#define TAG WINPR_TAG("sspi.Kerberos")
Packit Service fa4841
Packit Service fa4841
struct _KRB_CONTEXT
Packit Service fa4841
{
Packit Service fa4841
	CtxtHandle context;
Packit Service fa4841
	SSPI_CREDENTIALS* credentials;
Packit Service fa4841
	SEC_WINNT_AUTH_IDENTITY identity;
Packit Service fa4841
Packit Service fa4841
	/* GSSAPI */
Packit Service fa4841
	UINT32 major_status;
Packit Service fa4841
	UINT32 minor_status;
Packit Service fa4841
	UINT32 actual_time;
Packit Service fa4841
	sspi_gss_cred_id_t cred;
Packit Service fa4841
	sspi_gss_ctx_id_t gss_ctx;
Packit Service fa4841
	sspi_gss_name_t target_name;
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
static const char* KRB_PACKAGE_NAME = "Kerberos";
Packit Service fa4841
Packit Service fa4841
const SecPkgInfoA KERBEROS_SecPkgInfoA = {
Packit Service fa4841
	0x000F3BBF,                 /* fCapabilities */
Packit Service fa4841
	1,                          /* wVersion */
Packit Service fa4841
	0x0010,                     /* wRPCID */
Packit Service fa4841
	0x0000BB80,                 /* cbMaxToken : 48k bytes maximum for Windows Server 2012 */
Packit Service fa4841
	"Kerberos",                 /* Name */
Packit Service fa4841
	"Kerberos Security Package" /* Comment */
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
static WCHAR KERBEROS_SecPkgInfoW_Name[] = { 'K', 'e', 'r', 'b', 'e', 'r', 'o', 's', '\0' };
Packit Service fa4841
Packit Service fa4841
static WCHAR KERBEROS_SecPkgInfoW_Comment[] = { 'K', 'e', 'r', 'b', 'e', 'r', 'o', 's', ' ',
Packit Service fa4841
	                                            'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ',
Packit Service fa4841
	                                            'P', 'a', 'c', 'k', 'a', 'g', 'e', '\0' };
Packit Service fa4841
Packit Service fa4841
const SecPkgInfoW KERBEROS_SecPkgInfoW = {
Packit Service fa4841
	0x000F3BBF,                  /* fCapabilities */
Packit Service fa4841
	1,                           /* wVersion */
Packit Service fa4841
	0x0010,                      /* wRPCID */
Packit Service fa4841
	0x0000BB80,                  /* cbMaxToken : 48k bytes maximum for Windows Server 2012 */
Packit Service fa4841
	KERBEROS_SecPkgInfoW_Name,   /* Name */
Packit Service fa4841
	KERBEROS_SecPkgInfoW_Comment /* Comment */
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
static sspi_gss_OID_desc g_SSPI_GSS_C_SPNEGO_KRB5 = {
Packit Service fa4841
	9, (void*)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
Packit Service fa4841
};
Packit Service fa4841
static sspi_gss_OID SSPI_GSS_C_SPNEGO_KRB5 = &g_SSPI_GSS_C_SPNEGO_KRB5;
Packit Service fa4841
Packit Service fa4841
static KRB_CONTEXT* kerberos_ContextNew(void)
Packit Service fa4841
{
Packit Service fa4841
	KRB_CONTEXT* context;
Packit Service fa4841
	context = (KRB_CONTEXT*)calloc(1, sizeof(KRB_CONTEXT));
Packit Service fa4841
Packit Service fa4841
	if (!context)
Packit Service fa4841
		return NULL;
Packit Service fa4841
Packit Service fa4841
	context->minor_status = 0;
Packit Service fa4841
	context->major_status = 0;
Packit Service fa4841
	context->gss_ctx = SSPI_GSS_C_NO_CONTEXT;
Packit Service fa4841
	context->cred = SSPI_GSS_C_NO_CREDENTIAL;
Packit Service fa4841
	return context;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static void kerberos_ContextFree(KRB_CONTEXT* context)
Packit Service fa4841
{
Packit Service fa4841
	UINT32 minor_status;
Packit Service fa4841
Packit Service fa4841
	if (!context)
Packit Service fa4841
		return;
Packit Service fa4841
Packit Service fa4841
	if (context->target_name)
Packit Service fa4841
	{
Packit Service fa4841
		sspi_gss_release_name(&minor_status, &context->target_name);
Packit Service fa4841
		context->target_name = NULL;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (context->gss_ctx)
Packit Service fa4841
	{
Packit Service fa4841
		sspi_gss_delete_sec_context(&minor_status, &context->gss_ctx, SSPI_GSS_C_NO_BUFFER);
Packit Service fa4841
		context->gss_ctx = SSPI_GSS_C_NO_CONTEXT;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	free(context);
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleW(
Packit Service fa4841
    SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
Packit Service fa4841
    void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
Packit Service fa4841
    PTimeStamp ptsExpiry)
Packit Service fa4841
{
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleA(
Packit Service fa4841
    SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
Packit Service fa4841
    void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
Packit Service fa4841
    PTimeStamp ptsExpiry)
Packit Service fa4841
{
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_FreeCredentialsHandle(PCredHandle phCredential)
Packit Service fa4841
{
Packit Service fa4841
	SSPI_CREDENTIALS* credentials;
Packit Service fa4841
Packit Service fa4841
	if (!phCredential)
Packit Service fa4841
		return SEC_E_INVALID_HANDLE;
Packit Service fa4841
Packit Service fa4841
	credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
Packit Service fa4841
Packit Service fa4841
	if (!credentials)
Packit Service fa4841
		return SEC_E_INVALID_HANDLE;
Packit Service fa4841
Packit Service fa4841
	sspi_CredentialsFree(credentials);
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesW(PCredHandle phCredential,
Packit Service fa4841
                                                                      ULONG ulAttribute,
Packit Service fa4841
                                                                      void* pBuffer)
Packit Service fa4841
{
Packit Service fa4841
	if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
Packit Service fa4841
	{
Packit Service fa4841
		return SEC_E_OK;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return SEC_E_UNSUPPORTED_FUNCTION;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesA(PCredHandle phCredential,
Packit Service fa4841
                                                                      ULONG ulAttribute,
Packit Service fa4841
                                                                      void* pBuffer)
Packit Service fa4841
{
Packit Service fa4841
	return kerberos_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextW(
Packit Service fa4841
    PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
Packit Service fa4841
    ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
Packit Service fa4841
    PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr, PTimeStamp ptsExpiry)
Packit Service fa4841
{
Packit Service fa4841
	return SEC_E_UNSUPPORTED_FUNCTION;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static int kerberos_SetContextServicePrincipalNameA(KRB_CONTEXT* context,
Packit Service fa4841
                                                    SEC_CHAR* ServicePrincipalName)
Packit Service fa4841
{
Packit Service fa4841
	char* p;
Packit Service fa4841
	UINT32 major_status;
Packit Service fa4841
	UINT32 minor_status;
Packit Service fa4841
	char* gss_name = NULL;
Packit Service fa4841
	sspi_gss_buffer_desc name_buffer;
Packit Service fa4841
Packit Service fa4841
	if (!ServicePrincipalName)
Packit Service fa4841
	{
Packit Service fa4841
		context->target_name = NULL;
Packit Service fa4841
		return 1;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* GSSAPI expects a SPN of type <service>@FQDN, let's construct it */
Packit Service fa4841
	gss_name = _strdup(ServicePrincipalName);
Packit Service fa4841
Packit Service fa4841
	if (!gss_name)
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service fa4841
	p = strchr(gss_name, '/');
Packit Service fa4841
Packit Service fa4841
	if (p)
Packit Service fa4841
		*p = '@';
Packit Service fa4841
Packit Service fa4841
	name_buffer.value = gss_name;
Packit Service fa4841
	name_buffer.length = strlen(gss_name) + 1;
Packit Service fa4841
	major_status = sspi_gss_import_name(&minor_status, &name_buffer,
Packit Service fa4841
	                                    SSPI_GSS_C_NT_HOSTBASED_SERVICE, &(context->target_name));
Packit Service fa4841
	free(gss_name);
Packit Service fa4841
Packit Service fa4841
	if (SSPI_GSS_ERROR(major_status))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error: gss_import_name failed");
Packit Service fa4841
		return -1;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return 1;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
#ifdef WITH_GSSAPI
Packit Service fa4841
static krb5_error_code KRB5_CALLCONV acquire_cred(krb5_context ctx, krb5_principal client,
Packit Service fa4841
                                                  const char* password)
Packit Service fa4841
{
Packit Service fa4841
	krb5_error_code ret;
Packit Service fa4841
	krb5_creds creds;
Packit Service fa4841
	krb5_deltat starttime = 0;
Packit Service fa4841
	krb5_get_init_creds_opt* options = NULL;
Packit Service fa4841
	krb5_ccache ccache;
Packit Service fa4841
	krb5_init_creds_context init_ctx = NULL;
Packit Service fa4841
Packit Service fa4841
	/* Get default ccache */
Packit Service fa4841
	if ((ret = krb5_cc_default(ctx, &ccache)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error while getting default ccache");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if ((ret = krb5_cc_initialize(ctx, ccache, client)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error: could not initialize ccache");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	memset(&creds, 0, sizeof(creds));
Packit Service fa4841
Packit Service fa4841
	if ((ret = krb5_get_init_creds_opt_alloc(ctx, &options)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error while allocating options");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* Set default options */
Packit Service fa4841
	krb5_get_init_creds_opt_set_forwardable(options, 0);
Packit Service fa4841
	krb5_get_init_creds_opt_set_proxiable(options, 0);
Packit Service fa4841
#ifdef WITH_GSSAPI_MIT
Packit Service fa4841
Packit Service fa4841
	/* for MIT we specify ccache output using an option */
Packit Service fa4841
	if ((ret = krb5_get_init_creds_opt_set_out_ccache(ctx, options, ccache)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error while setting ccache output");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
	if ((ret = krb5_init_creds_init(ctx, client, NULL, NULL, starttime, options, &init_ctx)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error krb5_init_creds_init failed");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if ((ret = krb5_init_creds_set_password(ctx, init_ctx, password)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error krb5_init_creds_set_password failed");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* Get credentials */
Packit Service fa4841
	if ((ret = krb5_init_creds_get(ctx, init_ctx)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error while getting credentials");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* Retrieve credentials */
Packit Service fa4841
	if ((ret = krb5_init_creds_get_creds(ctx, init_ctx, &creds)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error while retrieving credentials");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
#ifdef WITH_GSSAPI_HEIMDAL
Packit Service fa4841
Packit Service fa4841
	/* For Heimdal, we use this function to store credentials */
Packit Service fa4841
	if ((ret = krb5_init_creds_store(ctx, init_ctx, ccache)))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error while storing credentials");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
#endif
Packit Service fa4841
cleanup:
Packit Service fa4841
	krb5_free_cred_contents(ctx, &creds);
Packit Service fa4841
#ifdef HAVE_AT_LEAST_KRB_V1_13
Packit Service fa4841
Packit Service fa4841
	/* MIT Kerberos version 1.13 at minimum.
Packit Service fa4841
	 * For releases 1.12 and previous, krb5_get_init_creds_opt structure
Packit Service fa4841
	 * is freed in krb5_init_creds_free() */
Packit Service fa4841
	if (options)
Packit Service fa4841
		krb5_get_init_creds_opt_free(ctx, options);
Packit Service fa4841
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
	if (init_ctx)
Packit Service fa4841
		krb5_init_creds_free(ctx, init_ctx);
Packit Service fa4841
Packit Service fa4841
	if (ccache)
Packit Service fa4841
		krb5_cc_close(ctx, ccache);
Packit Service fa4841
Packit Service fa4841
	return ret;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static int init_creds(LPCWSTR username, size_t username_len, LPCWSTR password, size_t password_len)
Packit Service fa4841
{
Packit Service fa4841
	krb5_error_code ret = 0;
Packit Service fa4841
	krb5_context ctx = NULL;
Packit Service fa4841
	krb5_principal principal = NULL;
Packit Service fa4841
	char* krb_name = NULL;
Packit Service fa4841
	char* lusername = NULL;
Packit Service fa4841
	char* lrealm = NULL;
Packit Service fa4841
	char* lpassword = NULL;
Packit Service fa4841
	int flags = 0;
Packit Service fa4841
	char* pstr = NULL;
Packit Service fa4841
	size_t krb_name_len = 0;
Packit Service fa4841
	size_t lrealm_len = 0;
Packit Service fa4841
	size_t lusername_len = 0;
Packit Service fa4841
	int status = 0;
Packit Service fa4841
	status = ConvertFromUnicode(CP_UTF8, 0, username, username_len, &lusername, 0, NULL, NULL);
Packit Service fa4841
Packit Service fa4841
	if (status <= 0)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Failed to convert username");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	status = ConvertFromUnicode(CP_UTF8, 0, password, password_len, &lpassword, 0, NULL, NULL);
Packit Service fa4841
Packit Service fa4841
	if (status <= 0)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Failed to convert password");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* Could call krb5_init_secure_context, but it disallows user overrides */
Packit Service fa4841
	ret = krb5_init_context(&ctx;;
Packit Service fa4841
Packit Service fa4841
	if (ret)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error: while initializing Kerberos 5 library");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	ret = krb5_get_default_realm(ctx, &lrealm);
Packit Service fa4841
Packit Service fa4841
	if (ret)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_WARN(TAG, "could not get Kerberos default realm");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	lrealm_len = strlen(lrealm);
Packit Service fa4841
	lusername_len = strlen(lusername);
Packit Service fa4841
	krb_name_len = lusername_len + lrealm_len + 1; // +1 for '@'
Packit Service fa4841
	krb_name = calloc(krb_name_len + 1, sizeof(char));
Packit Service fa4841
Packit Service fa4841
	if (!krb_name)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "could not allocate memory for string rep of principal\n");
Packit Service fa4841
		ret = -1;
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	/* Set buffer */
Packit Service fa4841
	_snprintf(krb_name, krb_name_len + 1, "%s@%s", lusername, lrealm);
Packit Service fa4841
#ifdef WITH_DEBUG_NLA
Packit Service fa4841
	WLog_DBG(TAG, "copied string is %s\n", krb_name);
Packit Service fa4841
#endif
Packit Service fa4841
	pstr = strchr(lusername, '@');
Packit Service fa4841
Packit Service fa4841
	if (pstr != NULL)
Packit Service fa4841
		flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE;
Packit Service fa4841
Packit Service fa4841
	/* Use the specified principal name. */
Packit Service fa4841
	ret = krb5_parse_name_flags(ctx, krb_name, flags, &principal);
Packit Service fa4841
Packit Service fa4841
	if (ret)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "could not convert %s to principal", krb_name);
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	ret = acquire_cred(ctx, principal, lpassword);
Packit Service fa4841
Packit Service fa4841
	if (ret)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "Kerberos credentials not found and could not be acquired");
Packit Service fa4841
		goto cleanup;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
cleanup:
Packit Service fa4841
	free(lusername);
Packit Service fa4841
	free(lpassword);
Packit Service fa4841
Packit Service fa4841
	if (krb_name)
Packit Service fa4841
		free(krb_name);
Packit Service fa4841
Packit Service fa4841
	if (lrealm)
Packit Service fa4841
		krb5_free_default_realm(ctx, lrealm);
Packit Service fa4841
Packit Service fa4841
	if (principal)
Packit Service fa4841
		krb5_free_principal(ctx, principal);
Packit Service fa4841
Packit Service fa4841
	if (ctx)
Packit Service fa4841
		krb5_free_context(ctx);
Packit Service fa4841
Packit Service fa4841
	return ret;
Packit Service fa4841
}
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA(
Packit Service fa4841
    PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,
Packit Service fa4841
    ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
Packit Service fa4841
    PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG* pfContextAttr, PTimeStamp ptsExpiry)
Packit Service fa4841
{
Packit Service fa4841
	KRB_CONTEXT* context;
Packit Service fa4841
	SSPI_CREDENTIALS* credentials;
Packit Service fa4841
	PSecBuffer input_buffer = NULL;
Packit Service fa4841
	PSecBuffer output_buffer = NULL;
Packit Service fa4841
	sspi_gss_buffer_desc input_tok = { 0 };
Packit Service fa4841
	sspi_gss_buffer_desc output_tok = { 0 };
Packit Service fa4841
	sspi_gss_OID actual_mech;
Packit Service fa4841
	sspi_gss_OID desired_mech;
Packit Service fa4841
	UINT32 actual_services;
Packit Service fa4841
	input_tok.length = 0;
Packit Service fa4841
	output_tok.length = 0;
Packit Service fa4841
	desired_mech = SSPI_GSS_C_SPNEGO_KRB5;
Packit Service fa4841
	context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
Packit Service fa4841
Packit Service fa4841
	if (!context)
Packit Service fa4841
	{
Packit Service fa4841
		context = kerberos_ContextNew();
Packit Service fa4841
Packit Service fa4841
		if (!context)
Packit Service fa4841
			return SEC_E_INSUFFICIENT_MEMORY;
Packit Service fa4841
Packit Service fa4841
		credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
Packit Service fa4841
		context->credentials = credentials;
Packit Service fa4841
Packit Service fa4841
		if (kerberos_SetContextServicePrincipalNameA(context, pszTargetName) < 0)
Packit Service fa4841
		{
Packit Service fa4841
			kerberos_ContextFree(context);
Packit Service fa4841
			return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
		sspi_SecureHandleSetLowerPointer(phNewContext, context);
Packit Service fa4841
		sspi_SecureHandleSetUpperPointer(phNewContext, (void*)KRB_PACKAGE_NAME);
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!pInput)
Packit Service fa4841
	{
Packit Service fa4841
#if defined(WITH_GSSAPI)
Packit Service fa4841
		context->major_status = sspi_gss_init_sec_context(
Packit Service fa4841
		    &(context->minor_status), context->cred, &(context->gss_ctx), context->target_name,
Packit Service fa4841
		    desired_mech, SSPI_GSS_C_MUTUAL_FLAG | SSPI_GSS_C_DELEG_FLAG, SSPI_GSS_C_INDEFINITE,
Packit Service fa4841
		    SSPI_GSS_C_NO_CHANNEL_BINDINGS, &input_tok, &actual_mech, &output_tok, &actual_services,
Packit Service fa4841
		    &(context->actual_time));
Packit Service fa4841
Packit Service fa4841
		if (SSPI_GSS_ERROR(context->major_status))
Packit Service fa4841
		{
Packit Service fa4841
			/* GSSAPI failed because we do not have credentials */
Packit Service fa4841
			if (context->major_status & SSPI_GSS_S_NO_CRED)
Packit Service fa4841
			{
Packit Service fa4841
				/* Then let's try to acquire credentials using login and password,
Packit Service fa4841
				 * and only those two, means not with a smartcard.
Packit Service fa4841
				 * If we use smartcard-logon, the credentials have already
Packit Service fa4841
				 * been acquired by pkinit process. If not, returned error previously.
Packit Service fa4841
				 */
Packit Service fa4841
				if (init_creds(context->credentials->identity.User,
Packit Service fa4841
				               context->credentials->identity.UserLength,
Packit Service fa4841
				               context->credentials->identity.Password,
Packit Service fa4841
				               context->credentials->identity.PasswordLength))
Packit Service fa4841
					return SEC_E_NO_CREDENTIALS;
Packit Service fa4841
Packit Service fa4841
				WLog_INFO(TAG, "Authenticated to Kerberos v5 via login/password");
Packit Service fa4841
				/* retry GSSAPI call */
Packit Service fa4841
				context->major_status = sspi_gss_init_sec_context(
Packit Service fa4841
				    &(context->minor_status), context->cred, &(context->gss_ctx),
Packit Service fa4841
				    context->target_name, desired_mech,
Packit Service fa4841
				    SSPI_GSS_C_MUTUAL_FLAG | SSPI_GSS_C_DELEG_FLAG, SSPI_GSS_C_INDEFINITE,
Packit Service fa4841
				    SSPI_GSS_C_NO_CHANNEL_BINDINGS, &input_tok, &actual_mech, &output_tok,
Packit Service fa4841
				    &actual_services, &(context->actual_time));
Packit Service fa4841
Packit Service fa4841
				if (SSPI_GSS_ERROR(context->major_status))
Packit Service fa4841
				{
Packit Service fa4841
					/* We can't use Kerberos */
Packit Service fa4841
					WLog_ERR(TAG, "Init GSS security context failed : can't use Kerberos");
Packit Service fa4841
					return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
				}
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
Packit Service fa4841
#endif
Packit Service fa4841
Packit Service fa4841
		if (context->major_status & SSPI_GSS_S_CONTINUE_NEEDED)
Packit Service fa4841
		{
Packit Service fa4841
			if (output_tok.length != 0)
Packit Service fa4841
			{
Packit Service fa4841
				if (!pOutput)
Packit Service fa4841
					return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
				if (pOutput->cBuffers < 1)
Packit Service fa4841
					return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
				output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
Packit Service fa4841
Packit Service fa4841
				if (!output_buffer)
Packit Service fa4841
					return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
				if (output_buffer->cbBuffer < 1)
Packit Service fa4841
					return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
				CopyMemory(output_buffer->pvBuffer, output_tok.value, output_tok.length);
Packit Service fa4841
				output_buffer->cbBuffer = output_tok.length;
Packit Service fa4841
				sspi_gss_release_buffer(&(context->minor_status), &output_tok);
Packit Service fa4841
				return SEC_I_CONTINUE_NEEDED;
Packit Service fa4841
			}
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
	else
Packit Service fa4841
	{
Packit Service fa4841
		input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
Packit Service fa4841
Packit Service fa4841
		if (!input_buffer)
Packit Service fa4841
			return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
		if (input_buffer->cbBuffer < 1)
Packit Service fa4841
			return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
		input_tok.value = input_buffer->pvBuffer;
Packit Service fa4841
		input_tok.length = input_buffer->cbBuffer;
Packit Service fa4841
		context->major_status = sspi_gss_init_sec_context(
Packit Service fa4841
		    &(context->minor_status), context->cred, &(context->gss_ctx), context->target_name,
Packit Service fa4841
		    desired_mech, SSPI_GSS_C_MUTUAL_FLAG | SSPI_GSS_C_DELEG_FLAG, SSPI_GSS_C_INDEFINITE,
Packit Service fa4841
		    SSPI_GSS_C_NO_CHANNEL_BINDINGS, &input_tok, &actual_mech, &output_tok, &actual_services,
Packit Service fa4841
		    &(context->actual_time));
Packit Service fa4841
Packit Service fa4841
		if (SSPI_GSS_ERROR(context->major_status))
Packit Service fa4841
			return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
Packit Service fa4841
		if (output_tok.length == 0)
Packit Service fa4841
		{
Packit Service fa4841
			/* Free output_buffer to detect second call in NLA */
Packit Service fa4841
			output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
Packit Service fa4841
			sspi_SecBufferFree(output_buffer);
Packit Service fa4841
			return SEC_E_OK;
Packit Service fa4841
		}
Packit Service fa4841
		else
Packit Service fa4841
		{
Packit Service fa4841
			return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
		}
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_DeleteSecurityContext(PCtxtHandle phContext)
Packit Service fa4841
{
Packit Service fa4841
	KRB_CONTEXT* context;
Packit Service fa4841
	context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
Packit Service fa4841
Packit Service fa4841
	if (!context)
Packit Service fa4841
		return SEC_E_INVALID_HANDLE;
Packit Service fa4841
Packit Service fa4841
	kerberos_ContextFree(context);
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesW(PCtxtHandle phContext,
Packit Service fa4841
                                                                  ULONG ulAttribute, void* pBuffer)
Packit Service fa4841
{
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(PCtxtHandle phContext,
Packit Service fa4841
                                                                  ULONG ulAttribute, void* pBuffer)
Packit Service fa4841
{
Packit Service fa4841
	if (!phContext)
Packit Service fa4841
		return SEC_E_INVALID_HANDLE;
Packit Service fa4841
Packit Service fa4841
	if (!pBuffer)
Packit Service fa4841
		return SEC_E_INSUFFICIENT_MEMORY;
Packit Service fa4841
Packit Service fa4841
	if (ulAttribute == SECPKG_ATTR_SIZES)
Packit Service fa4841
	{
Packit Service fa4841
		SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer;
Packit Service fa4841
		/* The MaxTokenSize by default is 12,000 bytes. This has been the default value
Packit Service fa4841
		 * since Windows 2000 SP2 and still remains in Windows 7 and Windows 2008 R2.
Packit Service fa4841
		 *  For Windows Server 2012, the default value of the MaxTokenSize registry
Packit Service fa4841
		 *  entry is 48,000 bytes.*/
Packit Service fa4841
		ContextSizes->cbMaxToken = KERBEROS_SecPkgInfoA.cbMaxToken;
Packit Service fa4841
		ContextSizes->cbMaxSignature = 0; /* means verify not supported */
Packit Service fa4841
		ContextSizes->cbBlockSize = 0;    /* padding not used */
Packit Service fa4841
		ContextSizes->cbSecurityTrailer =
Packit Service fa4841
		    60; /* gss_wrap adds additional 60 bytes for encrypt message */
Packit Service fa4841
		return SEC_E_OK;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return SEC_E_UNSUPPORTED_FUNCTION;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
Packit Service fa4841
                                                         PSecBufferDesc pMessage,
Packit Service fa4841
                                                         ULONG MessageSeqNo)
Packit Service fa4841
{
Packit Service fa4841
	int index;
Packit Service fa4841
	int conf_state;
Packit Service fa4841
	UINT32 major_status;
Packit Service fa4841
	UINT32 minor_status;
Packit Service fa4841
	KRB_CONTEXT* context;
Packit Service fa4841
	sspi_gss_buffer_desc input;
Packit Service fa4841
	sspi_gss_buffer_desc output;
Packit Service fa4841
	PSecBuffer data_buffer = NULL;
Packit Service fa4841
	context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
Packit Service fa4841
Packit Service fa4841
	if (!context)
Packit Service fa4841
		return SEC_E_INVALID_HANDLE;
Packit Service fa4841
Packit Service fa4841
	for (index = 0; index < (int)pMessage->cBuffers; index++)
Packit Service fa4841
	{
Packit Service fa4841
		if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
Packit Service fa4841
			data_buffer = &pMessage->pBuffers[index];
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!data_buffer)
Packit Service fa4841
		return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
	input.value = data_buffer->pvBuffer;
Packit Service fa4841
	input.length = data_buffer->cbBuffer;
Packit Service fa4841
	major_status = sspi_gss_wrap(&minor_status, context->gss_ctx, TRUE, SSPI_GSS_C_QOP_DEFAULT,
Packit Service fa4841
	                             &input, &conf_state, &output);
Packit Service fa4841
Packit Service fa4841
	if (SSPI_GSS_ERROR(major_status))
Packit Service fa4841
		return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
Packit Service fa4841
	if (conf_state == 0)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error: gss_wrap confidentiality was not applied");
Packit Service fa4841
		sspi_gss_release_buffer(&minor_status, &output);
Packit Service fa4841
		return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	CopyMemory(data_buffer->pvBuffer, output.value, output.length);
Packit Service fa4841
	sspi_gss_release_buffer(&minor_status, &output);
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(PCtxtHandle phContext,
Packit Service fa4841
                                                         PSecBufferDesc pMessage,
Packit Service fa4841
                                                         ULONG MessageSeqNo, ULONG* pfQOP)
Packit Service fa4841
{
Packit Service fa4841
	int index;
Packit Service fa4841
	int conf_state;
Packit Service fa4841
	UINT32 major_status;
Packit Service fa4841
	UINT32 minor_status;
Packit Service fa4841
	KRB_CONTEXT* context;
Packit Service fa4841
	sspi_gss_buffer_desc input_data;
Packit Service fa4841
	sspi_gss_buffer_desc output;
Packit Service fa4841
	PSecBuffer data_buffer_to_unwrap = NULL;
Packit Service fa4841
	context = (KRB_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
Packit Service fa4841
Packit Service fa4841
	if (!context)
Packit Service fa4841
		return SEC_E_INVALID_HANDLE;
Packit Service fa4841
Packit Service fa4841
	for (index = 0; index < (int)pMessage->cBuffers; index++)
Packit Service fa4841
	{
Packit Service fa4841
		if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
Packit Service fa4841
			data_buffer_to_unwrap = &pMessage->pBuffers[index];
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!data_buffer_to_unwrap)
Packit Service fa4841
		return SEC_E_INVALID_TOKEN;
Packit Service fa4841
Packit Service fa4841
	/* unwrap encrypted TLS key AND its signature */
Packit Service fa4841
	input_data.value = data_buffer_to_unwrap->pvBuffer;
Packit Service fa4841
	input_data.length = data_buffer_to_unwrap->cbBuffer;
Packit Service fa4841
	major_status =
Packit Service fa4841
	    sspi_gss_unwrap(&minor_status, context->gss_ctx, &input_data, &output, &conf_state, NULL);
Packit Service fa4841
Packit Service fa4841
	if (SSPI_GSS_ERROR(major_status))
Packit Service fa4841
		return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
Packit Service fa4841
	if (conf_state == 0)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error: gss_unwrap confidentiality was not applied");
Packit Service fa4841
		sspi_gss_release_buffer(&minor_status, &output);
Packit Service fa4841
		return SEC_E_INTERNAL_ERROR;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	CopyMemory(data_buffer_to_unwrap->pvBuffer, output.value, output.length);
Packit Service fa4841
	sspi_gss_release_buffer(&minor_status, &output);
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
Packit Service fa4841
                                                        PSecBufferDesc pMessage, ULONG MessageSeqNo)
Packit Service fa4841
{
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SECURITY_STATUS SEC_ENTRY kerberos_VerifySignature(PCtxtHandle phContext,
Packit Service fa4841
                                                          PSecBufferDesc pMessage,
Packit Service fa4841
                                                          ULONG MessageSeqNo, ULONG* pfQOP)
Packit Service fa4841
{
Packit Service fa4841
	return SEC_E_OK;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
const SecurityFunctionTableA KERBEROS_SecurityFunctionTableA = {
Packit Service fa4841
	1,                                    /* dwVersion */
Packit Service fa4841
	NULL,                                 /* EnumerateSecurityPackages */
Packit Service fa4841
	kerberos_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
Packit Service fa4841
	kerberos_AcquireCredentialsHandleA,   /* AcquireCredentialsHandle */
Packit Service fa4841
	kerberos_FreeCredentialsHandle,       /* FreeCredentialsHandle */
Packit Service fa4841
	NULL,                                 /* Reserved2 */
Packit Service fa4841
	kerberos_InitializeSecurityContextA,  /* InitializeSecurityContext */
Packit Service fa4841
	NULL,                                 /* AcceptSecurityContext */
Packit Service fa4841
	NULL,                                 /* CompleteAuthToken */
Packit Service fa4841
	kerberos_DeleteSecurityContext,       /* DeleteSecurityContext */
Packit Service fa4841
	NULL,                                 /* ApplyControlToken */
Packit Service fa4841
	kerberos_QueryContextAttributesA,     /* QueryContextAttributes */
Packit Service fa4841
	NULL,                                 /* ImpersonateSecurityContext */
Packit Service fa4841
	NULL,                                 /* RevertSecurityContext */
Packit Service fa4841
	kerberos_MakeSignature,               /* MakeSignature */
Packit Service fa4841
	kerberos_VerifySignature,             /* VerifySignature */
Packit Service fa4841
	NULL,                                 /* FreeContextBuffer */
Packit Service fa4841
	NULL,                                 /* QuerySecurityPackageInfo */
Packit Service fa4841
	NULL,                                 /* Reserved3 */
Packit Service fa4841
	NULL,                                 /* Reserved4 */
Packit Service fa4841
	NULL,                                 /* ExportSecurityContext */
Packit Service fa4841
	NULL,                                 /* ImportSecurityContext */
Packit Service fa4841
	NULL,                                 /* AddCredentials */
Packit Service fa4841
	NULL,                                 /* Reserved8 */
Packit Service fa4841
	NULL,                                 /* QuerySecurityContextToken */
Packit Service fa4841
	kerberos_EncryptMessage,              /* EncryptMessage */
Packit Service fa4841
	kerberos_DecryptMessage,              /* DecryptMessage */
Packit Service fa4841
	NULL,                                 /* SetContextAttributes */
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
const SecurityFunctionTableW KERBEROS_SecurityFunctionTableW = {
Packit Service fa4841
	1,                                    /* dwVersion */
Packit Service fa4841
	NULL,                                 /* EnumerateSecurityPackages */
Packit Service fa4841
	kerberos_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
Packit Service fa4841
	kerberos_AcquireCredentialsHandleW,   /* AcquireCredentialsHandle */
Packit Service fa4841
	kerberos_FreeCredentialsHandle,       /* FreeCredentialsHandle */
Packit Service fa4841
	NULL,                                 /* Reserved2 */
Packit Service fa4841
	kerberos_InitializeSecurityContextW,  /* InitializeSecurityContext */
Packit Service fa4841
	NULL,                                 /* AcceptSecurityContext */
Packit Service fa4841
	NULL,                                 /* CompleteAuthToken */
Packit Service fa4841
	kerberos_DeleteSecurityContext,       /* DeleteSecurityContext */
Packit Service fa4841
	NULL,                                 /* ApplyControlToken */
Packit Service fa4841
	kerberos_QueryContextAttributesW,     /* QueryContextAttributes */
Packit Service fa4841
	NULL,                                 /* ImpersonateSecurityContext */
Packit Service fa4841
	NULL,                                 /* RevertSecurityContext */
Packit Service fa4841
	kerberos_MakeSignature,               /* MakeSignature */
Packit Service fa4841
	kerberos_VerifySignature,             /* VerifySignature */
Packit Service fa4841
	NULL,                                 /* FreeContextBuffer */
Packit Service fa4841
	NULL,                                 /* QuerySecurityPackageInfo */
Packit Service fa4841
	NULL,                                 /* Reserved3 */
Packit Service fa4841
	NULL,                                 /* Reserved4 */
Packit Service fa4841
	NULL,                                 /* ExportSecurityContext */
Packit Service fa4841
	NULL,                                 /* ImportSecurityContext */
Packit Service fa4841
	NULL,                                 /* AddCredentials */
Packit Service fa4841
	NULL,                                 /* Reserved8 */
Packit Service fa4841
	NULL,                                 /* QuerySecurityContextToken */
Packit Service fa4841
	kerberos_EncryptMessage,              /* EncryptMessage */
Packit Service fa4841
	kerberos_DecryptMessage,              /* DecryptMessage */
Packit Service fa4841
	NULL,                                 /* SetContextAttributes */
Packit Service fa4841
};