[01/10] secur32: Implement AcquireCredentialsHandle for Kerberos.

Hans Leidekker hans at codeweavers.com
Mon Oct 23 04:09:16 CDT 2017


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 configure.ac                |  18 +++
 dlls/secur32/Makefile.in    |   2 +-
 dlls/secur32/kerberos.c     | 276 +++++++++++++++++++++++++++++++++++++++++---
 dlls/secur32/secur32.c      |   1 +
 dlls/secur32/secur32_priv.h |   1 +
 5 files changed, 282 insertions(+), 16 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3f5f89f935..48b2c7fdb1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -53,6 +53,7 @@ AC_ARG_WITH(glu,       AS_HELP_STRING([--without-glu],[do not use the GLU librar
 AC_ARG_WITH(gnutls,    AS_HELP_STRING([--without-gnutls],[do not use GnuTLS (schannel support)]))
 AC_ARG_WITH(gsm,       AS_HELP_STRING([--without-gsm],[do not use libgsm (GSM 06.10 codec support)]),
             [if test "x$withval" = "xno"; then ac_cv_header_gsm_h=no; ac_cv_header_gsm_gsm_h=no; fi])
+AC_ARG_WITH(gssapi,    AS_HELP_STRING([--without-gssapi],[do not use GSSAPI (Kerberos SSP support)]))
 AC_ARG_WITH(gstreamer, AS_HELP_STRING([--without-gstreamer],[do not use GStreamer (codecs support)]))
 AC_ARG_WITH(hal,       AS_HELP_STRING([--without-hal],[do not use HAL (dynamic device support)]))
 AC_ARG_WITH(jpeg,      AS_HELP_STRING([--without-jpeg],[do not use JPEG]))
@@ -1643,6 +1644,23 @@ 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 gssapi ****
+if test "x$with_gssapi" != "xno"
+then
+    WINE_PACKAGE_FLAGS(GSSAPI,[krb5-gssapi],,
+                       [`${KRB5_CONFIG:-krb5-config} --cflags gssapi 2>/dev/null`],
+                       [`${KRB5_CONFIG:-krb5-config} --libs gssapi 2>/dev/null`],
+        [AC_CHECK_HEADERS([gssapi/gssapi.h gssapi/gssapi_ext.h])
+        if test "$ac_cv_header_gssapi_gssapi_h" = "yes" -a "$ac_cv_header_gssapi_gssapi_ext_h" = "yes"
+        then
+            WINE_CHECK_SONAME(gssapi_krb5, gss_init_sec_context,,[GSSAPI_LIBS=""],[$GSSAPI_LIBS])
+        else
+            GSSAPI_CFLAGS=""
+        fi])
+fi
+WINE_WARNING_WITH(gssapi,[test "x$ac_cv_lib_soname_gssapi_krb5" = "x"],
+                 [libgssapi_krb5 ${notice_platform}development files not found (or too old), no Kerberos SSP support.])
+
 dnl **** Check for libjpeg ****
 if test "x$with_jpeg" != "xno"
 then
diff --git a/dlls/secur32/Makefile.in b/dlls/secur32/Makefile.in
index 6548521f53..16870b4c86 100644
--- a/dlls/secur32/Makefile.in
+++ b/dlls/secur32/Makefile.in
@@ -2,7 +2,7 @@ MODULE    = secur32.dll
 IMPORTLIB = secur32
 IMPORTS   = netapi32 advapi32
 DELAYIMPORTS = crypt32
-EXTRAINCL = $(GNUTLS_CFLAGS)
+EXTRAINCL = $(GNUTLS_CFLAGS) $(GSSAPI_CFLAGS)
 EXTRALIBS = $(SECURITY_LIBS)
 
 C_SRCS = \
diff --git a/dlls/secur32/kerberos.c b/dlls/secur32/kerberos.c
index 753e9748d2..67404b72f2 100644
--- a/dlls/secur32/kerberos.c
+++ b/dlls/secur32/kerberos.c
@@ -1,6 +1,8 @@
 /*
  * Copyright 2005, 2006 Kai Blin
+ * Copyright 2008 Robert Shearman for CodeWeavers
  * Copyright 2016 Jacek Caban for CodeWeavers
+ * Copyright 2017 Hans Leidekker for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,9 +19,15 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
-#include <assert.h>
+#include "config.h"
+#include "wine/port.h"
+
 #include <stdarg.h>
 #include <stdio.h>
+#ifdef SONAME_LIBGSSAPI_KRB5
+# include <gssapi/gssapi.h>
+# include <gssapi/gssapi_ext.h>
+#endif
 
 #include "windef.h"
 #include "winbase.h"
@@ -27,12 +35,119 @@
 #include "sspi.h"
 
 #include "secur32_priv.h"
-
 #include "wine/debug.h"
+#include "wine/library.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
 
-#define KERBEROS_MAX_BUF 12000
+#ifdef SONAME_LIBGSSAPI_KRB5
+
+WINE_DECLARE_DEBUG_CHANNEL(winediag);
+static void *libgssapi_krb5_handle;
+
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f
+MAKE_FUNCPTR(gss_acquire_cred);
+MAKE_FUNCPTR(gss_import_name);
+MAKE_FUNCPTR(gss_release_name);
+#undef MAKE_FUNCPTR
+
+static BOOL load_gssapi_krb5(void)
+{
+    if (!(libgssapi_krb5_handle = wine_dlopen( SONAME_LIBGSSAPI_KRB5, RTLD_NOW, NULL, 0 )))
+    {
+        ERR_(winediag)( "Failed to load libgssapi_krb5, Kerberos SSP support will not be available.\n" );
+        return FALSE;
+    }
+
+#define LOAD_FUNCPTR(f) \
+    if (!(p##f = wine_dlsym( libgssapi_krb5_handle, #f, NULL, 0 ))) \
+    { \
+        ERR( "Failed to load %s\n", #f ); \
+        goto fail; \
+    }
+
+    LOAD_FUNCPTR(gss_acquire_cred)
+    LOAD_FUNCPTR(gss_import_name)
+    LOAD_FUNCPTR(gss_release_name)
+#undef LOAD_FUNCPTR
+
+    return TRUE;
+
+fail:
+    wine_dlclose( libgssapi_krb5_handle, NULL, 0 );
+    libgssapi_krb5_handle = NULL;
+    return FALSE;
+}
+
+static void unload_gssapi_krb5(void)
+{
+    wine_dlclose( libgssapi_krb5_handle, NULL, 0 );
+    libgssapi_krb5_handle = NULL;
+}
+
+static inline void credhandle_gss_to_sspi( gss_cred_id_t handle, gss_name_t principal, CredHandle *cred )
+{
+    cred->dwLower = (DWORD_PTR)handle;
+    cred->dwUpper = (DWORD_PTR)principal;
+}
+
+static SECURITY_STATUS status_gss_to_sspi( 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_INVALID_TOKEN;
+    case GSS_S_DEFECTIVE_CREDENTIAL: return SEC_E_NO_CREDENTIALS;
+    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%08x to SECURITY_STATUS\n", status );
+        return SEC_E_INTERNAL_ERROR;
+    }
+}
+
+static void expirytime_gss_to_sspi( OM_uint32 expirytime, TimeStamp *timestamp )
+{
+    SYSTEMTIME time;
+    FILETIME filetime;
+    ULARGE_INTEGER tmp;
+
+    GetLocalTime( &time );
+    SystemTimeToFileTime( &time, &filetime );
+    tmp.QuadPart = ((ULONGLONG)filetime.dwLowDateTime | (ULONGLONG)filetime.dwHighDateTime << 32) + expirytime;
+    timestamp->LowPart  = tmp.QuadPart;
+    timestamp->HighPart = tmp.QuadPart >> 32;
+}
+
+static SECURITY_STATUS name_sspi_to_gss( const SEC_WCHAR *name_str, gss_name_t *name )
+{
+    OM_uint32 ret, minor_status;
+    gss_OID type = GSS_C_NO_OID; /* FIXME: detect the appropriate value for this ourselves? */
+    gss_buffer_desc buf;
+
+    buf.length = WideCharToMultiByte( CP_UNIXCP, 0, name_str, -1, NULL, 0, NULL, NULL );
+    if (!(buf.value = HeapAlloc( GetProcessHeap(), 0, buf.length ))) return SEC_E_INSUFFICIENT_MEMORY;
+    WideCharToMultiByte( CP_UNIXCP, 0, name_str, -1, buf.value, buf.length, NULL, NULL );
+    buf.length--;
+
+    ret = pgss_import_name( &minor_status, &buf, type, name );
+    TRACE( "gss_import_name returned %08x minor status %08x\n", ret, minor_status );
+
+    HeapFree( GetProcessHeap(), 0, buf.value );
+    return status_gss_to_sspi( ret );
+}
+#endif
 
 /***********************************************************************
  *              QueryCredentialsAttributesA
@@ -55,23 +170,135 @@ static SECURITY_STATUS SEC_ENTRY kerberos_QueryCredentialsAttributesW(CredHandle
 /***********************************************************************
  *              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)
+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;
+#ifdef SONAME_LIBGSSAPI_KRB5
+    OM_uint32 ret, minor_status, expiry_time;
+    gss_name_t principal = GSS_C_NO_NAME;
+    gss_cred_usage_t cred_usage;
+    gss_cred_id_t cred_handle;
+
+    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)
+    {
+        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:
+            return SEC_E_UNKNOWN_CREDENTIALS;
+    }
+
+    if (pszPrincipal && ((ret = name_sspi_to_gss( pszPrincipal, &principal )) != SEC_E_OK)) return ret;
+
+    ret = pgss_acquire_cred( &minor_status, principal, GSS_C_INDEFINITE, GSS_C_NULL_OID_SET, cred_usage,
+                              &cred_handle, NULL, &expiry_time );
+    TRACE( "gss_acquire_cred returned %08x minor status %08x\n", ret, minor_status );
+    if (ret == GSS_S_COMPLETE || ret == GSS_S_CONTINUE_NEEDED)
+    {
+        credhandle_gss_to_sspi( cred_handle, principal, phCredential );
+        expirytime_gss_to_sspi( expiry_time, ptsExpiry );
+    }
+    else if (principal != GSS_C_NO_NAME) pgss_release_name( &minor_status, &principal );
+
+    return status_gss_to_sspi( ret );
+#else
+    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 );
+    FIXME( "Wine was built without Kerberos support.\n" );
+    return SEC_E_UNSUPPORTED_FUNCTION;
+#endif
 }
 
 /***********************************************************************
  *              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)
+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;
+    SECURITY_STATUS ret = SEC_E_INSUFFICIENT_MEMORY;
+    int len_user = 0, len_domain = 0, len_passwd = 0;
+    SEC_WCHAR *principal = NULL, *package = NULL, *user = NULL, *domain = NULL, *passwd = NULL;
+    SEC_WINNT_AUTH_IDENTITY_W *authdata = NULL;
+    SEC_WINNT_AUTH_IDENTITY_A *id = 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 (pszPrincipal)
+    {
+        int len = MultiByteToWideChar( CP_ACP, 0, pszPrincipal, -1, NULL, 0 );
+        if (!(principal = HeapAlloc( GetProcessHeap(), 0, len * sizeof(SEC_WCHAR) ))) goto done;
+        MultiByteToWideChar( CP_ACP, 0, pszPrincipal, -1, principal, len );
+    }
+    if (pszPackage)
+    {
+        int len = MultiByteToWideChar( CP_ACP, 0, pszPackage, -1, NULL, 0 );
+        if (!(package = HeapAlloc( GetProcessHeap(), 0, len * sizeof(SEC_WCHAR) ))) goto done;
+        MultiByteToWideChar( CP_ACP, 0, pszPackage, -1, package, len );
+    }
+    if (pAuthData)
+    {
+        id = (PSEC_WINNT_AUTH_IDENTITY_A)pAuthData;
+        if (id->Flags == SEC_WINNT_AUTH_IDENTITY_ANSI)
+        {
+            if (!(authdata = HeapAlloc( GetProcessHeap(), 0, sizeof(SEC_WINNT_AUTH_IDENTITY_W) ))) goto done;
+            if (id->UserLength)
+            {
+                len_user = MultiByteToWideChar( CP_ACP, 0, (char *)id->User, id->UserLength, NULL, 0 );
+                if (!(user = HeapAlloc( GetProcessHeap(), 0, len_user * sizeof(SEC_WCHAR) ))) goto done;
+                MultiByteToWideChar( CP_ACP, 0, (char *)id->User, id->UserLength, user, len_user );
+            }
+            if (id->DomainLength)
+            {
+                len_domain = MultiByteToWideChar( CP_ACP, 0, (char *)id->Domain, id->DomainLength, NULL, 0 );
+                if (!(domain = HeapAlloc( GetProcessHeap(), 0, len_domain * sizeof(SEC_WCHAR) ))) goto done;
+                MultiByteToWideChar( CP_ACP, 0, (char *)id->Domain, id->DomainLength, domain, len_domain );
+            }
+            if (id->PasswordLength)
+            {
+                len_passwd = MultiByteToWideChar( CP_ACP, 0, (char *)id->Password, id->PasswordLength, NULL, 0 );
+                if (!(passwd = HeapAlloc( GetProcessHeap(), 0, len_passwd * sizeof(SEC_WCHAR) ))) goto done;
+                MultiByteToWideChar( CP_ACP, 0, (char *)id->Password, id->PasswordLength, passwd, len_passwd );
+            }
+            authdata->Flags          = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+            authdata->User           = user;
+            authdata->UserLength     = len_user;
+            authdata->Domain         = domain;
+            authdata->DomainLength   = len_domain;
+            authdata->Password       = passwd;
+            authdata->PasswordLength = len_passwd;
+        }
+        else authdata = (PSEC_WINNT_AUTH_IDENTITY_W)id;
+    }
+
+    ret = kerberos_AcquireCredentialsHandleW( principal, package, fCredentialUse, pLogonID, authdata, pGetKeyFn,
+                                              pGetKeyArgument, phCredential, ptsExpiry );
+
+done:
+    if (authdata != (SEC_WINNT_AUTH_IDENTITY_W *)id) HeapFree( GetProcessHeap(), 0, authdata );
+    HeapFree( GetProcessHeap(), 0, package );
+    HeapFree( GetProcessHeap(), 0, principal );
+    HeapFree( GetProcessHeap(), 0, user );
+    HeapFree( GetProcessHeap(), 0, domain );
+    HeapFree( GetProcessHeap(), 0, passwd );
+    return ret;
 }
 
 /***********************************************************************
@@ -270,6 +497,8 @@ static const SecurityFunctionTableW kerberosTableW = {
     NULL,   /* SetContextAttributesW */
 };
 
+#define KERBEROS_MAX_BUF 12000
+
 #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;
@@ -316,8 +545,25 @@ static const SecPkgInfoA infoA = {
     kerberos_comment_A
 };
 
+void SECUR32_deinitKerberosSP(void)
+{
+#ifdef SONAME_LIBGSSAPI_KRB5
+    unload_gssapi_krb5();
+#endif
+}
+
 void SECUR32_initKerberosSP(void)
 {
-    SecureProvider *provider = SECUR32_addProvider(&kerberosTableA, &kerberosTableW, NULL);
-    SECUR32_addPackages(provider, 1, &infoA, &infoW);
+    SecureProvider *provider;
+
+#ifdef SONAME_LIBGSSAPI_KRB5
+    if (!load_gssapi_krb5()) return;
+#endif
+    if (!(provider = SECUR32_addProvider( &kerberosTableA, &kerberosTableW, NULL )))
+    {
+        ERR( "Failed to add Kerberos provider.\n" );
+        SECUR32_deinitKerberosSP();
+        return;
+    }
+    SECUR32_addPackages( provider, 1, &infoA, &infoW );
 }
diff --git a/dlls/secur32/secur32.c b/dlls/secur32/secur32.c
index 1e12f3c543..5b65cf791d 100644
--- a/dlls/secur32/secur32.c
+++ b/dlls/secur32/secur32.c
@@ -677,6 +677,7 @@ static void SECUR32_freeProviders(void)
     EnterCriticalSection(&cs);
 
     SECUR32_deinitSchannelSP();
+    SECUR32_deinitKerberosSP();
 
     if (packageTable)
     {
diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h
index 9a60d65788..7e16b50404 100644
--- a/dlls/secur32/secur32_priv.h
+++ b/dlls/secur32/secur32_priv.h
@@ -139,6 +139,7 @@ void SECUR32_initNTLMSP(void) DECLSPEC_HIDDEN;
 void SECUR32_initKerberosSP(void) DECLSPEC_HIDDEN;
 
 /* Cleanup functions for built-in providers */
+void SECUR32_deinitKerberosSP(void) DECLSPEC_HIDDEN;
 void SECUR32_deinitSchannelSP(void) DECLSPEC_HIDDEN;
 
 /* Functions from dispatcher.c used elsewhere in the code */
-- 
2.11.0




More information about the wine-patches mailing list