>From 0a8c9850fec58760e7491f7382d6d4da01adb51a Mon Sep 17 00:00:00 2001
From: George Popoff <ambulance@etersoft.ru>
Date: Wed, 11 Oct 2017 14:58:09 +0300
Subject: [PATCH 8/8] fix negotiate ssp to choose between kerberberos and ntlm
 ssp

---
 wine/dlls/secur32/negotiate.c    | 213 +++++++++++++++++++++++++++++++++------
 wine/dlls/secur32/secur32_priv.h |  20 +---
 2 files changed, 181 insertions(+), 52 deletions(-)

diff --git a/wine/dlls/secur32/negotiate.c b/wine/dlls/secur32/negotiate.c
index bf16258..8d63581 100644
--- a/wine/dlls/secur32/negotiate.c
+++ b/wine/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/wine/dlls/secur32/secur32_priv.h b/wine/dlls/secur32/secur32_priv.h
index 9a60d65..212d19c 100644
--- a/wine/dlls/secur32/secur32_priv.h
+++ b/wine/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.10.4

