Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37045675
en ru br
Репозитории ALT
S:8.15.1-alt1
5.1: 1.1.44-alt1.M50P.1
4.1: 1.0.9-alt0.M41.35
4.0: 1.0.9-alt0.M40.35
+backports:1.0.9-alt0.M40.16
3.0: 20050715-alt0.1
+backports:20061212-alt0.M30.1
www.altlinux.org/Changes

Группа :: Эмуляторы
Пакет: wine

 Главная   Изменения   Спек   Патчи   Sources   Загрузить   Gear   Bugs and FR  Repocop 

Патч: 0001-kerberos.patch
Скачать


From 4f2fafdec8cd6d517ac01d259f112a61a41c62f7 Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Mon, 4 Sep 2017 15:54:34 +0300
Subject: [3/9] kerberos: Add support for KerbQueryTicketCacheMessage.
Content-Type: text/plain; charset=UTF-8
Based on the code written by George Popoff.
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
---
 dlls/kerberos/krb5_ap.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++-
 include/ntsecapi.h      |  96 ++++++++++++++++
 2 files changed, 392 insertions(+), 3 deletions(-)
diff --git a/dlls/kerberos/krb5_ap.c b/dlls/kerberos/krb5_ap.c
index 0134b94f6d..a0b6053af4 100644
--- a/dlls/kerberos/krb5_ap.c
+++ b/dlls/kerberos/krb5_ap.c
@@ -1,5 +1,6 @@
 /*
  * Copyright 2017 Dmitry Timoshkov
+ * Copyright 2017 George Popoff
  *
  * Kerberos5 Authentication Package
  *
@@ -30,6 +31,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "winnls.h"
 #include "sspi.h"
 #include "ntsecapi.h"
 #include "ntsecpkg.h"
@@ -48,8 +50,36 @@ static void *libkrb5_handle;
 
 #define MAKE_FUNCPTR(f) static typeof(f) * p_##f
 MAKE_FUNCPTR(krb5_init_context);
+MAKE_FUNCPTR(krb5_free_context);
+MAKE_FUNCPTR(krb5_cccol_cursor_new);
+MAKE_FUNCPTR(krb5_cccol_cursor_next);
+MAKE_FUNCPTR(krb5_cc_close);
+MAKE_FUNCPTR(krb5_cc_start_seq_get);
+MAKE_FUNCPTR(krb5_cc_end_seq_get);
+MAKE_FUNCPTR(krb5_cc_next_cred);
+MAKE_FUNCPTR(krb5_is_config_principal);
+MAKE_FUNCPTR(krb5_decode_ticket);
+MAKE_FUNCPTR(krb5_unparse_name);
+MAKE_FUNCPTR(krb5_unparse_name_flags);
+MAKE_FUNCPTR(krb5_free_unparsed_name);
+MAKE_FUNCPTR(krb5_free_cred_contents);
 #undef MAKE_FUNCPTR
 
+static void *heap_alloc(SIZE_T size)
+{
+    return HeapAlloc(GetProcessHeap(), 0, size);
+}
+
+static void *heap_realloc(void *p, SIZE_T size)
+{
+    return HeapReAlloc(GetProcessHeap(), 0, p, size);
+}
+
+static void heap_free(void *p)
+{
+    HeapFree(GetProcessHeap(), 0, p);
+}
+
 static void load_krb5(void)
 {
     if (!(libkrb5_handle = wine_dlopen(SONAME_LIBKRB5, RTLD_NOW, NULL, 0)))
@@ -66,6 +96,19 @@ static void load_krb5(void)
     }
 
     LOAD_FUNCPTR(krb5_init_context)
+    LOAD_FUNCPTR(krb5_free_context)
+    LOAD_FUNCPTR(krb5_cccol_cursor_new)
+    LOAD_FUNCPTR(krb5_cccol_cursor_next)
+    LOAD_FUNCPTR(krb5_cc_close)
+    LOAD_FUNCPTR(krb5_cc_start_seq_get)
+    LOAD_FUNCPTR(krb5_cc_end_seq_get)
+    LOAD_FUNCPTR(krb5_cc_next_cred)
+    LOAD_FUNCPTR(krb5_is_config_principal)
+    LOAD_FUNCPTR(krb5_decode_ticket)
+    LOAD_FUNCPTR(krb5_unparse_name)
+    LOAD_FUNCPTR(krb5_unparse_name_flags)
+    LOAD_FUNCPTR(krb5_free_unparsed_name)
+    LOAD_FUNCPTR(krb5_free_cred_contents)
 #undef LOAD_FUNCPTR
 
     return;
@@ -111,15 +154,265 @@ static NTSTATUS NTAPI krb5_LsaApInitializePackage(ULONG package_id, PLSA_DISPATC
     return STATUS_SUCCESS;
 }
 
+static NTSTATUS krb5_error_to_status(krb5_error_code error)
+{
+    switch (error)
+    {
+    case 0: return STATUS_SUCCESS;
+
+    default:
+        /* FIXME */
+        return STATUS_UNSUCCESSFUL;
+    }
+}
+
+static WCHAR *utf8_to_wstr(const char *utf8)
+{
+    int size;
+    WCHAR *wstr;
+
+    size = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
+    wstr = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+    if (wstr)
+        MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, size);
+
+    return wstr;
+}
+
+static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_TICKET_CACHE_INFO *ticket_info)
+{
+    NTSTATUS status;
+    krb5_cc_cursor cursor;
+    krb5_error_code error;
+    krb5_creds credentials;
+    krb5_ticket *ticket;
+    char *name_with_realm;
+    char *name_without_realm;
+    char *realm_name;
+    WCHAR *realm_nameW;
+    WCHAR *name_without_realmW;
+
+    error = p_krb5_cc_start_seq_get(context, cache, &cursor);
+    if (error) return krb5_error_to_status(error);
+
+    status = STATUS_SUCCESS;
+
+    for (;;)
+    {
+        error = p_krb5_cc_next_cred(context, cache, &cursor, &credentials);
+        if (error)
+        {
+            status = krb5_error_to_status(error);
+            break;
+        }
+
+        if (p_krb5_is_config_principal(context, credentials.server))
+        {
+            p_krb5_free_cred_contents(context, &credentials);
+            continue;
+        }
+
+        error = p_krb5_unparse_name(context, credentials.server, &name_with_realm);
+        if (error)
+        {
+            p_krb5_free_cred_contents(context, &credentials);
+            status = krb5_error_to_status(error);
+            break;
+        }
+
+        TRACE("name_with_realm: %s\n", debugstr_a(name_with_realm));
+
+        error = p_krb5_unparse_name_flags(context, credentials.server,
+            KRB5_PRINCIPAL_UNPARSE_NO_REALM, &name_without_realm);
+        if (error)
+        {
+            p_krb5_free_unparsed_name(context, name_with_realm);
+            p_krb5_free_cred_contents(context, &credentials);
+            status = krb5_error_to_status(error);
+            break;
+        }
+
+        TRACE("name_without_realm: %s\n", debugstr_a(name_without_realm));
+
+        name_without_realmW = utf8_to_wstr(name_without_realm);
+        RtlInitUnicodeString(&ticket_info->ServerName, name_without_realmW);
+
+        realm_name = name_with_realm;
+
+        /* Increment pointer while not contain realm name */
+        do {
+            ++realm_name;
+        } while (*realm_name != '@' && *realm_name != '\0');
+        ++realm_name;
+
+        /* realm_name - now contains only realm! */
+
+        realm_nameW = utf8_to_wstr(realm_name);
+        RtlInitUnicodeString(&ticket_info->RealmName, realm_nameW);
+
+        if (!credentials.times.starttime)
+            credentials.times.starttime = credentials.times.authtime;
+
+        /* TODO: if krb5_is_config_principal = true */
+        RtlSecondsSince1970ToTime(credentials.times.starttime, &ticket_info->StartTime);
+        RtlSecondsSince1970ToTime(credentials.times.endtime, &ticket_info->EndTime);
+        RtlSecondsSince1970ToTime(credentials.times.renew_till, &ticket_info->RenewTime);
+
+        ticket_info->TicketFlags = credentials.ticket_flags;
+
+        error = p_krb5_decode_ticket(&credentials.ticket, &ticket);
+        if (error)
+            status = krb5_error_to_status(error);
+        else
+        {
+            ticket_info->EncryptionType = ticket->enc_part.enctype;
+            status = STATUS_SUCCESS;
+        }
+
+        p_krb5_free_unparsed_name(context, name_with_realm);
+        p_krb5_free_unparsed_name(context, name_without_realm);
+        p_krb5_free_cred_contents(context, &credentials);
+
+        break;
+    }
+
+    p_krb5_cc_end_seq_get(context, cache, &cursor);
+
+    return status;
+}
+
+static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG in_len, void **out, ULONG *out_len)
+{
+    NTSTATUS status;
+    KERB_QUERY_TKT_CACHE_REQUEST *query;
+    KERB_QUERY_TKT_CACHE_RESPONSE *resp;
+    ULONG tickets_allocated;
+    krb5_error_code error;
+    krb5_context context = NULL;
+    krb5_cccol_cursor cursor;
+    krb5_ccache cache;
+
+    if (!in || in_len != sizeof(KERB_QUERY_TKT_CACHE_REQUEST) || !out || !out_len)
+        return STATUS_INVALID_PARAMETER;
+
+    query = (KERB_QUERY_TKT_CACHE_REQUEST *)in;
+
+    if (query->LogonId.HighPart != 0 || query->LogonId.LowPart != 0)
+        return STATUS_ACCESS_DENIED;
+
+    tickets_allocated = 16;
+    resp = heap_alloc(sizeof(*resp) + sizeof(resp->Tickets[0]) * (tickets_allocated - 1));
+    if (!resp) return STATUS_NO_MEMORY;
+
+    resp->MessageType = KerbQueryTicketCacheMessage;
+    resp->CountOfTickets = 0;
+
+    error = p_krb5_init_context(&context);
+    if (error)
+    {
+        status = krb5_error_to_status(error);
+        goto done;
+    }
+
+    error = p_krb5_cccol_cursor_new(context, &cursor);
+    if (error)
+    {
+        status = krb5_error_to_status(error);
+        goto done;
+    }
+
+    for (;;)
+    {
+        error = p_krb5_cccol_cursor_next(context, cursor, &cache);
+        if (error || !cache)
+        {
+            status = krb5_error_to_status(error);
+            break;
+        }
+
+        if (resp->CountOfTickets == tickets_allocated)
+        {
+            KERB_QUERY_TKT_CACHE_RESPONSE *new_resp;
+
+            tickets_allocated *= 2;
+            new_resp = heap_realloc(resp, sizeof(*resp) + sizeof(resp->Tickets[0]) * (tickets_allocated - 1));
+            if (!new_resp)
+            {
+                status = STATUS_NO_MEMORY;
+                p_krb5_cc_close(context, cache);
+                break;
+            }
+
+            resp = new_resp;
+        }
+
+        status = copy_ticket_info(context, cache, &resp->Tickets[resp->CountOfTickets]);
+        if (status == STATUS_SUCCESS)
+            resp->CountOfTickets++;
+
+        p_krb5_cc_close(context, cache);
+    }
+
+    if (status) goto done;
+
+    /* FIXME: copy server/realm names to client space as well.
+     * As long as LSA works in current process space it's OK.
+     */
+
+    *out_len = sizeof(*resp);
+    if (resp->CountOfTickets > 1)
+        *out_len += sizeof(resp->Tickets[0]) * (resp->CountOfTickets - 1);
+    status = lsa_dispatch.AllocateClientBuffer(lsa_req, *out_len, out);
+    if (status) goto done;
+
+    status = lsa_dispatch.CopyToClientBuffer(lsa_req, *out_len, *out, resp);
+    if (status)
+        lsa_dispatch.FreeClientBuffer(lsa_req, *out);
+
+done:
+    if (context)
+        p_krb5_free_context(context);
+    heap_free(resp);
+    return status;
+}
+
 static NTSTATUS NTAPI krb5_LsaApCallPackageUntrusted(PLSA_CLIENT_REQUEST request,
     PVOID in_buffer, PVOID client_buffer_base, ULONG in_buffer_length,
     PVOID *out_buffer, PULONG out_buffer_length, PNTSTATUS status)
 {
-    FIXME("%p,%p,%p,%u,%p,%p,%p: stub\n", request, in_buffer, client_buffer_base,
+    KERB_PROTOCOL_MESSAGE_TYPE msg;
+
+    TRACE("%p,%p,%p,%u,%p,%p,%p\n", request, in_buffer, client_buffer_base,
         in_buffer_length, out_buffer, out_buffer_length, status);
 
-    *status = STATUS_NOT_IMPLEMENTED;
-    return STATUS_NOT_IMPLEMENTED;
+    if (!in_buffer || in_buffer_length < sizeof(msg))
+        return STATUS_INVALID_PARAMETER;
+
+    msg = *(KERB_PROTOCOL_MESSAGE_TYPE *)in_buffer;
+
+    switch(msg)
+    {
+    case KerbQueryTicketCacheMessage:
+        *status = query_ticket_cache(request, in_buffer, in_buffer_length, out_buffer, out_buffer_length);
+        break;
+
+    case KerbRetrieveTicketMessage:
+        FIXME("KerbRetrieveTicketMessage stub\n");
+        *status = STATUS_NOT_IMPLEMENTED;
+        break;
+
+    case KerbPurgeTicketCacheMessage:
+        FIXME("KerbPurgeTicketCacheMessage stub\n");
+        *status = STATUS_NOT_IMPLEMENTED;
+        break;
+
+    default: /* All other requests should call LsaApCallPackage */
+        WARN("%u => access denied\n", msg);
+        *status = STATUS_ACCESS_DENIED;
+        break;
+    }
+
+    return *status;
 }
 
 static SECPKG_FUNCTION_TABLE krb5_table =
diff --git a/include/ntsecapi.h b/include/ntsecapi.h
index 36357c61b4..3193b10f42 100644
--- a/include/ntsecapi.h
+++ b/include/ntsecapi.h
@@ -352,6 +352,102 @@ static const WCHAR MICROSOFT_KERBEROS_NAME_W[] = { 'K','e','r','b','e','r','o','
 #define MICROSOFT_KERBEROS_NAME_A "Kerberos"
 #endif
 
+#define KERB_TICKET_FLAGS_reserved          0x80000000
+#define KERB_TICKET_FLAGS_forwardable       0x40000000
+#define KERB_TICKET_FLAGS_forwarded         0x20000000
+#define KERB_TICKET_FLAGS_proxiable         0x10000000
+#define KERB_TICKET_FLAGS_proxy             0x08000000
+#define KERB_TICKET_FLAGS_may_postdate      0x04000000
+#define KERB_TICKET_FLAGS_postdated         0x02000000
+#define KERB_TICKET_FLAGS_invalid           0x01000000
+#define KERB_TICKET_FLAGS_renewable         0x00800000
+#define KERB_TICKET_FLAGS_initial           0x00400000
+#define KERB_TICKET_FLAGS_pre_authent       0x00200000
+#define KERB_TICKET_FLAGS_hw_authent        0x00100000
+#define KERB_TICKET_FLAGS_ok_as_delegate    0x00040000
+#define KERB_TICKET_FLAGS_name_canonicalize 0x00010000
+#define KERB_TICKET_FLAGS_cname_in_pa_data  0x00040000
+#define KERB_TICKET_FLAGS_reserved1         0x00000001
+
+typedef enum _KERB_PROTOCOL_MESSAGE_TYPE
+{
+    KerbDebugRequestMessage = 0,
+    KerbQueryTicketCacheMessage,
+    KerbChangeMachinePasswordMessage,
+    KerbVerifyPacMessage,
+    KerbRetrieveTicketMessage,
+    KerbUpdateAddressesMessage,
+    KerbPurgeTicketCacheMessage,
+    KerbChangePasswordMessage,
+    KerbRetrieveEncodedTicketMessage,
+    KerbDecryptDataMessage,
+    KerbAddBindingCacheEntryMessage,
+    KerbSetPasswordMessage,
+    KerbSetPasswordExMessage,
+    KerbVerifyCredentialsMessage,
+    KerbQueryTicketCacheExMessage,
+    KerbPurgeTicketCacheExMessage,
+    KerbRefreshSmartcardCredentialsMessage,
+    KerbAddExtraCredentialsMessage,
+    KerbQuerySupplementalCredentialsMessage,
+    KerbTransferCredentialsMessage,
+    KerbQueryTicketCacheEx2Message,
+    KerbSubmitTicketMessage,
+    KerbAddExtraCredentialsExMessage,
+    KerbQueryKdcProxyCacheMessage,
+    KerbPurgeKdcProxyCacheMessage,
+    KerbQueryTicketCacheEx3Message,
+    KerbCleanupMachinePkinitCredsMessage,
+    KerbAddBindingCacheEntryExMessage,
+    KerbQueryBindingCacheMessage,
+    KerbPurgeBindingCacheMessage,
+    KerbQueryDomainExtendedPoliciesMessage,
+    KerbQueryS4U2ProxyCacheMessage
+} KERB_PROTOCOL_MESSAGE_TYPE, *PKERB_PROTOCOL_MESSAGE_TYPE;
+
+typedef struct _KERB_TICKET_CACHE_INFO
+{
+    UNICODE_STRING ServerName;
+    UNICODE_STRING RealmName;
+    LARGE_INTEGER StartTime;
+    LARGE_INTEGER EndTime;
+    LARGE_INTEGER RenewTime;
+    LONG EncryptionType;
+    ULONG TicketFlags;
+} KERB_TICKET_CACHE_INFO, *PKERB_TICKET_CACHE_INFO;
+
+typedef struct _KERB_QUERY_TKT_CACHE_REQUEST
+{
+    KERB_PROTOCOL_MESSAGE_TYPE MessageType;
+    LUID LogonId;
+} KERB_QUERY_TKT_CACHE_REQUEST, *PKERB_QUERY_TKT_CACHE_REQUEST;
+
+typedef struct _KERB_QUERY_TKT_CACHE_RESPONSE
+{
+    KERB_PROTOCOL_MESSAGE_TYPE MessageType;
+    ULONG CountOfTickets;
+    KERB_TICKET_CACHE_INFO Tickets[ANYSIZE_ARRAY];
+} KERB_QUERY_TKT_CACHE_RESPONSE, *PKERB_QUERY_TKT_CACHE_RESPONSE;
+
+typedef struct _KERB_RETRIEVE_TKT_REQUEST
+{
+    KERB_PROTOCOL_MESSAGE_TYPE MessageType;
+    LUID LogonId;
+    UNICODE_STRING TargetName;
+    ULONG TicketFlags;
+    ULONG CacheOptions;
+    LONG EncryptionType;
+    SecHandle CredentialsHandle;
+} KERB_RETRIEVE_TKT_REQUEST, *PKERB_RETRIEVE_TKT_REQUEST;
+
+typedef struct _KERB_PURGE_TKT_CACHE_REQUEST
+{
+    KERB_PROTOCOL_MESSAGE_TYPE MessageType;
+    LUID LogonId;
+    UNICODE_STRING ServerName;
+    UNICODE_STRING RealmName;
+} KERB_PURGE_TKT_CACHE_REQUEST, *PKERB_PURGE_TKT_CACHE_REQUEST;
+
 #define RtlGenRandom                    SystemFunction036
 #define RtlEncryptMemory                SystemFunction040
 #define RtlDecryptMemory                SystemFunction041
-- 
2.15.0
From 59a89a2d862dd456335f1b0a30a4f914422caf7d Mon Sep 17 00:00:00 2001
From: George Popoff <ambulance@etersoft.ru>
Date: Wed, 11 Oct 2017 14:25:15 +0300
Subject: [4/9] add wincred.h include for missing SecHandle definition
Content-Type: text/plain; charset=UTF-8
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
---
 dlls/advapi32/advapi32_misc.h  | 1 +
 dlls/advapi32/tests/lsa.c      | 1 +
 dlls/advapi32/tests/security.c | 1 +
 dlls/bcrypt/bcrypt_main.c      | 1 +
 dlls/jscript/math.c            | 1 +
 dlls/kerberos/krb5_ap.c        | 1 +
 dlls/msvcrt/misc.c             | 1 +
 dlls/netapi32/netapi32.c       | 1 +
 dlls/rpcrt4/rpcrt4_main.c      | 1 +
 dlls/rpcrt4/tests/rpc.c        | 1 +
 dlls/schannel/lsamode.c        | 1 +
 dlls/schannel/tests/main.c     | 1 +
 dlls/schannel/usermode.c       | 1 +
 dlls/scrrun/filesystem.c       | 1 +
 dlls/secur32/lsa.c             | 1 +
 dlls/secur32/secur32.c         | 1 +
 dlls/wbemprox/builtin.c        | 1 +
 programs/services/rpc.c        | 1 +
 18 files changed, 18 insertions(+)
diff --git a/dlls/advapi32/advapi32_misc.h b/dlls/advapi32/advapi32_misc.h
index 7fc02a4a2e..b55ecf4072 100644
--- a/dlls/advapi32/advapi32_misc.h
+++ b/dlls/advapi32/advapi32_misc.h
@@ -20,6 +20,7 @@
 #ifndef __WINE_ADVAPI32MISC_H
 #define __WINE_ADVAPI32MISC_H
 
+#include "wincred.h"
 #include "ntsecapi.h"
 #include "winsvc.h"
 #include "winnls.h"
diff --git a/dlls/advapi32/tests/lsa.c b/dlls/advapi32/tests/lsa.c
index d7f2e498ef..939fd488ee 100644
--- a/dlls/advapi32/tests/lsa.c
+++ b/dlls/advapi32/tests/lsa.c
@@ -25,6 +25,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "winreg.h"
 #include "ntsecapi.h"
 #include "sddl.h"
diff --git a/dlls/advapi32/tests/security.c b/dlls/advapi32/tests/security.c
index 446c98fd07..9a31a36f49 100644
--- a/dlls/advapi32/tests/security.c
+++ b/dlls/advapi32/tests/security.c
@@ -26,6 +26,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "winerror.h"
 #include "winternl.h"
 #include "aclapi.h"
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c
index 2aecfb9a22..02b4328ab3 100644
--- a/dlls/bcrypt/bcrypt_main.c
+++ b/dlls/bcrypt/bcrypt_main.c
@@ -34,6 +34,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "ntsecapi.h"
 #include "bcrypt.h"
 
diff --git a/dlls/jscript/math.c b/dlls/jscript/math.c
index ef906f5439..dedadeb23d 100644
--- a/dlls/jscript/math.c
+++ b/dlls/jscript/math.c
@@ -24,6 +24,7 @@
 #include <limits.h>
 
 #include "jscript.h"
+#include "wincred.h"
 #include "ntsecapi.h"
 
 #include "wine/debug.h"
diff --git a/dlls/kerberos/krb5_ap.c b/dlls/kerberos/krb5_ap.c
index a0b6053af4..38708ab60a 100644
--- a/dlls/kerberos/krb5_ap.c
+++ b/dlls/kerberos/krb5_ap.c
@@ -31,6 +31,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "winnls.h"
 #include "sspi.h"
 #include "ntsecapi.h"
diff --git a/dlls/msvcrt/misc.c b/dlls/msvcrt/misc.c
index ba9dd10838..531367edcb 100644
--- a/dlls/msvcrt/misc.c
+++ b/dlls/msvcrt/misc.c
@@ -25,6 +25,7 @@
 
 #include "msvcrt.h"
 #include "wine/debug.h"
+#include "wincred.h"
 #include "ntsecapi.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
diff --git a/dlls/netapi32/netapi32.c b/dlls/netapi32/netapi32.c
index 1c5f110b82..945995661e 100644
--- a/dlls/netapi32/netapi32.c
+++ b/dlls/netapi32/netapi32.c
@@ -37,6 +37,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "lm.h"
 #include "lmaccess.h"
 #include "lmat.h"
diff --git a/dlls/rpcrt4/rpcrt4_main.c b/dlls/rpcrt4/rpcrt4_main.c
index d5d349dc58..4775d68526 100644
--- a/dlls/rpcrt4/rpcrt4_main.c
+++ b/dlls/rpcrt4/rpcrt4_main.c
@@ -40,6 +40,7 @@
 #include "windef.h"
 #include "winerror.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "winuser.h"
 #include "winnt.h"
 #include "winternl.h"
diff --git a/dlls/rpcrt4/tests/rpc.c b/dlls/rpcrt4/tests/rpc.c
index f026e99de2..9ed2584f45 100644
--- a/dlls/rpcrt4/tests/rpc.c
+++ b/dlls/rpcrt4/tests/rpc.c
@@ -28,6 +28,7 @@
 #include "wine/test.h"
 #include <windef.h>
 #include <winbase.h>
+#include <wincred.h>
 #include <winnt.h>
 #include <winerror.h>
 #include <ole2.h>
diff --git a/dlls/schannel/lsamode.c b/dlls/schannel/lsamode.c
index 96ca240c08..258266c6f0 100644
--- a/dlls/schannel/lsamode.c
+++ b/dlls/schannel/lsamode.c
@@ -24,6 +24,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "sspi.h"
 #include "ntsecapi.h"
 #include "ntsecpkg.h"
diff --git a/dlls/schannel/tests/main.c b/dlls/schannel/tests/main.c
index 4240dd76cb..9c1f827f58 100644
--- a/dlls/schannel/tests/main.c
+++ b/dlls/schannel/tests/main.c
@@ -24,6 +24,7 @@
 #define WIN32_NO_STATUS
 #include <windef.h>
 #include <winbase.h>
+#include <wincred.h>
 #define SECURITY_WIN32
 #include <security.h>
 #include <schannel.h>
diff --git a/dlls/schannel/usermode.c b/dlls/schannel/usermode.c
index 0713f822f9..1afa5d5ae6 100644
--- a/dlls/schannel/usermode.c
+++ b/dlls/schannel/usermode.c
@@ -24,6 +24,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "sspi.h"
 #include "ntsecapi.h"
 #include "ntsecpkg.h"
diff --git a/dlls/scrrun/filesystem.c b/dlls/scrrun/filesystem.c
index 2acef849fa..b6381a62d8 100644
--- a/dlls/scrrun/filesystem.c
+++ b/dlls/scrrun/filesystem.c
@@ -24,6 +24,7 @@
 
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "ole2.h"
 #include "olectl.h"
 #include "dispex.h"
diff --git a/dlls/secur32/lsa.c b/dlls/secur32/lsa.c
index 1028c18e71..56d574ea4d 100644
--- a/dlls/secur32/lsa.c
+++ b/dlls/secur32/lsa.c
@@ -26,6 +26,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "winreg.h"
 #include "sspi.h"
 #include "ntsecapi.h"
diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c
index 1e12f3c543..f5d5746171 100644
--- a/dlls/secur32/secur32.c
+++ b/dlls/secur32/secur32.c
@@ -23,6 +23,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #include "winnls.h"
 #include "winreg.h"
 #include "winternl.h"
diff --git a/dlls/wbemprox/builtin.c b/dlls/wbemprox/builtin.c
index 826ec4b086..04739649f9 100644
--- a/dlls/wbemprox/builtin.c
+++ b/dlls/wbemprox/builtin.c
@@ -34,6 +34,7 @@
 #define WIN32_NO_STATUS
 #include "windef.h"
 #include "winbase.h"
+#include "wincred.h"
 #ifdef __MINGW32__
 # include "winsock2.h"
 # include "ws2tcpip.h"
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 20c5a2761f..fb70eef561 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -25,6 +25,7 @@
 #include <windows.h>
 #include <winternl.h>
 #include <winsvc.h>
+#include <wincred.h>
 #include <ntsecapi.h>
 #include <rpc.h>
 
-- 
2.15.0
From 484c150d69beec217686fd8d2c17374ec075e75d Mon Sep 17 00:00:00 2001
From: George Popoff <ambulance@etersoft.ru>
Date: Wed, 11 Oct 2017 14:36:36 +0300
Subject: [5/9] kerberos: fix KerbQueryTicketCacheMessage to return all tickets
 in all ccaches
Content-Type: text/plain; charset=UTF-8
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
---
 dlls/kerberos/krb5_ap.c | 53 +++++++++++++++++++++++++++++++++++--------------
 1 file changed, 38 insertions(+), 15 deletions(-)
diff --git a/dlls/kerberos/krb5_ap.c b/dlls/kerberos/krb5_ap.c
index 38708ab60a..5238d5a77b 100644
--- a/dlls/kerberos/krb5_ap.c
+++ b/dlls/kerberos/krb5_ap.c
@@ -54,6 +54,7 @@ MAKE_FUNCPTR(krb5_init_context);
 MAKE_FUNCPTR(krb5_free_context);
 MAKE_FUNCPTR(krb5_cccol_cursor_new);
 MAKE_FUNCPTR(krb5_cccol_cursor_next);
+MAKE_FUNCPTR(krb5_cccol_cursor_free);
 MAKE_FUNCPTR(krb5_cc_close);
 MAKE_FUNCPTR(krb5_cc_start_seq_get);
 MAKE_FUNCPTR(krb5_cc_end_seq_get);
@@ -100,6 +101,7 @@ static void load_krb5(void)
     LOAD_FUNCPTR(krb5_free_context)
     LOAD_FUNCPTR(krb5_cccol_cursor_new)
     LOAD_FUNCPTR(krb5_cccol_cursor_next)
+    LOAD_FUNCPTR(krb5_cccol_cursor_free)
     LOAD_FUNCPTR(krb5_cc_close)
     LOAD_FUNCPTR(krb5_cc_start_seq_get)
     LOAD_FUNCPTR(krb5_cc_end_seq_get)
@@ -160,7 +162,6 @@ static NTSTATUS krb5_error_to_status(krb5_error_code error)
     switch (error)
     {
     case 0: return STATUS_SUCCESS;
-
     default:
         /* FIXME */
         return STATUS_UNSUCCESSFUL;
@@ -180,7 +181,8 @@ static WCHAR *utf8_to_wstr(const char *utf8)
     return wstr;
 }
 
-static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_TICKET_CACHE_INFO *ticket_info)
+static NTSTATUS copy_tickets_info(krb5_context context, krb5_ccache cache,
+    KERB_TICKET_CACHE_INFO *tickets_info, ULONG tickets_allocated, ULONG *tickets_in_cache)
 {
     NTSTATUS status;
     krb5_cc_cursor cursor;
@@ -198,14 +200,13 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
 
     status = STATUS_SUCCESS;
 
+    *tickets_in_cache = 0;
+
     for (;;)
     {
         error = p_krb5_cc_next_cred(context, cache, &cursor, &credentials);
         if (error)
-        {
-            status = krb5_error_to_status(error);
             break;
-        }
 
         if (p_krb5_is_config_principal(context, credentials.server))
         {
@@ -213,6 +214,16 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
             continue;
         }
 
+        TRACE("credential has more tickets!\n");
+
+        (*tickets_in_cache)++;
+        if (*tickets_in_cache > tickets_allocated)
+        {
+            p_krb5_free_cred_contents(context, &credentials);
+            /* Indicate that we fill out a ticket_info buffer */
+            status = SEC_E_BUFFER_TOO_SMALL;
+        }
+
         error = p_krb5_unparse_name(context, credentials.server, &name_with_realm);
         if (error)
         {
@@ -236,7 +247,7 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
         TRACE("name_without_realm: %s\n", debugstr_a(name_without_realm));
 
         name_without_realmW = utf8_to_wstr(name_without_realm);
-        RtlInitUnicodeString(&ticket_info->ServerName, name_without_realmW);
+        RtlInitUnicodeString(&tickets_info->ServerName, name_without_realmW);
 
         realm_name = name_with_realm;
 
@@ -249,24 +260,24 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
         /* realm_name - now contains only realm! */
 
         realm_nameW = utf8_to_wstr(realm_name);
-        RtlInitUnicodeString(&ticket_info->RealmName, realm_nameW);
+        RtlInitUnicodeString(&tickets_info->RealmName, realm_nameW);
 
         if (!credentials.times.starttime)
             credentials.times.starttime = credentials.times.authtime;
 
         /* TODO: if krb5_is_config_principal = true */
-        RtlSecondsSince1970ToTime(credentials.times.starttime, &ticket_info->StartTime);
-        RtlSecondsSince1970ToTime(credentials.times.endtime, &ticket_info->EndTime);
-        RtlSecondsSince1970ToTime(credentials.times.renew_till, &ticket_info->RenewTime);
+        RtlSecondsSince1970ToTime(credentials.times.starttime, &tickets_info->StartTime);
+        RtlSecondsSince1970ToTime(credentials.times.endtime, &tickets_info->EndTime);
+        RtlSecondsSince1970ToTime(credentials.times.renew_till, &tickets_info->RenewTime);
 
-        ticket_info->TicketFlags = credentials.ticket_flags;
+        tickets_info->TicketFlags = credentials.ticket_flags;
 
         error = p_krb5_decode_ticket(&credentials.ticket, &ticket);
         if (error)
             status = krb5_error_to_status(error);
         else
         {
-            ticket_info->EncryptionType = ticket->enc_part.enctype;
+            tickets_info->EncryptionType = ticket->enc_part.enctype;
             status = STATUS_SUCCESS;
         }
 
@@ -274,7 +285,8 @@ static NTSTATUS copy_ticket_info(krb5_context context, krb5_ccache cache, KERB_T
         p_krb5_free_unparsed_name(context, name_without_realm);
         p_krb5_free_cred_contents(context, &credentials);
 
-        break;
+        /* fill next ticket in ccache */
+        tickets_info++;
     }
 
     p_krb5_cc_end_seq_get(context, cache, &cursor);
@@ -288,6 +300,7 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
     KERB_QUERY_TKT_CACHE_REQUEST *query;
     KERB_QUERY_TKT_CACHE_RESPONSE *resp;
     ULONG tickets_allocated;
+    ULONG tickets_in_cache;
     krb5_error_code error;
     krb5_context context = NULL;
     krb5_cccol_cursor cursor;
@@ -331,6 +344,8 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
             break;
         }
 
+        TRACE("Found another ccache!\n");
+
         if (resp->CountOfTickets == tickets_allocated)
         {
             KERB_QUERY_TKT_CACHE_RESPONSE *new_resp;
@@ -347,15 +362,22 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
             resp = new_resp;
         }
 
-        status = copy_ticket_info(context, cache, &resp->Tickets[resp->CountOfTickets]);
+        tickets_in_cache = 0;
+        status = copy_tickets_info(context, cache, &resp->Tickets[resp->CountOfTickets],
+                tickets_allocated - resp->CountOfTickets, &tickets_in_cache);
+
         if (status == STATUS_SUCCESS)
-            resp->CountOfTickets++;
+            resp->CountOfTickets += tickets_in_cache;
+        else if (status == STATUS_BUFFER_TOO_SMALL)
+            FIXME("Too many tickets in ccache, some tickets may be ommited\n");
 
         p_krb5_cc_close(context, cache);
     }
 
     if (status) goto done;
 
+    p_krb5_cccol_cursor_free(context, &cursor);
+
     /* FIXME: copy server/realm names to client space as well.
      * As long as LSA works in current process space it's OK.
      */
@@ -373,6 +395,7 @@ static NTSTATUS query_ticket_cache(PLSA_CLIENT_REQUEST lsa_req, void *in, ULONG
 done:
     if (context)
         p_krb5_free_context(context);
+
     heap_free(resp);
     return status;
 }
-- 
2.15.0
From f0c2b891d3271472fa9cf424c7c643ee13eeb0b7 Mon Sep 17 00:00:00 2001
From: George Popoff <ambulance@etersoft.ru>
Date: Wed, 11 Oct 2017 14:38:48 +0300
Subject: [6/9] add terminating null to kerberos name variable
Content-Type: text/plain; charset=UTF-8
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
---
 include/ntsecapi.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/include/ntsecapi.h b/include/ntsecapi.h
index 3193b10f42..f7c8a5451c 100644
--- a/include/ntsecapi.h
+++ b/include/ntsecapi.h
@@ -349,7 +349,7 @@ typedef enum _POLICY_NOTIFICATION_INFORMATION_CLASS
 static const WCHAR MICROSOFT_KERBEROS_NAME_W[] = { 'K','e','r','b','e','r','o','s',0 };
 #endif
 #else /* UNICODE */
-#define MICROSOFT_KERBEROS_NAME_A "Kerberos"
+static const char MICROSOFT_KERBEROS_NAME_A[] = { 'K','e','r','b','e','r','o','s',0 };
 #endif
 
 #define KERB_TICKET_FLAGS_reserved          0x80000000
-- 
2.15.0
From 0cb7ea40c604071f003b2f548bf732fb33bf3f75 Mon Sep 17 00:00:00 2001
From: George Popoff <ambulance@etersoft.ru>
Date: Wed, 11 Oct 2017 14:53:11 +0300
Subject: [7/9] create new kerberos ssp based on GSSAPI ssp. Delete old.
Content-Type: text/plain; charset=UTF-8
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
---
 configure                   |   68 +++
 configure.ac                |   10 +
 dlls/kerberos/Makefile.in   |    3 +-
 dlls/kerberos/kerberos.spec |    2 +
 dlls/kerberos/krb5_ssp.c    | 1304 +++++++++++++++++++++++++++++++++++++++++++
 dlls/secur32/Makefile.in    |    1 -
 dlls/secur32/kerberos.c     |  323 -----------
 dlls/secur32/secur32.c      |   33 +-
 include/config.h.in         |    6 +
 loader/wine.inf.in          |    2 +
 10 files changed, 1411 insertions(+), 341 deletions(-)
 create mode 100644 dlls/kerberos/krb5_ssp.c
 delete mode 100644 dlls/secur32/kerberos.c
diff --git a/configure b/configure
index 0f6a849c58..8a6c2315e5 100755
--- a/configure
+++ b/configure
@@ -7024,6 +7024,8 @@ for ac_header in \
 	grp.h \
 	gsm.h \
 	gsm/gsm.h \
+	gssapi/gssapi.h \
+	gssapi/gssapi_krb5.h \
 	ieeefp.h \
 	inet/mib2.h \
 	io.h \
@@ -14576,6 +14578,72 @@ esac
 
 fi
 
+if test "$ac_cv_header_gssapi_gssapi_krb5_h" = "yes"
+then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -lgssapi_krb5" >&5
+$as_echo_n "checking for -lgssapi_krb5... " >&6; }
+if ${ac_cv_lib_soname_gssapi_krb5+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_soname_save_LIBS=$LIBS
+LIBS="-lgssapi_krb5  $LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char gss_init_sec_context ();
+int
+main ()
+{
+return gss_init_sec_context ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  case "$LIBEXT" in
+    dll) ac_cv_lib_soname_gssapi_krb5=`$ac_cv_path_LDD conftest.exe | grep "gssapi_krb5" | sed -e "s/dll.*/dll/"';2,$d'` ;;
+    dylib) ac_cv_lib_soname_gssapi_krb5=`$OTOOL -L conftest$ac_exeext | grep "libgssapi_krb5\\.[0-9A-Za-z.]*dylib" | sed -e "s/^.*\/\(libgssapi_krb5\.[0-9A-Za-z.]*dylib\).*$/\1/"';2,$d'` ;;
+    *) ac_cv_lib_soname_gssapi_krb5=`$READELF -d conftest$ac_exeext | grep "NEEDED.*libgssapi_krb5\\.$LIBEXT" | sed -e "s/^.*\\[\\(libgssapi_krb5\\.$LIBEXT[^	 ]*\\)\\].*$/\1/"';2,$d'`
+       if ${ac_cv_lib_soname_gssapi_krb5:+false} :; then :
+  ac_cv_lib_soname_gssapi_krb5=`$LDD conftest$ac_exeext | grep "libgssapi_krb5\\.$LIBEXT" | sed -e "s/^.*\(libgssapi_krb5\.$LIBEXT[^	 ]*\).*$/\1/"';2,$d'`
+fi ;;
+  esac
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS=$ac_check_soname_save_LIBS
+fi
+if ${ac_cv_lib_soname_gssapi_krb5:+false} :; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5
+$as_echo "not found" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_soname_gssapi_krb5" >&5
+$as_echo "$ac_cv_lib_soname_gssapi_krb5" >&6; }
+
+cat >>confdefs.h <<_ACEOF
+#define SONAME_LIBGSSAPI_KRB5 "$ac_cv_lib_soname_gssapi_krb5"
+_ACEOF
+
+
+fi
+fi
+if test "x$ac_cv_lib_soname_gssapi_krb5" = "x"; then :
+  case "x$with_gssapi_krb5" in
+  x)   as_fn_append wine_notices "|libgssapi_krb5 ${notice_platform}development files not found gssapi won't be supported." ;;
+  xno) ;;
+  *)   as_fn_error $? "libgssapi_krb5 ${notice_platform}development files not found gssapi won't be supported.
+This is an error since --with-gssapi_krb5 was requested." "$LINENO" 5 ;;
+esac
+
+fi
+
 if test "x$with_jpeg" != "xno"
 then
     if ${JPEG_CFLAGS:+false} :; then :
diff --git a/configure.ac b/configure.ac
index 0e1a0a1ee7..4c2a1b46bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -437,6 +437,8 @@ AC_CHECK_HEADERS(\
 	grp.h \
 	gsm.h \
 	gsm/gsm.h \
+	gssapi/gssapi.h \
+	gssapi/gssapi_krb5.h \
 	ieeefp.h \
 	inet/mib2.h \
 	io.h \
@@ -1732,6 +1734,14 @@ fi
 WINE_NOTICE_WITH(krb5,[test "x$ac_cv_lib_soname_krb5" = "x"],
                  [libkrb5 ${notice_platform}development files not found, Kerberos won't be supported.])
 
+dnl **** Check for libgssapi_krb5 ****
+if test "$ac_cv_header_gssapi_gssapi_krb5_h" = "yes"
+then
+    WINE_CHECK_SONAME(gssapi_krb5,gss_init_sec_context)
+fi
+WINE_NOTICE_WITH(gssapi_krb5,[test "x$ac_cv_lib_soname_gssapi_krb5" = "x"],
+                 [libgssapi_krb5 ${notice_platform}development files not found gssapi won't be supported.])
+
 dnl **** Check for libjpeg ****
 if test "x$with_jpeg" != "xno"
 then
diff --git a/dlls/kerberos/Makefile.in b/dlls/kerberos/Makefile.in
index 253e944716..ec095efd87 100644
--- a/dlls/kerberos/Makefile.in
+++ b/dlls/kerberos/Makefile.in
@@ -2,4 +2,5 @@ MODULE    = kerberos.dll
 EXTRAINCL = $(KRB5_CFLAGS)
 
 C_SRCS = \
-	krb5_ap.c
+	krb5_ap.c \
+	krb5_ssp.c
diff --git a/dlls/kerberos/kerberos.spec b/dlls/kerberos/kerberos.spec
index c41b830e46..8bb2fa1c8d 100644
--- a/dlls/kerberos/kerberos.spec
+++ b/dlls/kerberos/kerberos.spec
@@ -1,4 +1,6 @@
 1 stub SpInitialize
+@ stdcall InitSecurityInterfaceA()
+@ stdcall InitSecurityInterfaceW()
 2 stub KerbDomainChangeCallback
 3 stdcall SpLsaModeInitialize(long ptr ptr ptr)
 4 stub SpUserModeInitialize
diff --git a/dlls/kerberos/krb5_ssp.c b/dlls/kerberos/krb5_ssp.c
new file mode 100644
index 0000000000..cfb408edbb
--- /dev/null
+++ b/dlls/kerberos/krb5_ssp.c
@@ -0,0 +1,1304 @@
+/*
+ * Kerberos API based on GSSAPI SSP
+ *
+ * Copyright 2005, 2006 Kai Blin
+ * Copyright 2008 Robert Shearman for CodeWeavers
+ * Copyright 2016 Jacek Caban for CodeWeavers
+ * Copyright 2017 George Popoff
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#endif
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "wincred.h"
+#include "rpc.h"
+#include "sspi.h"
+#include "lm.h"
+#include "wine/unicode.h"
+#include "wine/library.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(kerberos);
+WINE_DECLARE_DEBUG_CHANNEL(winediag);
+
+#define MAKE_FUNCPTR(f) static typeof(f) * p_##f
+MAKE_FUNCPTR(gss_import_name);
+MAKE_FUNCPTR(gss_acquire_cred);
+MAKE_FUNCPTR(gss_release_name);
+MAKE_FUNCPTR(gss_init_sec_context);
+MAKE_FUNCPTR(gss_accept_sec_context);
+MAKE_FUNCPTR(gss_delete_sec_context);
+MAKE_FUNCPTR(gss_get_mic);
+MAKE_FUNCPTR(gss_verify_mic);
+MAKE_FUNCPTR(gss_release_cred);
+MAKE_FUNCPTR(gss_wrap);
+MAKE_FUNCPTR(gss_unwrap);
+#undef MAKE_FUNCPTR
+
+#define GSS_KERBEROS_MAX_BUF 1904
+
+#define KERBEROS_MAGIC ('K' << 24 | 'E' << 16 | 'R' << 8 | 'B')
+
+#define GSS_KERBEROS_COMMENT \
+   { 'K', 'e', 'r', 'b', 'e', 'r', 'o', 's', ' ', \
+     'S', 'e', 'c', 'u', 'r', 'i', 't', 'y', ' ', \
+     'P', 'a', 'c', 'k', 'a', 'g', 'e', 0}
+
+static CHAR kerberos_comment_A[] = GSS_KERBEROS_COMMENT;
+static WCHAR kerberos_comment_W[] = GSS_KERBEROS_COMMENT;
+
+#define GSS_KERBEROS_NAME {'K', 'e', 'r', 'b', 'e', 'r', 'o', 's', 0}
+
+static char kerberos_name_A[] = GSS_KERBEROS_NAME;
+static WCHAR kerberos_name_W[] = GSS_KERBEROS_NAME;
+
+/* FIXME */
+#define CAPS ( \
+        SECPKG_FLAG_INTEGRITY | \
+        SECPKG_FLAG_PRIVACY | \
+        SECPKG_FLAG_TOKEN_ONLY | \
+        SECPKG_FLAG_CONNECTION | \
+        SECPKG_FLAG_MULTI_REQUIRED | \
+        SECPKG_FLAG_IMPERSONATION | \
+        SECPKG_FLAG_ACCEPT_WIN32_NAME | \
+        SECPKG_FLAG_READONLY_WITH_CHECKSUM)
+
+
+static SecPkgInfoW infoW = {
+    CAPS,
+    1,
+    RPC_C_AUTHN_GSS_KERBEROS,
+    GSS_KERBEROS_MAX_BUF,
+    kerberos_name_W,
+    kerberos_comment_W
+};
+
+static SecPkgInfoA infoA = {
+    CAPS,
+    1,
+    RPC_C_AUTHN_GSS_KERBEROS,
+    GSS_KERBEROS_MAX_BUF,
+    kerberos_name_A,
+    kerberos_comment_A
+};
+
+
+SecPkgInfoA *kerberos_package_infoA = (SecPkgInfoA *)&infoA;
+SecPkgInfoW *kerberos_package_infoW = (SecPkgInfoW *)&infoW;
+
+static BOOL load_gssapi(void)
+{
+    void *libgssapi_handle;
+
+    if (!(libgssapi_handle = wine_dlopen(SONAME_LIBGSSAPI_KRB5, RTLD_NOW, NULL, 0)))
+    {
+        ERR_(winediag)("failed to load libgssapi\n" );
+        return FALSE;
+    }
+
+#define LOAD_FUNCPTR(f) \
+    if (!(p_##f = wine_dlsym(libgssapi_handle, #f, NULL, 0))) \
+    { \
+        ERR("failed to load %s\n", #f); \
+        goto fail; \
+    }
+
+    LOAD_FUNCPTR(gss_import_name);
+    LOAD_FUNCPTR(gss_acquire_cred);
+    LOAD_FUNCPTR(gss_release_name);
+    LOAD_FUNCPTR(gss_init_sec_context);
+    LOAD_FUNCPTR(gss_accept_sec_context);
+    LOAD_FUNCPTR(gss_delete_sec_context);
+    LOAD_FUNCPTR(gss_get_mic);
+    LOAD_FUNCPTR(gss_verify_mic);
+    LOAD_FUNCPTR(gss_release_cred);
+    LOAD_FUNCPTR(gss_wrap);
+    LOAD_FUNCPTR(gss_unwrap);
+#undef LOAD_FUNCPTR
+
+    return TRUE;
+
+fail:
+    wine_dlclose(libgssapi_handle, NULL, 0);
+    return FALSE;
+}
+
+static inline gss_cred_id_t kerberos_CredHandleSSPItoGSSAPI(const CredHandle *phCredential)
+{
+    return (gss_cred_id_t)phCredential->dwLower;
+}
+
+static inline void kerberos_CredHandleGSSAPItoSSPI(gss_cred_id_t cred_handle, PCredHandle phCredential)
+{
+    phCredential->dwLower = (DWORD)cred_handle;
+    phCredential->dwUpper = KERBEROS_MAGIC;
+}
+
+static inline gss_ctx_id_t kerberos_CtxtHandleSSPItoGSSAPI(const CtxtHandle *phCredential)
+{
+    return (gss_ctx_id_t)phCredential->dwLower;
+}
+
+static inline void kerberos_CtxtHandleGSSAPItoSSPI(gss_ctx_id_t ctx_handle, PCtxtHandle phCredential)
+{
+    phCredential->dwLower = (DWORD)ctx_handle;
+    phCredential->dwUpper = KERBEROS_MAGIC;
+}
+
+static inline void kerberos_CtxtAttrsGSSPItoSSPI(OM_uint32 ret_flags, PULONG pfContextAttr)
+{
+    *pfContextAttr = 0;
+
+    if (ret_flags & GSS_C_DELEG_FLAG)
+        *pfContextAttr |= ISC_RET_DELEGATE;
+    if (ret_flags & GSS_C_MUTUAL_FLAG)
+        *pfContextAttr |= ISC_RET_MUTUAL_AUTH;
+    if (ret_flags & GSS_C_REPLAY_FLAG)
+        *pfContextAttr |= ISC_RET_REPLAY_DETECT;
+    if (ret_flags & GSS_C_SEQUENCE_FLAG)
+        *pfContextAttr |= ISC_RET_SEQUENCE_DETECT;
+    if (ret_flags & GSS_C_CONF_FLAG)
+        *pfContextAttr |= ISC_RET_CONFIDENTIALITY;
+    if (ret_flags & GSS_C_INTEG_FLAG)
+        *pfContextAttr |= ISC_RET_INTEGRITY;
+    if (ret_flags & GSS_C_ANON_FLAG)
+        *pfContextAttr |= ISC_RET_NULL_SESSION;
+}
+
+static inline OM_uint32 kerberos_CtxtAttrsSSPItoGSSAPI(ULONG fContextAttr)
+{
+    OM_uint32 ret_flags = 0;
+
+    if (fContextAttr & ISC_RET_DELEGATE)
+        ret_flags |= GSS_C_DELEG_FLAG;
+    if (fContextAttr & ISC_RET_MUTUAL_AUTH)
+        ret_flags |= GSS_C_MUTUAL_FLAG;
+    if (fContextAttr & ISC_RET_REPLAY_DETECT)
+        ret_flags |= GSS_C_REPLAY_FLAG;
+    if (fContextAttr & ISC_RET_SEQUENCE_DETECT)
+        ret_flags |= GSS_C_SEQUENCE_FLAG;
+    if (fContextAttr & ISC_RET_CONFIDENTIALITY)
+        ret_flags |= GSS_C_CONF_FLAG;
+    if (fContextAttr & ISC_RET_INTEGRITY)
+        ret_flags |= GSS_C_INTEG_FLAG;
+    if (fContextAttr & ISC_RET_NULL_SESSION)
+        ret_flags |= GSS_C_ANON_FLAG;
+
+    return ret_flags;
+}
+
+static inline OM_uint32 kerberos_CtxtReqsSSPItoGSSAPI(ULONG fRequirements)
+{
+    OM_uint32 req_flags = 0;
+
+    if (fRequirements & ISC_REQ_DELEGATE)
+        req_flags |= GSS_C_DELEG_FLAG;
+    if (fRequirements & ISC_REQ_MUTUAL_AUTH)
+        req_flags |= GSS_C_MUTUAL_FLAG;
+    if (fRequirements & ISC_REQ_REPLAY_DETECT)
+        req_flags |= GSS_C_REPLAY_FLAG;
+    if (fRequirements & ISC_REQ_SEQUENCE_DETECT)
+        req_flags |= GSS_C_SEQUENCE_FLAG;
+    if (fRequirements & ISC_REQ_CONFIDENTIALITY)
+        req_flags |= GSS_C_CONF_FLAG;
+    if (fRequirements & ISC_REQ_INTEGRITY)
+        req_flags |= GSS_C_INTEG_FLAG;
+    if (fRequirements & ISC_REQ_NULL_SESSION)
+        req_flags |= GSS_C_ANON_FLAG;
+
+    return req_flags;
+}
+
+static inline void kerberos_CtxtReqsGSSAPItoSSPI(OM_uint32 req_flags, PULONG pfRequirements)
+{
+    *pfRequirements = 0;
+
+    if (req_flags & GSS_C_DELEG_FLAG)
+        *pfRequirements |= ISC_REQ_DELEGATE;
+    if (req_flags & GSS_C_MUTUAL_FLAG)
+        *pfRequirements |= ISC_REQ_MUTUAL_AUTH;
+    if (req_flags & GSS_C_REPLAY_FLAG)
+        *pfRequirements |= ISC_REQ_REPLAY_DETECT;
+    if (req_flags & GSS_C_SEQUENCE_FLAG)
+        *pfRequirements |= ISC_REQ_SEQUENCE_DETECT;
+    if (req_flags & GSS_C_CONF_FLAG)
+        *pfRequirements |= ISC_REQ_CONFIDENTIALITY;
+    if (req_flags & GSS_C_INTEG_FLAG)
+        *pfRequirements |= ISC_REQ_INTEGRITY;
+    if (req_flags & GSS_C_ANON_FLAG)
+        *pfRequirements |= ISC_REQ_NULL_SESSION;
+}
+
+static SECURITY_STATUS kerberos_GSSAPIStatusToSecurityStatus(OM_uint32 status)
+{
+    switch (status)
+    {
+    case GSS_S_COMPLETE: return SEC_E_OK;
+    case GSS_S_BAD_MECH: return SEC_E_SECPKG_NOT_FOUND;
+    case GSS_S_BAD_SIG: return SEC_E_MESSAGE_ALTERED;
+    case GSS_S_NO_CRED: return SEC_E_NO_CREDENTIALS;
+    case GSS_S_NO_CONTEXT: return SEC_E_INVALID_HANDLE;
+    case GSS_S_DEFECTIVE_TOKEN: return SEC_E_MESSAGE_ALTERED;
+    case GSS_S_DEFECTIVE_CREDENTIAL: return SEC_E_INVALID_TOKEN;
+    case GSS_S_CREDENTIALS_EXPIRED: return SEC_E_CONTEXT_EXPIRED;
+    case GSS_S_CONTEXT_EXPIRED: return SEC_E_CONTEXT_EXPIRED;
+    case GSS_S_BAD_QOP: return SEC_E_QOP_NOT_SUPPORTED;
+
+    case GSS_S_CONTINUE_NEEDED: return SEC_I_CONTINUE_NEEDED;
+    case GSS_S_DUPLICATE_TOKEN: return SEC_E_INVALID_TOKEN;
+    case GSS_S_OLD_TOKEN: return SEC_E_INVALID_TOKEN;
+    case GSS_S_UNSEQ_TOKEN: return SEC_E_OUT_OF_SEQUENCE;
+    case GSS_S_GAP_TOKEN: return SEC_E_OUT_OF_SEQUENCE;
+
+    default:
+        FIXME("couldn't convert status 0x%x to SECURITY_STATUS\n", status);
+
+        return SEC_E_INTERNAL_ERROR;
+    }
+}
+
+/***********************************************************************
+ *              EnumerateSecurityPackagesW
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_EnumerateSecurityPackagesW(
+        PULONG pcPackages, PSecPkgInfoW *ppPackageInfo)
+{
+    *pcPackages = 1;
+    *ppPackageInfo = &infoW;
+
+    return SEC_E_OK;
+}
+
+/***********************************************************************
+ *              EnumerateSecurityPackagesA
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_EnumerateSecurityPackagesA(
+        PULONG pcPackages, PSecPkgInfoA *ppPackageInfo)
+{
+    *pcPackages = 1;
+    *ppPackageInfo = &infoA;
+
+    return SEC_E_OK;
+}
+
+/***********************************************************************
+ *              QueryCredentialsAttributesA
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesA(
+        PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
+
+    if(ulAttribute == SECPKG_ATTR_NAMES)
+    {
+        FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
+        ret = SEC_E_UNSUPPORTED_FUNCTION;
+    }
+    else
+        ret = SEC_E_UNSUPPORTED_FUNCTION;
+
+    return ret;
+}
+
+/***********************************************************************
+ *              QueryCredentialsAttributesW
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesW(
+        PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
+
+    if(ulAttribute == SECPKG_ATTR_NAMES)
+    {
+        FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
+        ret = SEC_E_UNSUPPORTED_FUNCTION;
+    }
+    else
+        ret = SEC_E_UNSUPPORTED_FUNCTION;
+
+    return ret;
+}
+
+static void kerberos_ExpiryTimeToTimeStamp(OM_uint32 expiry_time, PTimeStamp ptsExpiry)
+{
+    SYSTEMTIME current_local_time;
+    FILETIME current_local_filetime;
+    ULARGE_INTEGER temp;
+    GetLocalTime(&current_local_time);
+    SystemTimeToFileTime(&current_local_time, &current_local_filetime);
+    temp.QuadPart = ((ULONGLONG)current_local_filetime.dwLowDateTime |
+        (ULONGLONG)current_local_filetime.dwHighDateTime << 32) +
+        expiry_time;
+    ptsExpiry->LowPart = temp.QuadPart;
+    ptsExpiry->HighPart = temp.QuadPart >> 32;
+}
+
+/***********************************************************************
+ *              AcquireCredentialsHandleW
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleW(
+ SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
+ PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
+{
+    OM_uint32 ret;
+    OM_uint32 minor_status;
+    gss_name_t principal_name = GSS_C_NO_NAME;
+    gss_cred_usage_t cred_usage;
+    gss_cred_id_t cred_handle;
+    OM_uint32 expiry_time;
+
+    TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
+          debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
+          pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
+
+    if (pAuthData)
+    {
+        /* gssapi has no way to specify the credentials to use, so we just
+         * return an error when the caller tries to do so */
+        FIXME("specific credentials not supported\n");
+        return SEC_E_UNKNOWN_CREDENTIALS;
+    }
+
+    switch (fCredentialUse)
+    {
+        case SECPKG_CRED_INBOUND:
+            cred_usage = GSS_C_ACCEPT;
+            break;
+        case SECPKG_CRED_OUTBOUND:
+            cred_usage = GSS_C_INITIATE;
+            break;
+        case SECPKG_CRED_BOTH:
+            cred_usage = GSS_C_BOTH;
+            break;
+        default:
+            phCredential = NULL;
+            return SEC_E_UNKNOWN_CREDENTIALS;
+    }
+
+    if (pszPrincipal)
+    {
+        char *principal_name_str;
+        int len;
+        gss_buffer_desc name_buffer;
+
+        len = WideCharToMultiByte(CP_UNIXCP, 0, pszPrincipal, -1, NULL, 0, NULL, NULL);
+        principal_name_str = HeapAlloc(GetProcessHeap(), 0, len);
+        if (!principal_name_str)
+            return SEC_E_INSUFFICIENT_MEMORY;
+        WideCharToMultiByte(CP_UNIXCP, 0, pszPrincipal, -1, principal_name_str, len, NULL, NULL);
+
+        name_buffer.length = len-1;
+        name_buffer.value = principal_name_str;
+
+        ret = p_gss_import_name(&minor_status, &name_buffer,
+                              GSS_C_NULL_OID /* FIXME: detect the appropriate value for this ourselves? */,
+                              &principal_name);
+        HeapFree(GetProcessHeap(), 0, principal_name_str);
+
+        if (ret != GSS_S_COMPLETE)
+            return kerberos_GSSAPIStatusToSecurityStatus(ret);
+    }
+
+    ret = p_gss_acquire_cred(&minor_status, principal_name, GSS_C_INDEFINITE,
+                           GSS_C_NULL_OID_SET /* FIXME: gss_mech_set_krb5 */, cred_usage, &cred_handle,
+                           NULL, &expiry_time);
+
+    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED)
+    {
+        if (ptsExpiry != NULL) {
+            kerberos_ExpiryTimeToTimeStamp(expiry_time, ptsExpiry);
+            kerberos_CredHandleGSSAPItoSSPI(cred_handle, phCredential);
+        }
+    }
+
+    if (principal_name != GSS_C_NO_NAME)
+        p_gss_release_name(&minor_status, &principal_name);
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+/***********************************************************************
+ *              AcquireCredentialsHandleA
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleA(
+ SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
+ PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
+ PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+    int user_sizeW, domain_sizeW, passwd_sizeW;
+
+    SEC_WCHAR *user = NULL, *domain = NULL, *passwd = NULL, *package = NULL;
+
+    PSEC_WINNT_AUTH_IDENTITY_W pAuthDataW = NULL;
+    PSEC_WINNT_AUTH_IDENTITY_A identity  = NULL;
+
+    TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
+     debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
+     pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
+
+    if(pszPackage != NULL)
+    {
+        int package_sizeW = MultiByteToWideChar(CP_ACP, 0, pszPackage, -1,
+                NULL, 0);
+
+        package = HeapAlloc(GetProcessHeap(), 0, package_sizeW *
+                sizeof(SEC_WCHAR));
+        MultiByteToWideChar(CP_ACP, 0, pszPackage, -1, package, package_sizeW);
+    }
+
+
+    if(pAuthData != NULL)
+    {
+        identity = (PSEC_WINNT_AUTH_IDENTITY_A)pAuthData;
+
+        if(identity->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI)
+        {
+            pAuthDataW = HeapAlloc(GetProcessHeap(), 0,
+                    sizeof(SEC_WINNT_AUTH_IDENTITY_W));
+
+            if(identity->UserLength != 0)
+            {
+                user_sizeW = MultiByteToWideChar(CP_ACP, 0,
+                    (LPCSTR)identity->User, identity->UserLength, NULL, 0);
+                user = HeapAlloc(GetProcessHeap(), 0, user_sizeW *
+                        sizeof(SEC_WCHAR));
+                MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->User,
+                    identity->UserLength, user, user_sizeW);
+            }
+            else
+            {
+                user_sizeW = 0;
+            }
+
+            if(identity->DomainLength != 0)
+            {
+                domain_sizeW = MultiByteToWideChar(CP_ACP, 0,
+                    (LPCSTR)identity->Domain, identity->DomainLength, NULL, 0);
+                domain = HeapAlloc(GetProcessHeap(), 0, domain_sizeW
+                    * sizeof(SEC_WCHAR));
+                MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Domain,
+                    identity->DomainLength, domain, domain_sizeW);
+            }
+            else
+            {
+                domain_sizeW = 0;
+            }
+
+            if(identity->PasswordLength != 0)
+            {
+                passwd_sizeW = MultiByteToWideChar(CP_ACP, 0,
+                    (LPCSTR)identity->Password, identity->PasswordLength,
+                    NULL, 0);
+                passwd = HeapAlloc(GetProcessHeap(), 0, passwd_sizeW
+                    * sizeof(SEC_WCHAR));
+                MultiByteToWideChar(CP_ACP, 0, (LPCSTR)identity->Password,
+                    identity->PasswordLength, passwd, passwd_sizeW);
+            }
+            else
+            {
+                passwd_sizeW = 0;
+            }
+
+            pAuthDataW->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+            pAuthDataW->User = user;
+            pAuthDataW->UserLength = user_sizeW;
+            pAuthDataW->Domain = domain;
+            pAuthDataW->DomainLength = domain_sizeW;
+            pAuthDataW->Password = passwd;
+            pAuthDataW->PasswordLength = passwd_sizeW;
+        }
+        else
+        {
+            pAuthDataW = (PSEC_WINNT_AUTH_IDENTITY_W)identity;
+        }
+    }
+
+    ret = kerberos_AcquireCredentialsHandleW(NULL, package, fCredentialUse,
+            pLogonID, pAuthDataW, pGetKeyFn, pGetKeyArgument, phCredential,
+            ptsExpiry);
+
+    HeapFree(GetProcessHeap(), 0, package);
+    HeapFree(GetProcessHeap(), 0, user);
+    HeapFree(GetProcessHeap(), 0, domain);
+    HeapFree(GetProcessHeap(), 0, passwd);
+    if(pAuthDataW != (PSEC_WINNT_AUTH_IDENTITY_W)identity)
+        HeapFree(GetProcessHeap(), 0, pAuthDataW);
+
+    return ret;
+}
+
+/*************************************************************************
+ *             kerberos_GetTokenBufferIndex
+ * Calculates the index of the secbuffer with BufferType == SECBUFFER_TOKEN
+ * Returns index if found or -1 if not found.
+ */
+static int kerberos_GetTokenBufferIndex(PSecBufferDesc pMessage)
+{
+    UINT i;
+
+    TRACE("%p\n", pMessage);
+
+    for( i = 0; i < pMessage->cBuffers; ++i )
+    {
+        if(pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
+            return i;
+    }
+
+    return -1;
+}
+
+/*************************************************************************
+ *             kerberos_GetDataBufferIndex
+ * Calculates the index of the first secbuffer with BufferType == SECBUFFER_DATA
+ * Returns index if found or -1 if not found.
+ */
+static int kerberos_GetDataBufferIndex(PSecBufferDesc pMessage)
+{
+    UINT i;
+
+    TRACE("%p\n", pMessage);
+
+    for( i = 0; i < pMessage->cBuffers; ++i )
+    {
+        if(pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
+            return i;
+    }
+
+    return -1;
+}
+
+/***********************************************************************
+ *              InitializeSecurityContextW
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextW(
+ PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
+ ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
+ PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
+ PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    int output_token_idx;
+    OM_uint32 ret;
+    OM_uint32 minor_status;
+    gss_cred_id_t cred_handle;
+    gss_ctx_id_t context_handle = GSS_C_NO_CONTEXT;
+    OM_uint32 req_flags = 0;
+    OM_uint32 ret_flags;
+    gss_buffer_desc input_token;
+    gss_buffer_desc output_token;
+    OM_uint32 expiry_time;
+    gss_name_t target_name = GSS_C_NO_NAME;
+
+    TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
+     debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
+     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+
+    if (!phContext && !pInput)
+    {
+        if (!phCredential)
+            return SEC_E_INVALID_HANDLE;
+        cred_handle = kerberos_CredHandleSSPItoGSSAPI(phCredential);
+    }
+    else
+    {
+        cred_handle = GSS_C_NO_CREDENTIAL;
+
+        if (phContext)
+        {
+            context_handle = kerberos_CtxtHandleSSPItoGSSAPI(phContext);
+        }
+    }
+
+    input_token.length = 0;
+    if (pInput)
+    {
+        int input_token_idx = kerberos_GetTokenBufferIndex(pInput);
+        if (input_token_idx == -1)
+            return SEC_E_INVALID_TOKEN;
+        input_token.length = pInput->pBuffers[input_token_idx].cbBuffer;
+        input_token.value = pInput->pBuffers[input_token_idx].pvBuffer;
+    }
+
+    req_flags = kerberos_CtxtReqsSSPItoGSSAPI(fContextReq);
+
+    output_token_idx = kerberos_GetTokenBufferIndex(pOutput);
+    if (output_token_idx == -1)
+        return SEC_E_INVALID_TOKEN;
+
+    output_token.length = pOutput->pBuffers[output_token_idx].cbBuffer;
+    output_token.value = pOutput->pBuffers[output_token_idx].pvBuffer;
+
+    if (pszTargetName)
+    {
+        char *target_name_str;
+        int len;
+        gss_buffer_desc name_buffer;
+
+        len = WideCharToMultiByte(CP_UNIXCP, 0, pszTargetName, -1, NULL, 0, NULL, NULL);
+        target_name_str = HeapAlloc(GetProcessHeap(), 0, len);
+        if (!target_name_str)
+            return SEC_E_INSUFFICIENT_MEMORY;
+        WideCharToMultiByte(CP_UNIXCP, 0, pszTargetName, -1, target_name_str, len, NULL, NULL);
+
+        name_buffer.length = len-1;
+        name_buffer.value = target_name_str;
+
+        ret = p_gss_import_name(&minor_status, &name_buffer,
+                              GSS_C_NULL_OID /* FIXME: detect the appropriate value for this ourselves? */,
+                              &target_name);
+        HeapFree(GetProcessHeap(), 0, target_name_str);
+
+        if (ret != GSS_S_COMPLETE)
+            return kerberos_GSSAPIStatusToSecurityStatus(ret);
+    }
+
+    ret = p_gss_init_sec_context(&minor_status, cred_handle, &context_handle,
+                               target_name, GSS_C_NO_OID, req_flags, 0,
+                               GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL,
+                               &output_token, &ret_flags, &expiry_time);
+
+    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED)
+    {
+        kerberos_ExpiryTimeToTimeStamp(expiry_time, ptsExpiry);
+        kerberos_CtxtHandleGSSAPItoSSPI(context_handle, phNewContext);
+
+        if (pfContextAttr)
+        {
+            kerberos_CtxtAttrsGSSPItoSSPI(ret_flags, pfContextAttr);
+        }
+    }
+
+    if (target_name != GSS_C_NO_NAME)
+        p_gss_release_name(&minor_status, &target_name);
+
+    pOutput->pBuffers[output_token_idx].cbBuffer = (ULONG) output_token.length;
+    pOutput->pBuffers[output_token_idx].pvBuffer = output_token.value;
+
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+/***********************************************************************
+ *              InitializeSecurityContextA
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA(
+ PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
+ ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
+ PSecBufferDesc pInput,ULONG Reserved2, PCtxtHandle phNewContext,
+ PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    SECURITY_STATUS ret;
+    SEC_WCHAR *target = NULL;
+
+    TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
+     debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
+     Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
+
+    if(pszTargetName != NULL)
+    {
+        int target_size = MultiByteToWideChar(CP_ACP, 0, pszTargetName,
+            strlen(pszTargetName)+1, NULL, 0);
+        target = HeapAlloc(GetProcessHeap(), 0, target_size *
+                sizeof(SEC_WCHAR));
+        MultiByteToWideChar(CP_ACP, 0, pszTargetName, strlen(pszTargetName)+1,
+            target, target_size);
+    }
+
+    ret = kerberos_InitializeSecurityContextW(phCredential, phContext, target,
+            fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
+            phNewContext, pOutput, pfContextAttr, ptsExpiry);
+
+    HeapFree(GetProcessHeap(), 0, target);
+    return ret;
+}
+
+/***********************************************************************
+ *              AcceptSecurityContext
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext(
+ PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
+ ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext,
+ PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
+{
+    int output_token_idx;
+    OM_uint32 ret;
+    OM_uint32 minor_status;
+    gss_cred_id_t cred_handle;
+    gss_ctx_id_t context_handle;
+    OM_uint32 ret_flags;
+    gss_buffer_desc input_token;
+    gss_buffer_desc output_token;
+    OM_uint32 expiry_time;
+
+    TRACE("%p %p %p %d %d %p %p %p %p\n", phCredential, phContext, pInput,
+     fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr,
+     ptsExpiry);
+
+    if (!phContext && !pInput)
+    {
+        if (!phCredential)
+            return SEC_E_INVALID_HANDLE;
+        cred_handle = kerberos_CredHandleSSPItoGSSAPI(phCredential);
+    }
+    else
+        cred_handle = GSS_C_NO_CREDENTIAL;
+
+    if (pInput)
+    {
+        int input_token_idx = kerberos_GetTokenBufferIndex(pInput);
+        if (input_token_idx == -1)
+            return SEC_E_INVALID_TOKEN;
+        input_token.length = pInput->pBuffers[input_token_idx].cbBuffer;
+        input_token.value = pInput->pBuffers[input_token_idx].pvBuffer;
+    }
+
+    output_token_idx = kerberos_GetTokenBufferIndex(pOutput);
+    if (output_token_idx == -1)
+        return SEC_E_INVALID_TOKEN;
+    output_token.length = pOutput->pBuffers[output_token_idx].cbBuffer;
+    output_token.value = pOutput->pBuffers[output_token_idx].pvBuffer;
+
+    /* FIXME: get target_name */
+    ret = p_gss_accept_sec_context(&minor_status, &context_handle, cred_handle,
+                                 &input_token, GSS_C_NO_CHANNEL_BINDINGS,
+                                 NULL, NULL, &output_token, &ret_flags,
+                                 &expiry_time, NULL);
+
+    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED)
+    {
+        kerberos_ExpiryTimeToTimeStamp(expiry_time, ptsExpiry);
+        kerberos_CtxtHandleGSSAPItoSSPI(context_handle, phContext);
+    }
+
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+/***********************************************************************
+ *              CompleteAuthToken
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_CompleteAuthToken(PCtxtHandle phContext,
+ PSecBufferDesc pToken)
+{
+    /* We never need to call CompleteAuthToken anyway */
+    TRACE("%p %p\n", phContext, pToken);
+    if (!phContext)
+        return SEC_E_INVALID_HANDLE;
+
+    return SEC_E_OK;
+}
+
+/***********************************************************************
+ *              DeleteSecurityContext
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_DeleteSecurityContext(PCtxtHandle phContext)
+{
+    OM_uint32 ret;
+    OM_uint32 minor_status;
+    gss_buffer_desc output_buffer;
+    gss_ctx_id_t context_handle;
+
+    TRACE("%p\n", phContext);
+    if (!phContext)
+        return SEC_E_INVALID_HANDLE;
+
+    /* FIXME: is this correct? */
+    output_buffer.length = 0;
+    output_buffer.value = NULL;
+
+    context_handle = kerberos_CtxtHandleSSPItoGSSAPI(phContext);
+    ret = p_gss_delete_sec_context(&minor_status, &context_handle, &output_buffer);
+
+    phContext->dwUpper = 0;
+    phContext->dwLower = 0;
+
+    /* From https://docs.oracle.com/cd/E19683-01/816-0214/6m6nf1opf/index.html
+
+    * This function is provided for compatibility with the GSS-API version 1.
+    *  Because gss_delete_sec_context() no longer returns a valid output_token
+    *  to be sent to gss_process_context_token(), applications using a newer
+    *  version of the GSS-API do not need to rely on this function.
+    *
+    *
+    if (ret == GSS_S_COMPLETE)
+    {
+
+        ret = gss_process_context_token(&minor_status, context_handle,
+                                        &output_buffer);
+    }*/
+
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+/***********************************************************************
+ *              QueryContextAttributesW
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesW(PCtxtHandle phContext,
+ ULONG ulAttribute, void *pBuffer)
+{
+    TRACE("%p %d %p\n", phContext, ulAttribute, pBuffer);
+    if (!phContext)
+        return SEC_E_INVALID_HANDLE;
+
+    switch(ulAttribute)
+    {
+#define _x(x) case (x) : FIXME(#x" stub\n"); break
+        _x(SECPKG_ATTR_ACCESS_TOKEN);
+        _x(SECPKG_ATTR_AUTHORITY);
+        _x(SECPKG_ATTR_DCE_INFO);
+#if 0
+        case SECPKG_ATTR_FLAGS:
+        {
+            PSecPkgContext_Flags spcf = (PSecPkgContext_Flags)pBuffer;
+            PNegoHelper helper = (PNegoHelper)phContext->dwLower;
+
+            spcf->Flags = 0;
+            if(helper->neg_flags & NTLMSSP_NEGOTIATE_SIGN)
+                spcf->Flags |= ISC_RET_INTEGRITY;
+            if(helper->neg_flags & NTLMSSP_NEGOTIATE_SEAL)
+                spcf->Flags |= ISC_RET_CONFIDENTIALITY;
+            return SEC_E_OK;
+        }
+#endif
+        _x(SECPKG_ATTR_KEY_INFO);
+        _x(SECPKG_ATTR_LIFESPAN);
+        _x(SECPKG_ATTR_NAMES);
+        _x(SECPKG_ATTR_NATIVE_NAMES);
+        _x(SECPKG_ATTR_NEGOTIATION_INFO);
+        _x(SECPKG_ATTR_PACKAGE_INFO);
+        _x(SECPKG_ATTR_PASSWORD_EXPIRY);
+        _x(SECPKG_ATTR_SESSION_KEY);
+#if 0
+        case SECPKG_ATTR_SIZES:
+        {
+            PSecPkgContext_Sizes spcs = (PSecPkgContext_Sizes)pBuffer;
+            spcs->cbMaxToken = NTLM_MAX_BUF;
+            spcs->cbMaxSignature = 16;
+            spcs->cbBlockSize = 0;
+            spcs->cbSecurityTrailer = 16;
+            return SEC_E_OK;
+        }
+#endif
+        _x(SECPKG_ATTR_STREAM_SIZES);
+        _x(SECPKG_ATTR_TARGET_INFORMATION);
+#undef _x
+        default:
+            TRACE("Unknown value %d passed for ulAttribute\n", ulAttribute);
+    }
+
+    return SEC_E_UNSUPPORTED_FUNCTION;
+}
+
+/***********************************************************************
+ *              QueryContextAttributesA
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(PCtxtHandle phContext,
+ ULONG ulAttribute, void *pBuffer)
+{
+    return kerberos_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
+}
+
+/***********************************************************************
+ *              ImpersonateSecurityContext
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_ImpersonateSecurityContext(PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p\n", phContext);
+    if (phContext)
+    {
+        ret = SEC_E_UNSUPPORTED_FUNCTION;
+    }
+    else
+    {
+        ret = SEC_E_INVALID_HANDLE;
+    }
+    return ret;
+}
+
+/***********************************************************************
+ *              RevertSecurityContext
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_RevertSecurityContext(PCtxtHandle phContext)
+{
+    SECURITY_STATUS ret;
+
+    TRACE("%p\n", phContext);
+    if (phContext)
+    {
+        ret = SEC_E_UNSUPPORTED_FUNCTION;
+    }
+    else
+    {
+        ret = SEC_E_INVALID_HANDLE;
+    }
+    return ret;
+}
+
+/***********************************************************************
+ *              MakeSignature
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo)
+{
+    int token_idx, message_idx;
+    OM_uint32 minor_status;
+    OM_uint32 ret;
+    gss_buffer_desc token_buffer;
+    gss_buffer_desc message_buffer;
+    gss_ctx_id_t context_handle;
+    OM_uint32 qop = GSS_C_QOP_DEFAULT;
+
+    TRACE("%p %d %p %d\n", phContext, fQOP, pMessage, MessageSeqNo);
+
+    if (!phContext)
+        return SEC_E_INVALID_HANDLE;
+
+    context_handle = kerberos_CtxtHandleSSPItoGSSAPI(phContext);
+
+    if(fQOP)
+        FIXME("Ignoring fQOP 0x%08x\n", fQOP);
+
+    if(MessageSeqNo)
+        FIXME("Ignoring MessageSeqNo\n");
+
+    if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
+        return SEC_E_INVALID_TOKEN;
+
+    /* If we didn't find a SECBUFFER_TOKEN type buffer */
+    if((token_idx = kerberos_GetTokenBufferIndex(pMessage)) == -1)
+        return SEC_E_INVALID_TOKEN;
+
+    token_buffer.length = pMessage->pBuffers[token_idx].cbBuffer;
+    token_buffer.value = pMessage->pBuffers[token_idx].pvBuffer;
+
+    /* Note: gssapi doesn't support multiple buffers like SSPI does, so we only
+     * takes the first buffer */
+    for (message_idx = 0; message_idx < pMessage->cBuffers; ++message_idx)
+    {
+        if (pMessage->pBuffers[message_idx].BufferType & SECBUFFER_DATA)
+            break;
+    }
+    if (message_idx == pMessage->cBuffers)
+        return SEC_E_INVALID_TOKEN;
+
+    message_buffer.length = pMessage->pBuffers[token_idx].cbBuffer;
+    message_buffer.value = pMessage->pBuffers[token_idx].pvBuffer;
+
+    ret = p_gss_get_mic(&minor_status, context_handle, qop, &message_buffer, &token_buffer);
+
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+/***********************************************************************
+ *              VerifySignature
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_VerifySignature(PCtxtHandle phContext,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
+{
+    int token_idx, message_idx;
+    OM_uint32 minor_status;
+    OM_uint32 ret;
+    gss_buffer_desc token_buffer;
+    gss_buffer_desc message_buffer;
+    gss_ctx_id_t context_handle;
+    gss_qop_t qop_state;
+
+    TRACE("%p %p %d %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
+
+    if(!phContext)
+        return SEC_E_INVALID_HANDLE;
+
+    context_handle = kerberos_CtxtHandleSSPItoGSSAPI(phContext);
+
+    if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
+        return SEC_E_INVALID_TOKEN;
+
+    if((token_idx = kerberos_GetTokenBufferIndex(pMessage)) == -1)
+        return SEC_E_INVALID_TOKEN;
+
+    if(MessageSeqNo)
+        FIXME("Ignoring MessageSeqNo\n");
+
+    /* If we didn't find a SECBUFFER_TOKEN type buffer */
+    if((token_idx = kerberos_GetTokenBufferIndex(pMessage)) == -1)
+        return SEC_E_INVALID_TOKEN;
+
+    token_buffer.length = pMessage->pBuffers[token_idx].cbBuffer;
+    token_buffer.value = pMessage->pBuffers[token_idx].pvBuffer;
+
+    /* Note: gssapi doesn't support multiple buffers like SSPI does, so we only
+     * takes the first buffer */
+    for (message_idx = 0; message_idx < pMessage->cBuffers; ++message_idx)
+    {
+        if (pMessage->pBuffers[message_idx].BufferType & SECBUFFER_DATA)
+            break;
+    }
+    if (message_idx == pMessage->cBuffers)
+        return SEC_E_INVALID_TOKEN;
+
+    message_buffer.length = pMessage->pBuffers[token_idx].cbBuffer;
+    message_buffer.value = pMessage->pBuffers[token_idx].pvBuffer;
+
+    ret = p_gss_verify_mic(&minor_status, context_handle, &message_buffer,
+                         &token_buffer, &qop_state);
+
+    *pfQOP = 0;
+
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+/***********************************************************************
+ *             FreeCredentialsHandle
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_FreeCredentialsHandle(
+        PCredHandle phCredential)
+{
+    OM_uint32 ret;
+    OM_uint32 minor_status;
+
+    TRACE("(%p)\n", phCredential);
+
+    if (phCredential)
+    {
+        gss_cred_id_t cred_handle = kerberos_CredHandleSSPItoGSSAPI(phCredential);
+
+        HeapFree(GetProcessHeap(), 0, cred_handle);
+
+        phCredential->dwUpper = 0;
+        phCredential->dwLower = 0;
+
+        return kerberos_GSSAPIStatusToSecurityStatus(ret);
+    }
+    else
+        return SEC_E_OK;
+}
+
+/***********************************************************************
+ *             FreeContextBuffer (KERBEROS.@)
+ */
+SECURITY_STATUS WINAPI kerberos_FreeContextBuffer(PVOID pv)
+{
+    HeapFree(GetProcessHeap(), 0, pv);
+
+    return SEC_E_OK;
+}
+
+/***********************************************************************
+ *             EncryptMessage
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(PCtxtHandle phContext,
+        ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo)
+{
+    gss_ctx_id_t context_handle;
+    int token_idx, data_idx;
+    gss_qop_t qop_req = GSS_C_QOP_DEFAULT;
+    int conf_state;
+    gss_buffer_desc input_message_buffer;
+    gss_buffer_desc output_message_buffer;
+    OM_uint32 ret;
+    OM_uint32 minor_status;
+
+    TRACE("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo);
+
+    if(!phContext)
+        return SEC_E_INVALID_HANDLE;
+    context_handle = kerberos_CtxtHandleSSPItoGSSAPI(phContext);
+
+    if(fQOP && (fQOP != SECQOP_WRAP_NO_ENCRYPT))
+        FIXME("Ignoring fQOP 0x%x\n", fQOP);
+
+    if(MessageSeqNo)
+        FIXME("Ignoring MessageSeqNo\n");
+
+    if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
+        return SEC_E_INVALID_TOKEN;
+
+    if((token_idx = kerberos_GetTokenBufferIndex(pMessage)) == -1)
+        return SEC_E_INVALID_TOKEN;
+
+    if((data_idx = kerberos_GetDataBufferIndex(pMessage)) ==-1 )
+        return SEC_E_INVALID_TOKEN;
+
+    input_message_buffer.length = pMessage->pBuffers[data_idx].cbBuffer;
+    input_message_buffer.value = pMessage->pBuffers[data_idx].pvBuffer;
+
+    /* FIXME: does gss_wrap allow overlapping buffers? */
+    output_message_buffer.length = pMessage->pBuffers[data_idx].cbBuffer;
+    output_message_buffer.value = pMessage->pBuffers[data_idx].pvBuffer;
+
+    /* FIXME: sign data and store it in token */
+
+    ret = p_gss_wrap(&minor_status, context_handle,
+                   (fQOP == SECQOP_WRAP_NO_ENCRYPT) ? FALSE : TRUE, qop_req,
+                   &input_message_buffer, &conf_state, &output_message_buffer);
+
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+/***********************************************************************
+ *             DecryptMessage
+ */
+static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(PCtxtHandle phContext,
+        PSecBufferDesc pMessage, ULONG MessageSeqNo, PULONG pfQOP)
+{
+    gss_ctx_id_t context_handle;
+    OM_uint32 ret;
+    OM_uint32 minor_status;
+    int token_idx, data_idx;
+    gss_buffer_desc input_message_buffer;
+    gss_buffer_desc output_message_buffer;
+    gss_qop_t qop_req;
+    int conf_state;
+
+    TRACE("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP);
+
+    if(!phContext)
+        return SEC_E_INVALID_HANDLE;
+
+    context_handle = kerberos_CtxtHandleSSPItoGSSAPI(phContext);
+
+    if(MessageSeqNo)
+        FIXME("Ignoring MessageSeqNo\n");
+
+    if(!pMessage || !pMessage->pBuffers || pMessage->cBuffers < 2)
+        return SEC_E_INVALID_TOKEN;
+
+    if((token_idx = kerberos_GetTokenBufferIndex(pMessage)) == -1)
+        return SEC_E_INVALID_TOKEN;
+
+    if((data_idx = kerberos_GetDataBufferIndex(pMessage)) ==-1)
+        return SEC_E_INVALID_TOKEN;
+
+    input_message_buffer.length = pMessage->pBuffers[data_idx].cbBuffer;
+    input_message_buffer.value = pMessage->pBuffers[data_idx].pvBuffer;
+
+    /* FIXME: does gss_wrap allow overlapping buffers? */
+    output_message_buffer.length = pMessage->pBuffers[data_idx].cbBuffer;
+    output_message_buffer.value = pMessage->pBuffers[data_idx].pvBuffer;
+
+    ret = p_gss_unwrap(&minor_status, context_handle, &input_message_buffer,
+                     &output_message_buffer, &conf_state, &qop_req);
+
+    *pfQOP = 0;
+
+    /* FIXME: verify data using token */
+
+    return kerberos_GSSAPIStatusToSecurityStatus(ret);
+}
+
+static SecurityFunctionTableA gssKerberosTableA = {
+    SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION,
+    kerberos_EnumerateSecurityPackagesA,    /* EnumerateSecurityPackagesA */
+    kerberos_QueryCredentialsAttributesA,   /* QueryCredentialsAttributesA */
+    kerberos_AcquireCredentialsHandleA,     /* AcquireCredentialsHandleA */
+    kerberos_FreeCredentialsHandle,         /* FreeCredentialsHandle */
+    NULL,   /* Reserved2 */
+    kerberos_InitializeSecurityContextA,    /* InitializeSecurityContextA */
+    kerberos_AcceptSecurityContext,         /* AcceptSecurityContext */
+    kerberos_CompleteAuthToken,             /* CompleteAuthToken */
+    kerberos_DeleteSecurityContext,         /* DeleteSecurityContext */
+    NULL,  /* ApplyControlToken */
+    kerberos_QueryContextAttributesA,       /* QueryContextAttributesA */
+    kerberos_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
+    kerberos_RevertSecurityContext,         /* RevertSecurityContext */
+    kerberos_MakeSignature,                 /* MakeSignature */
+    kerberos_VerifySignature,               /* VerifySignature */
+    kerberos_FreeContextBuffer,             /* FreeContextBuffer */
+    NULL,   /* QuerySecurityPackageInfoA */
+    NULL,   /* Reserved3 */
+    NULL,   /* Reserved4 */
+    NULL,   /* ExportSecurityContext */
+    NULL,   /* ImportSecurityContextA */
+    NULL,   /* AddCredentialsA */
+    NULL,   /* Reserved8 */
+    NULL,   /* QuerySecurityContextToken */
+    kerberos_EncryptMessage,                /* EncryptMessage */
+    kerberos_DecryptMessage,                /* DecryptMessage */
+    NULL,   /* SetContextAttributesA */
+};
+
+static SecurityFunctionTableW gssKerberosTableW = {
+    SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION,
+    kerberos_EnumerateSecurityPackagesW,    /* EnumerateSecurityPackagesW */
+    kerberos_QueryCredentialsAttributesW,   /* QueryCredentialsAttributesW */
+    kerberos_AcquireCredentialsHandleW,     /* AcquireCredentialsHandleW */
+    kerberos_FreeCredentialsHandle,         /* FreeCredentialsHandle */
+    NULL,   /* Reserved2 */
+    kerberos_InitializeSecurityContextW,    /* InitializeSecurityContextW */
+    kerberos_AcceptSecurityContext,         /* AcceptSecurityContext */
+    kerberos_CompleteAuthToken,             /* CompleteAuthToken */
+    kerberos_DeleteSecurityContext,         /* DeleteSecurityContext */
+    NULL,  /* ApplyControlToken */
+    kerberos_QueryContextAttributesW,       /* QueryContextAttributesW */
+    kerberos_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
+    kerberos_RevertSecurityContext,         /* RevertSecurityContext */
+    kerberos_MakeSignature,                 /* MakeSignature */
+    kerberos_VerifySignature,               /* VerifySignature */
+    kerberos_FreeContextBuffer,             /* FreeContextBuffer */
+    NULL,   /* QuerySecurityPackageInfoW */
+    NULL,   /* Reserved3 */
+    NULL,   /* Reserved4 */
+    NULL,   /* ExportSecurityContext */
+    NULL,   /* ImportSecurityContextW */
+    NULL,   /* AddCredentialsW */
+    NULL,   /* Reserved8 */
+    NULL,   /* QuerySecurityContextToken */
+    kerberos_EncryptMessage,                /* EncryptMessage */
+    kerberos_DecryptMessage,                /* DecryptMessage */
+    NULL,   /* SetContextAttributesW */
+};
+
+
+/***********************************************************************
+ *            InitSecurityInterfaceA (KERBEROS.@)
+ */
+PSecurityFunctionTableA WINAPI InitSecurityInterfaceA(void)
+{
+    if (!load_gssapi())
+    {
+        return NULL;
+    }
+
+    return &gssKerberosTableA;
+}
+
+/***********************************************************************
+ *            InitSecurityInterfaceW (KERBEROS.@)
+ */
+PSecurityFunctionTableW WINAPI InitSecurityInterfaceW(void)
+{
+    if (!load_gssapi())
+    {
+        return NULL;
+    }
+
+    return &gssKerberosTableW;
+}
diff --git a/dlls/secur32/Makefile.in b/dlls/secur32/Makefile.in
index 6548521f53..c9acdee457 100644
--- a/dlls/secur32/Makefile.in
+++ b/dlls/secur32/Makefile.in
@@ -9,7 +9,6 @@ C_SRCS = \
 	base64_codec.c \
 	dispatcher.c \
 	hmac_md5.c \
-	kerberos.c \
 	lsa.c \
 	negotiate.c \
 	ntlm.c \
diff --git a/dlls/secur32/kerberos.c b/dlls/secur32/kerberos.c
deleted file mode 100644
index 753e9748d2..0000000000
--- a/dlls/secur32/kerberos.c
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright 2005, 2006 Kai Blin
- * Copyright 2016 Jacek Caban for CodeWeavers
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-#include <assert.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "windef.h"
-#include "winbase.h"
-#include "rpc.h"
-#include "sspi.h"
-
-#include "secur32_priv.h"
-
-#include "wine/debug.h"
-
-WINE_DEFAULT_DEBUG_CHANNEL(secur32);
-
-#define KERBEROS_MAX_BUF 12000
-
-/***********************************************************************
- *              QueryCredentialsAttributesA
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesA(CredHandle *phCredential, ULONG ulAttribute, void *pBuffer)
-{
-    FIXME("(%p %d %p)\n", phCredential, ulAttribute, pBuffer);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              QueryCredentialsAttributesW
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesW(CredHandle *phCredential, ULONG ulAttribute, void *pBuffer)
-{
-    FIXME("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              AcquireCredentialsHandleW
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleW(SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
-        LUID *pLogonID, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pGetKeyArgument, CredHandle *phCredential, TimeStamp *ptsExpiry)
-{
-    FIXME("(%s %s 0x%08x %p %p %p %p %p %p)\n", debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
-          pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
-    return SEC_E_NO_CREDENTIALS;
-}
-
-/***********************************************************************
- *              AcquireCredentialsHandleA
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_AcquireCredentialsHandleA(SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
-        LUID *pLogonID, void *pAuthData, SEC_GET_KEY_FN pGetKeyFn, void *pGetKeyArgument, CredHandle *phCredential, TimeStamp *ptsExpiry)
-{
-    FIXME("(%s %s 0x%08x %p %p %p %p %p %p)\n", debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
-          pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              InitializeSecurityContextW
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextW(CredHandle *phCredential, CtxtHandle *phContext, SEC_WCHAR *pszTargetName,
-        ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, SecBufferDesc *pInput, ULONG Reserved2, CtxtHandle *phNewContext,
-        SecBufferDesc *pOutput, ULONG *pfContextAttr, TimeStamp *ptsExpiry)
-{
-    FIXME("(%p %p %s 0x%08x %d %d %p %d %p %p %p %p)\n", phCredential, phContext, debugstr_w(pszTargetName),
-          fContextReq, Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              InitializeSecurityContextA
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_InitializeSecurityContextA(CredHandle *phCredential, CtxtHandle *phContext, SEC_CHAR *pszTargetName,
-        ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep, SecBufferDesc *pInput, ULONG Reserved2, CtxtHandle *phNewContext,
-        SecBufferDesc *pOutput, ULONG *pfContextAttr, TimeStamp *ptsExpiry)
-{
-    FIXME("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext, debugstr_a(pszTargetName), fContextReq,
-          Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              AcceptSecurityContext
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_AcceptSecurityContext(CredHandle *phCredential, CtxtHandle *phContext, SecBufferDesc *pInput,
-        ULONG fContextReq, ULONG TargetDataRep, CtxtHandle *phNewContext, SecBufferDesc *pOutput, ULONG *pfContextAttr, TimeStamp *ptsExpiry)
-{
-    FIXME("(%p %p %p %d %d %p %p %p %p)\n", phCredential, phContext, pInput, fContextReq, TargetDataRep, phNewContext, pOutput,
-          pfContextAttr, ptsExpiry);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              CompleteAuthToken
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_CompleteAuthToken(CtxtHandle *phContext, SecBufferDesc *pToken)
-{
-    FIXME("(%p %p)\n", phContext, pToken);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              DeleteSecurityContext
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_DeleteSecurityContext(CtxtHandle *phContext)
-{
-    FIXME("(%p)\n", phContext);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              QueryContextAttributesW
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesW(CtxtHandle *phContext, ULONG ulAttribute, void *pBuffer)
-{
-    FIXME("(%p %d %p)\n", phContext, ulAttribute, pBuffer);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              QueryContextAttributesA
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_QueryContextAttributesA(CtxtHandle *phContext, ULONG ulAttribute, void *pBuffer)
-{
-    FIXME("(%p %d %p)\n", phContext, ulAttribute, pBuffer);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              ImpersonateSecurityContext
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_ImpersonateSecurityContext(CtxtHandle *phContext)
-{
-    FIXME("(%p)\n", phContext);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              RevertSecurityContext
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_RevertSecurityContext(CtxtHandle *phContext)
-{
-    FIXME("(%p)\n", phContext);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              MakeSignature
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_MakeSignature(CtxtHandle *phContext, ULONG fQOP, SecBufferDesc *pMessage, ULONG MessageSeqNo)
-{
-    FIXME("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *              VerifySignature
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_VerifySignature(CtxtHandle *phContext, SecBufferDesc *pMessage, ULONG MessageSeqNo, PULONG pfQOP)
-{
-    FIXME("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *             FreeCredentialsHandle
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_FreeCredentialsHandle(PCredHandle phCredential)
-{
-    FIXME("(%p)\n", phCredential);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *             EncryptMessage
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_EncryptMessage(CtxtHandle *phContext, ULONG fQOP, SecBufferDesc *pMessage, ULONG MessageSeqNo)
-{
-    FIXME("(%p %d %p %d)\n", phContext, fQOP, pMessage, MessageSeqNo);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-/***********************************************************************
- *             DecryptMessage
- */
-static SECURITY_STATUS SEC_ENTRY kerberos_DecryptMessage(CtxtHandle *phContext, SecBufferDesc *pMessage, ULONG MessageSeqNo, PULONG pfQOP)
-{
-    FIXME("(%p %p %d %p)\n", phContext, pMessage, MessageSeqNo, pfQOP);
-    return SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-static const SecurityFunctionTableA kerberosTableA = {
-    1,
-    NULL,   /* EnumerateSecurityPackagesA */
-    kerberos_QueryCredentialsAttributesA,   /* QueryCredentialsAttributesA */
-    kerberos_AcquireCredentialsHandleA,     /* AcquireCredentialsHandleA */
-    kerberos_FreeCredentialsHandle,         /* FreeCredentialsHandle */
-    NULL,   /* Reserved2 */
-    kerberos_InitializeSecurityContextA,    /* InitializeSecurityContextA */
-    kerberos_AcceptSecurityContext,         /* AcceptSecurityContext */
-    kerberos_CompleteAuthToken,             /* CompleteAuthToken */
-    kerberos_DeleteSecurityContext,         /* DeleteSecurityContext */
-    NULL,  /* ApplyControlToken */
-    kerberos_QueryContextAttributesA,       /* QueryContextAttributesA */
-    kerberos_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
-    kerberos_RevertSecurityContext,         /* RevertSecurityContext */
-    kerberos_MakeSignature,                 /* MakeSignature */
-    kerberos_VerifySignature,               /* VerifySignature */
-    FreeContextBuffer,                      /* FreeContextBuffer */
-    NULL,   /* QuerySecurityPackageInfoA */
-    NULL,   /* Reserved3 */
-    NULL,   /* Reserved4 */
-    NULL,   /* ExportSecurityContext */
-    NULL,   /* ImportSecurityContextA */
-    NULL,   /* AddCredentialsA */
-    NULL,   /* Reserved8 */
-    NULL,   /* QuerySecurityContextToken */
-    kerberos_EncryptMessage,                /* EncryptMessage */
-    kerberos_DecryptMessage,                /* DecryptMessage */
-    NULL,   /* SetContextAttributesA */
-};
-
-static const SecurityFunctionTableW kerberosTableW = {
-    1,
-    NULL,   /* EnumerateSecurityPackagesW */
-    kerberos_QueryCredentialsAttributesW,   /* QueryCredentialsAttributesW */
-    kerberos_AcquireCredentialsHandleW,     /* AcquireCredentialsHandleW */
-    kerberos_FreeCredentialsHandle,         /* FreeCredentialsHandle */
-    NULL,   /* Reserved2 */
-    kerberos_InitializeSecurityContextW,    /* InitializeSecurityContextW */
-    kerberos_AcceptSecurityContext,         /* AcceptSecurityContext */
-    kerberos_CompleteAuthToken,             /* CompleteAuthToken */
-    kerberos_DeleteSecurityContext,         /* DeleteSecurityContext */
-    NULL,  /* ApplyControlToken */
-    kerberos_QueryContextAttributesW,       /* QueryContextAttributesW */
-    kerberos_ImpersonateSecurityContext,    /* ImpersonateSecurityContext */
-    kerberos_RevertSecurityContext,         /* RevertSecurityContext */
-    kerberos_MakeSignature,                 /* MakeSignature */
-    kerberos_VerifySignature,               /* VerifySignature */
-    FreeContextBuffer,                      /* FreeContextBuffer */
-    NULL,   /* QuerySecurityPackageInfoW */
-    NULL,   /* Reserved3 */
-    NULL,   /* Reserved4 */
-    NULL,   /* ExportSecurityContext */
-    NULL,   /* ImportSecurityContextW */
-    NULL,   /* AddCredentialsW */
-    NULL,   /* Reserved8 */
-    NULL,   /* QuerySecurityContextToken */
-    kerberos_EncryptMessage,                /* EncryptMessage */
-    kerberos_DecryptMessage,                /* DecryptMessage */
-    NULL,   /* SetContextAttributesW */
-};
-
-#define KERBEROS_COMMENT \
-    {'M','i','c','r','o','s','o','f','t',' ','K','e','r','b','e','r','o','s',' ','V','1','.','0',0}
-static CHAR kerberos_comment_A[] = KERBEROS_COMMENT;
-static WCHAR kerberos_comment_W[] = KERBEROS_COMMENT;
-
-#define KERBEROS_NAME {'K','e','r','b','e','r','o','s',0}
-static char kerberos_name_A[] = KERBEROS_NAME;
-static WCHAR kerberos_name_W[] = KERBEROS_NAME;
-
-#define CAPS \
-    ( SECPKG_FLAG_INTEGRITY \
-    | SECPKG_FLAG_PRIVACY \
-    | SECPKG_FLAG_TOKEN_ONLY \
-    | SECPKG_FLAG_DATAGRAM \
-    | SECPKG_FLAG_CONNECTION \
-    | SECPKG_FLAG_MULTI_REQUIRED \
-    | SECPKG_FLAG_EXTENDED_ERROR \
-    | SECPKG_FLAG_IMPERSONATION \
-    | SECPKG_FLAG_ACCEPT_WIN32_NAME \
-    | SECPKG_FLAG_NEGOTIABLE \
-    | SECPKG_FLAG_GSS_COMPATIBLE \
-    | SECPKG_FLAG_LOGON \
-    | SECPKG_FLAG_MUTUAL_AUTH \
-    | SECPKG_FLAG_DELEGATION \
-    | SECPKG_FLAG_READONLY_WITH_CHECKSUM \
-    | SECPKG_FLAG_RESTRICTED_TOKENS \
-    | SECPKG_FLAG_APPCONTAINER_CHECKS)
-
-static const SecPkgInfoW infoW = {
-    CAPS,
-    1,
-    RPC_C_AUTHN_GSS_KERBEROS,
-    KERBEROS_MAX_BUF,
-    kerberos_name_W,
-    kerberos_comment_W
-};
-
-static const SecPkgInfoA infoA = {
-    CAPS,
-    1,
-    RPC_C_AUTHN_GSS_KERBEROS,
-    KERBEROS_MAX_BUF,
-    kerberos_name_A,
-    kerberos_comment_A
-};
-
-void SECUR32_initKerberosSP(void)
-{
-    SecureProvider *provider = SECUR32_addProvider(&kerberosTableA, &kerberosTableW, NULL);
-    SECUR32_addPackages(provider, 1, &infoA, &infoW);
-}
diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c
index f5d5746171..b5bdaf2fdd 100644
--- a/dlls/secur32/secur32.c
+++ b/dlls/secur32/secur32.c
@@ -439,7 +439,7 @@ SecureProvider *SECUR32_addProvider(const SecurityFunctionTableA *fnTableA,
         ret->moduleName = SECUR32_strdupW(moduleName);
         ret->loaded = FALSE;
     }
-    
+
     LeaveCriticalSection(&cs);
     return ret;
 }
@@ -466,7 +466,7 @@ void SECUR32_addPackages(SecureProvider *provider, ULONG toAdd,
         packageTable->numPackages = 0;
         list_init(&packageTable->table);
     }
-        
+
     for (i = 0; i < toAdd; i++)
     {
         SecurePackage *package = HeapAlloc(GetProcessHeap(), 0, sizeof(SecurePackage));
@@ -488,7 +488,6 @@ void SECUR32_addPackages(SecureProvider *provider, ULONG toAdd,
 static void _tryLoadProvider(PWSTR moduleName)
 {
     HMODULE lib = LoadLibraryW(moduleName);
-
     if (lib)
     {
         INIT_SECURITY_INTERFACE_W pInitSecurityInterfaceW =
@@ -555,7 +554,7 @@ static const WCHAR securityProvidersKeyW[] = {
 static const WCHAR securityProvidersW[] = {
  'S','e','c','u','r','i','t','y','P','r','o','v','i','d','e','r','s',0
  };
- 
+
 static void SECUR32_initializeProviders(void)
 {
     HKEY key;
@@ -565,12 +564,9 @@ static void SECUR32_initializeProviders(void)
     /* First load built-in providers */
     SECUR32_initSchannelSP();
     SECUR32_initNTLMSP();
-    SECUR32_initKerberosSP();
-    /* Load the Negotiate provider last so apps stumble over the working NTLM
-     * provider first. Attempting to fix bug #16905 while keeping the
-     * application reported on wine-users on 2006-09-12 working. */
-    SECUR32_initNegotiateSP();
+
     /* Now load providers from registry */
+    TRACE("Loading providers from registry...\n");
     apiRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, securityProvidersKeyW, 0,
      KEY_READ, &key);
     if (apiRet == ERROR_SUCCESS)
@@ -604,6 +600,11 @@ static void SECUR32_initializeProviders(void)
         }
         RegCloseKey(key);
     }
+
+    /* Load the Negotiate provider last so apps stumble over the working NTLM
+     * provider first. Attempting to fix bug #16905 while keeping the
+     * application reported on wine-users on 2006-09-12 working. */
+    SECUR32_initNegotiateSP();
 }
 
 SecurePackage *SECUR32_findPackageW(PCWSTR packageName)
@@ -616,14 +617,14 @@ SecurePackage *SECUR32_findPackageW(PCWSTR packageName)
         LIST_FOR_EACH_ENTRY(ret, &packageTable->table, SecurePackage, entry)
         {
             matched = !lstrcmpiW(ret->infoW.Name, packageName);
-	    if (matched)
-		break;
+            if (matched)
+                break;
         }
-        
-	if (!matched)
-		return NULL;
 
-	if (ret->provider && !ret->provider->loaded)
+        if (!matched)
+            return NULL;
+
+        if (ret->provider && !ret->provider->loaded)
         {
             ret->provider->lib = LoadLibraryW(ret->provider->moduleName);
             if (ret->provider->lib)
@@ -913,7 +914,7 @@ SECURITY_STATUS WINAPI EnumerateSecurityPackagesA(PULONG pcPackages,
  *  nSize returns the number of characters written when lpNameBuffer is not
  *  NULL or the size of the buffer needed to hold the name when the buffer
  *  is too short or lpNameBuffer is NULL.
- * 
+ *
  *  It the buffer is too short, ERROR_INSUFFICIENT_BUFFER error will be set.
  */
 BOOLEAN WINAPI GetComputerObjectNameA(
diff --git a/include/config.h.in b/include/config.h.in
index 6596571b29..909d21e32d 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -288,6 +288,9 @@
 /* Define to 1 if you have the <gsm.h> header file. */
 #undef HAVE_GSM_H
 
+/* Define to 1 if you have the <gssapi/gssapi_krb5.h> header file. */
+#undef HAVE_GSSAPI_GSSAPI_KRB5_H
+
 /* Define if GTK 3 is installed */
 #undef HAVE_GTK3
 
@@ -1508,6 +1511,9 @@
 /* Define to the soname of the libgsm library. */
 #undef SONAME_LIBGSM
 
+/* Define to the soname of the libgssapi_krb5 library. */
+#undef SONAME_LIBGSSAPI_KRB5
+
 /* Define to the soname of the libgtk-3 library. */
 #undef SONAME_LIBGTK_3
 
diff --git a/loader/wine.inf.in b/loader/wine.inf.in
index 2b0668a837..09e29fa86d 100644
--- a/loader/wine.inf.in
+++ b/loader/wine.inf.in
@@ -743,6 +743,8 @@ HKLM,Software\Policies,,16
 HKLM,Software\Registered Applications,,16
 HKLM,System\CurrentControlSet\Control\hivelist,,16
 HKLM,System\CurrentControlSet\Control\Lsa\Kerberos,,16
+HKLM,System\CurrentControlSet\Control\Lsa\Kerberos,,16
+HKLM,System\CurrentControlSet\Control\SecurityProviders,"SecurityProviders",,"kerberos"
 HKLM,System\CurrentControlSet\Control\SecurityProviders\Schannel\Protocols\SSL 2.0\Client,"DisabledByDefault",0x10003,1
 HKLM,System\CurrentControlSet\Control\ServiceGroupOrder,"List",0x00010000,"TDI"
 HKLM,System\CurrentControlSet\Control\TimeZoneInformation,"StandardName",2,""
-- 
2.15.0
From e20dce3032d5f8fc3b52df0e495ddb788f1e9d8d Mon Sep 17 00:00:00 2001
From: George Popoff <ambulance@etersoft.ru>
Date: Wed, 11 Oct 2017 14:58:09 +0300
Subject: [8/9] fix negotiate ssp to choose between kerberberos and ntlm ssp
Content-Type: text/plain; charset=UTF-8
Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru>
---
 dlls/secur32/negotiate.c    | 213 +++++++++++++++++++++++++++++++++++++-------
 dlls/secur32/secur32_priv.h |  20 +----
 2 files changed, 181 insertions(+), 52 deletions(-)
diff --git a/dlls/secur32/negotiate.c b/dlls/secur32/negotiate.c
index bf16258fc2..8d635818c6 100644
--- a/dlls/secur32/negotiate.c
+++ b/dlls/secur32/negotiate.c
@@ -33,6 +33,14 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
 
+static SEC_WCHAR ntlm_name_W[] = {'N','T','L','M',0};
+static SEC_WCHAR kerberos_name_W[] = {'K', 'e', 'r', 'b', 'e', 'r', 'o', 's', 0};
+
+static SecurePackage *ntlm_package;
+static SecurePackage *kerberos_package;
+static SecureProvider *ntlm_provider;
+static SecureProvider *kerberos_provider;
+
 /***********************************************************************
  *              QueryCredentialsAttributesA
  */
@@ -61,22 +69,50 @@ static SECURITY_STATUS SEC_ENTRY nego_AcquireCredentialsHandleW(
     PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
     PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry )
 {
-    static SEC_WCHAR ntlmW[] = {'N','T','L','M',0};
     SECURITY_STATUS ret;
 
     TRACE("%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p\n",
           debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
           pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
 
-    FIXME("forwarding to NTLM\n");
-    ret = ntlm_AcquireCredentialsHandleW( pszPrincipal, ntlmW, fCredentialUse,
+    /* Assume this */
+    ret = SEC_E_INTERNAL_ERROR;
+
+    /* First we need to try kerberos */
+
+    if (kerberos_provider)
+    {
+        ret = kerberos_provider->fnTableW.
+                AcquireCredentialsHandleW(pszPrincipal, kerberos_name_W, fCredentialUse,
                                           pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument,
-                                          phCredential, ptsExpiry );
+                                          phCredential, ptsExpiry);
+    }
+
     if (ret == SEC_E_OK)
     {
+    /* FIXME: create KerberosCredentials */
         NtlmCredentials *cred = (NtlmCredentials *)phCredential->dwLower;
         cred->no_cached_credentials = (pAuthData == NULL);
+        return ret;
+    }
+
+    FIXME("Failed to AcquireCredentialHandle via Kerberos.\n");
+
+    /* Maybe ntlm? */
+    if (ntlm_provider)
+    {
+        ret = ntlm_provider->fnTableW.
+                AcquireCredentialsHandleW(pszPrincipal, ntlm_name_W, fCredentialUse,
+                                               pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument,
+                                               phCredential, ptsExpiry );
     }
+
+    if (ret == SEC_E_OK)
+    {
+        NtlmCredentials *cred = (NtlmCredentials *)phCredential->dwLower;
+        cred->no_cached_credentials = (pAuthData == NULL);
+    }
+
     return ret;
 }
 
@@ -173,15 +209,41 @@ static SECURITY_STATUS SEC_ENTRY nego_InitializeSecurityContextW(
     PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
     PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry )
 {
+    SECURITY_STATUS ret;
+
     TRACE("%p, %p, %s, 0x%08x, %u, %u, %p, %u, %p, %p, %p, %p\n",
           phCredential, phContext, debugstr_w(pszTargetName), fContextReq,
           Reserved1, TargetDataRep, pInput, Reserved1, phNewContext, pOutput,
           pfContextAttr, ptsExpiry);
 
-    return ntlm_InitializeSecurityContextW( phCredential, phContext, pszTargetName,
+    /* Assume this */
+    ret = SEC_E_INTERNAL_ERROR;
+
+    if (kerberos_provider)
+    {
+        ret = kerberos_provider->fnTableW.
+                InitializeSecurityContextW( phCredential, phContext, pszTargetName,
+                                            fContextReq, Reserved1, TargetDataRep,
+                                            pInput, Reserved2, phNewContext,
+                                            pOutput, pfContextAttr, ptsExpiry );
+    }
+
+    if (ret == SEC_E_OK || ret == SEC_I_CONTINUE_NEEDED)
+    {
+        return ret;
+    }
+
+
+    if (ntlm_provider)
+    {
+        ret = ntlm_provider->fnTableW.
+                InitializeSecurityContextW( phCredential, phContext, pszTargetName,
                                             fContextReq, Reserved1, TargetDataRep,
                                             pInput, Reserved2, phNewContext,
                                             pOutput, pfContextAttr, ptsExpiry );
+    }
+
+    return ret;
 }
 
 /***********************************************************************
@@ -227,9 +289,16 @@ static SECURITY_STATUS SEC_ENTRY nego_AcceptSecurityContext(
           pInput, fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr,
           ptsExpiry);
 
-    return ntlm_AcceptSecurityContext( phCredential, phContext, pInput,
+    /* FIXME: add kerberos invocation */
+    if (ntlm_provider)
+    {
+        return ntlm_provider->fnTableW.
+                AcceptSecurityContext( phCredential, phContext, pInput,
                                        fContextReq, TargetDataRep, phNewContext,
                                        pOutput, pfContextAttr, ptsExpiry );
+    }
+
+    return SEC_E_INTERNAL_ERROR;
 }
 
 /***********************************************************************
@@ -259,7 +328,13 @@ static SECURITY_STATUS SEC_ENTRY nego_DeleteSecurityContext(PCtxtHandle phContex
 {
     TRACE("%p\n", phContext);
 
-    return ntlm_DeleteSecurityContext( phContext );
+    if (phContext->dwUpper == KERBEROS_MAGIC && kerberos_provider)
+        return kerberos_provider->fnTableW.DeleteSecurityContext( phContext );
+
+    if (ntlm_provider)
+        return kerberos_provider->fnTableW.DeleteSecurityContext( phContext );
+
+    return SEC_E_INTERNAL_ERROR;
 }
 
 /***********************************************************************
@@ -288,28 +363,33 @@ static SECURITY_STATUS SEC_ENTRY nego_ApplyControlToken(PCtxtHandle phContext,
 static SECURITY_STATUS SEC_ENTRY nego_QueryContextAttributesW(
     PCtxtHandle phContext, ULONG ulAttribute, void *pBuffer)
 {
+    PSecPkgInfoW package_info_w;
+    SecureProvider *provider;
+
     TRACE("%p, %u, %p\n", phContext, ulAttribute, pBuffer);
 
-    switch (ulAttribute)
+    if (phContext->dwUpper == KERBEROS_MAGIC)
     {
-    case SECPKG_ATTR_SIZES:
+        package_info_w = &kerberos_package->infoW;
+        provider = kerberos_provider;
+    }
+    else
     {
-        SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)pBuffer;
-        sizes->cbMaxToken        = 2888;
-        sizes->cbMaxSignature    = 16;
-        sizes->cbSecurityTrailer = 16;
-        sizes->cbBlockSize       = 0;
-        return SEC_E_OK;
+        package_info_w = &ntlm_package->infoW;
+        provider = ntlm_provider;
     }
+
+    switch (ulAttribute)
+    {
     case SECPKG_ATTR_NEGOTIATION_INFO:
     {
         SecPkgContext_NegotiationInfoW *info = (SecPkgContext_NegotiationInfoW *)pBuffer;
-        info->PackageInfo      = ntlm_package_infoW;
+        info->PackageInfo      = package_info_w;
         info->NegotiationState = SECPKG_NEGOTIATION_COMPLETE;
         return SEC_E_OK;
     }
     default:
-        return ntlm_QueryContextAttributesW( phContext, ulAttribute, pBuffer );
+        return provider->fnTableW.QueryContextAttributesW( phContext, ulAttribute, pBuffer );
     }
 }
 
@@ -319,29 +399,59 @@ static SECURITY_STATUS SEC_ENTRY nego_QueryContextAttributesW(
 static SECURITY_STATUS SEC_ENTRY nego_QueryContextAttributesA(PCtxtHandle phContext,
  ULONG ulAttribute, void *pBuffer)
 {
+    SECURITY_STATUS ret;
+    PSecPkgInfoW package_info_W;
+    PSecPkgInfoA package_info_A;
+    SecureProvider *provider;
+    /* int cbSize; */
+
     TRACE("%p, %u, %p\n", phContext, ulAttribute, pBuffer);
 
-    switch (ulAttribute)
+    if (phContext->dwUpper == KERBEROS_MAGIC)
     {
-    case SECPKG_ATTR_SIZES:
+        package_info_W = &kerberos_package->infoW;
+        provider = kerberos_provider;
+    }
+    else
     {
-        SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)pBuffer;
-        sizes->cbMaxToken        = 2888;
-        sizes->cbMaxSignature    = 16;
-        sizes->cbSecurityTrailer = 16;
-        sizes->cbBlockSize       = 0;
-        return SEC_E_OK;
+        package_info_W = &ntlm_package->infoW;
+        provider = ntlm_provider;
     }
+
+    /* Convert package info from Widechar to ANSI */
+
+    /* package_info_A = (PSecPkgInfoA) HeapAlloc(GetProcessHeap(), 0, sizeof(*package_info_A));
+
+    cbSize = WideCharToMultiByte(CP_ACP, 0, package_info_W->Name, -1, NULL, 0, NULL, NULL);
+    package_info_A->Name = (SEC_CHAR *) HeapAlloc(GetProcessHeap(), 0, cbSize);
+    WideCharToMultiByte(CP_ACP, 0, package_info_W->Name, -1, &package_info_A->Name, cbSize, NULL, NULL);
+
+    cbSize = WideCharToMultiByte(CP_ACP, 0, package_info_W->Comment, -1, NULL, 0, NULL, NULL);
+    package_info_A->Comment = (SEC_CHAR *) HeapAlloc(GetProcessHeap(), 0, cbSize);
+    WideCharToMultiByte(CP_ACP, 0, package_info_W->Comment, -1, &package_info_A->Comment, cbSize, NULL, NULL);
+    */
+
+    switch (ulAttribute)
+    {
     case SECPKG_ATTR_NEGOTIATION_INFO:
     {
+        /* FIXME: Where we need to free the memory? */
+        /* SecurityPackage does not contains ANSI version of info */
+        /* Maybe we need to expand SecurityPackage structure? */
         SecPkgContext_NegotiationInfoA *info = (SecPkgContext_NegotiationInfoA *)pBuffer;
-        info->PackageInfo      = ntlm_package_infoA;
+        info->PackageInfo      = package_info_A;
         info->NegotiationState = SECPKG_NEGOTIATION_COMPLETE;
-        return SEC_E_OK;
+        return SEC_E_UNSUPPORTED_FUNCTION;
     }
     default:
-        return ntlm_QueryContextAttributesA( phContext, ulAttribute, pBuffer );
+        ret = provider->fnTableA.QueryContextAttributesA( phContext, ulAttribute, pBuffer );
     }
+
+    HeapFree(GetProcessHeap(), 0, package_info_A->Name);
+    HeapFree(GetProcessHeap(), 0, package_info_A->Comment);
+    HeapFree(GetProcessHeap(), 0, package_info_A);
+
+    return ret;
 }
 
 /***********************************************************************
@@ -390,7 +500,10 @@ static SECURITY_STATUS SEC_ENTRY nego_MakeSignature(PCtxtHandle phContext,
 {
     TRACE("%p, 0x%08x, %p, %u\n", phContext, fQOP, pMessage, MessageSeqNo);
 
-    return ntlm_MakeSignature( phContext, fQOP, pMessage, MessageSeqNo );
+    if (ntlm_provider)
+        return ntlm_provider->fnTableW.MakeSignature( phContext, fQOP, pMessage, MessageSeqNo );
+
+    return SEC_E_INTERNAL_ERROR;
 }
 
 /***********************************************************************
@@ -401,7 +514,10 @@ static SECURITY_STATUS SEC_ENTRY nego_VerifySignature(PCtxtHandle phContext,
 {
     TRACE("%p, %p, %u, %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
 
-    return ntlm_VerifySignature( phContext, pMessage, MessageSeqNo, pfQOP );
+    if (ntlm_provider)
+        return ntlm_provider->fnTableW.VerifySignature( phContext, pMessage, MessageSeqNo, pfQOP );
+
+    return SEC_E_INTERNAL_ERROR;
 }
 
 /***********************************************************************
@@ -411,7 +527,18 @@ static SECURITY_STATUS SEC_ENTRY nego_FreeCredentialsHandle(PCredHandle phCreden
 {
     TRACE("%p\n", phCredential);
 
-    return ntlm_FreeCredentialsHandle( phCredential );
+    if (phCredential->dwUpper == KERBEROS_MAGIC)
+    {
+        if (kerberos_provider)
+            return kerberos_provider->fnTableW.FreeCredentialsHandle( phCredential );
+    }
+    else
+    {
+        if (ntlm_provider)
+            return ntlm_provider->fnTableW.FreeCredentialsHandle( phCredential );
+    }
+
+    return SEC_E_INTERNAL_ERROR;
 }
 
 /***********************************************************************
@@ -422,7 +549,10 @@ static SECURITY_STATUS SEC_ENTRY nego_EncryptMessage(PCtxtHandle phContext,
 {
     TRACE("%p, 0x%08x, %p, %u\n", phContext, fQOP, pMessage, MessageSeqNo);
 
-    return ntlm_EncryptMessage( phContext, fQOP, pMessage, MessageSeqNo );
+    if (ntlm_provider)
+        return ntlm_provider->fnTableW.EncryptMessage( phContext, fQOP, pMessage, MessageSeqNo );
+
+    return SEC_E_INTERNAL_ERROR;
 }
 
 /***********************************************************************
@@ -433,7 +563,10 @@ static SECURITY_STATUS SEC_ENTRY nego_DecryptMessage(PCtxtHandle phContext,
 {
     TRACE("%p, %p, %u, %p\n", phContext, pMessage, MessageSeqNo, pfQOP);
 
-    return ntlm_DecryptMessage( phContext, pMessage, MessageSeqNo, pfQOP );
+    if (ntlm_provider)
+        return ntlm_provider->fnTableW.DecryptMessage( phContext, pMessage, MessageSeqNo, pfQOP );
+
+    return SEC_E_INTERNAL_ERROR;
 }
 
 static const SecurityFunctionTableA negoTableA = {
@@ -521,6 +654,7 @@ static CHAR negotiate_comment_A[] = "Microsoft Package Negotiator";
     SECPKG_FLAG_LOGON             | \
     SECPKG_FLAG_RESTRICTED_TOKENS )
 
+
 void SECUR32_initNegotiateSP(void)
 {
     SecureProvider *provider = SECUR32_addProvider(&negoTableA, &negoTableW, NULL);
@@ -530,4 +664,17 @@ void SECUR32_initNegotiateSP(void)
     const SecPkgInfoA infoA = {CAPS, 1, RPC_C_AUTHN_GSS_NEGOTIATE, NEGO_MAX_TOKEN,
                                nego_name_A, negotiate_comment_A};
     SECUR32_addPackages(provider, 1L, &infoA, &infoW);
+
+    ntlm_package = SECUR32_findPackageW(ntlm_name_W);
+    kerberos_package = SECUR32_findPackageW(kerberos_name_W);
+
+    if (ntlm_package)
+        ntlm_provider = ntlm_package->provider;
+    else
+        FIXME("SECUR32_findPackageW(ntlm) = null\n");
+
+    if (kerberos_package)
+        kerberos_provider = kerberos_package->provider;
+    else
+        FIXME("SECUR32_findPackageW(kerberos) = null\n");
 }
diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h
index 9a60d65788..212d19c753 100644
--- a/dlls/secur32/secur32_priv.h
+++ b/dlls/secur32/secur32_priv.h
@@ -136,7 +136,6 @@ PSTR  SECUR32_AllocMultiByteFromWide(PCWSTR str) DECLSPEC_HIDDEN;
 void SECUR32_initSchannelSP(void) DECLSPEC_HIDDEN;
 void SECUR32_initNegotiateSP(void) DECLSPEC_HIDDEN;
 void SECUR32_initNTLMSP(void) DECLSPEC_HIDDEN;
-void SECUR32_initKerberosSP(void) DECLSPEC_HIDDEN;
 
 /* Cleanup functions for built-in providers */
 void SECUR32_deinitSchannelSP(void) DECLSPEC_HIDDEN;
@@ -189,24 +188,7 @@ void SECUR32_arc4Cleanup(arc4_info *a4i) DECLSPEC_HIDDEN;
 #define NTLMSSP_NEGOTIATE_KEY_EXCHANGE              0x40000000
 #define NTLMSSP_NEGOTIATE_56                        0x80000000
 
-SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(SEC_WCHAR *, SEC_WCHAR *,
-    ULONG, PLUID, PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(PCredHandle, PCtxtHandle,
-    SEC_WCHAR *, ULONG fContextReq, ULONG, ULONG, PSecBufferDesc, ULONG, PCtxtHandle,
-    PSecBufferDesc, ULONG *, PTimeStamp) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(PCredHandle, PCtxtHandle, PSecBufferDesc,
-    ULONG, ULONG, PCtxtHandle, PSecBufferDesc, ULONG *, PTimeStamp) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle, ULONG, void *) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle, ULONG, void *) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle, ULONG, PSecBufferDesc, ULONG) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle, PSecBufferDesc, ULONG, PULONG) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle, ULONG, PSecBufferDesc, ULONG) DECLSPEC_HIDDEN;
-SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle, PSecBufferDesc, ULONG, PULONG) DECLSPEC_HIDDEN;
-
-SecPkgInfoW *ntlm_package_infoW DECLSPEC_HIDDEN;
-SecPkgInfoA *ntlm_package_infoA DECLSPEC_HIDDEN;
+#define KERBEROS_MAGIC ('K' << 24 | 'E' << 16 | 'R' << 8 | 'B')
 
 /* schannel internal interface */
 typedef struct schan_imp_session_opaque *schan_imp_session;
-- 
2.15.0
From cae0f12b60023ecc15e0d517f9e16ab4f89b7154 Mon Sep 17 00:00:00 2001
From: Vitaly Lipatov <lav@etersoft.ru>
Date: Wed, 8 Nov 2017 01:41:24 +0300
Subject: [9/9] add-wincred.h-include: add patch for wineboot (needed only in
 staging)
Content-Type: text/plain; charset=UTF-8
---
 programs/wineboot/wineboot.c | 1 +
 1 file changed, 1 insertion(+)
diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c
index c2c5792fc6..26519c1000 100644
--- a/programs/wineboot/wineboot.c
+++ b/programs/wineboot/wineboot.c
@@ -81,6 +81,7 @@
 #include <shobjidl.h>
 #include <shlwapi.h>
 #include <shellapi.h>
+#include <wincred.h>
 #include <ntsecapi.h>
 #include <wininet.h>
 #include "resource.h"
-- 
2.15.0
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin