[PATCH] secur32: Add support for client certificate authentication.

Hans Leidekker hans at codeweavers.com
Thu Feb 7 03:52:11 CST 2019


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/secur32/schannel.c        |  92 ++++++-------
 dlls/secur32/schannel_gnutls.c | 303 ++++++++++++++++++++++++++++++++++++++++-
 dlls/secur32/schannel_macosx.c |   4 +-
 dlls/secur32/secur32_priv.h    |   2 +-
 4 files changed, 345 insertions(+), 56 deletions(-)

diff --git a/dlls/secur32/schannel.c b/dlls/secur32/schannel.c
index 792d7804b3..8063fd4d6b 100644
--- a/dlls/secur32/schannel.c
+++ b/dlls/secur32/schannel.c
@@ -338,26 +338,26 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesW(
     return ret;
 }
 
-static SECURITY_STATUS schan_CheckCreds(const SCHANNEL_CRED *schanCred)
+static SECURITY_STATUS get_cert(const SCHANNEL_CRED *cred, CERT_CONTEXT const **cert)
 {
-    SECURITY_STATUS st;
+    SECURITY_STATUS status;
     DWORD i;
 
-    TRACE("dwVersion = %d\n", schanCred->dwVersion);
-    TRACE("cCreds = %d\n", schanCred->cCreds);
-    TRACE("hRootStore = %p\n", schanCred->hRootStore);
-    TRACE("cMappers = %d\n", schanCred->cMappers);
-    TRACE("cSupportedAlgs = %d:\n", schanCred->cSupportedAlgs);
-    for (i = 0; i < schanCred->cSupportedAlgs; i++)
-        TRACE("%08x\n", schanCred->palgSupportedAlgs[i]);
-    TRACE("grbitEnabledProtocols = %08x\n", schanCred->grbitEnabledProtocols);
-    TRACE("dwMinimumCipherStrength = %d\n", schanCred->dwMinimumCipherStrength);
-    TRACE("dwMaximumCipherStrength = %d\n", schanCred->dwMaximumCipherStrength);
-    TRACE("dwSessionLifespan = %d\n", schanCred->dwSessionLifespan);
-    TRACE("dwFlags = %08x\n", schanCred->dwFlags);
-    TRACE("dwCredFormat = %d\n", schanCred->dwCredFormat);
-
-    switch (schanCred->dwVersion)
+    TRACE("dwVersion = %u\n", cred->dwVersion);
+    TRACE("cCreds = %u\n", cred->cCreds);
+    TRACE("paCred = %p\n", cred->paCred);
+    TRACE("hRootStore = %p\n", cred->hRootStore);
+    TRACE("cMappers = %u\n", cred->cMappers);
+    TRACE("cSupportedAlgs = %u:\n", cred->cSupportedAlgs);
+    for (i = 0; i < cred->cSupportedAlgs; i++) TRACE("%08x\n", cred->palgSupportedAlgs[i]);
+    TRACE("grbitEnabledProtocols = %08x\n", cred->grbitEnabledProtocols);
+    TRACE("dwMinimumCipherStrength = %u\n", cred->dwMinimumCipherStrength);
+    TRACE("dwMaximumCipherStrength = %u\n", cred->dwMaximumCipherStrength);
+    TRACE("dwSessionLifespan = %u\n", cred->dwSessionLifespan);
+    TRACE("dwFlags = %08x\n", cred->dwFlags);
+    TRACE("dwCredFormat = %u\n", cred->dwCredFormat);
+
+    switch (cred->dwVersion)
     {
     case SCH_CRED_V3:
     case SCHANNEL_CRED_VERSION:
@@ -366,29 +366,24 @@ static SECURITY_STATUS schan_CheckCreds(const SCHANNEL_CRED *schanCred)
         return SEC_E_INTERNAL_ERROR;
     }
 
-    if (schanCred->cCreds == 0)
-        st = SEC_E_NO_CREDENTIALS;
-    else if (schanCred->cCreds > 1)
-        st = SEC_E_UNKNOWN_CREDENTIALS;
+    if (!cred->cCreds) status = SEC_E_NO_CREDENTIALS;
+    else if (cred->cCreds > 1) status = SEC_E_UNKNOWN_CREDENTIALS;
     else
     {
-        DWORD keySpec;
-        HCRYPTPROV csp;
-        BOOL ret, freeCSP;
-
-        ret = CryptAcquireCertificatePrivateKey(schanCred->paCred[0],
-         0, /* FIXME: what flags to use? */ NULL,
-         &csp, &keySpec, &freeCSP);
-        if (ret)
+        DWORD spec;
+        HCRYPTPROV prov;
+        BOOL free;
+
+        if (CryptAcquireCertificatePrivateKey(cred->paCred[0], CRYPT_ACQUIRE_CACHE_FLAG, NULL, &prov, &spec, &free))
         {
-            st = SEC_E_OK;
-            if (freeCSP)
-                CryptReleaseContext(csp, 0);
+            if (free) CryptReleaseContext(prov, 0);
+            *cert = cred->paCred[0];
+            status = SEC_E_OK;
         }
-        else
-            st = SEC_E_UNKNOWN_CREDENTIALS;
+        else status = SEC_E_UNKNOWN_CREDENTIALS;
     }
-    return st;
+
+    return status;
 }
 
 static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schanCred,
@@ -397,17 +392,18 @@ static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schan
     struct schan_credentials *creds;
     unsigned enabled_protocols;
     ULONG_PTR handle;
-    SECURITY_STATUS st = SEC_E_OK;
+    SECURITY_STATUS status = SEC_E_OK;
+    const CERT_CONTEXT *cert = NULL;
 
     TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
 
     if (schanCred)
     {
-        st = schan_CheckCreds(schanCred);
-        if (st != SEC_E_OK && st != SEC_E_NO_CREDENTIALS)
-            return st;
+        status = get_cert(schanCred, &cert);
+        if (status != SEC_E_OK && status != SEC_E_NO_CREDENTIALS)
+            return status;
 
-        st = SEC_E_OK;
+        status = SEC_E_OK;
     }
 
     read_config();
@@ -420,9 +416,6 @@ static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schan
         return SEC_E_NO_AUTHENTICATING_AUTHORITY;
     }
 
-    /* For now, the only thing I'm interested in is the direction of the
-     * connection, so just store it.
-     */
     creds = heap_alloc(sizeof(*creds));
     if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
 
@@ -430,7 +423,7 @@ static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schan
     if (handle == SCHAN_INVALID_HANDLE) goto fail;
 
     creds->credential_use = SECPKG_CRED_OUTBOUND;
-    if (!schan_imp_allocate_certificate_credentials(creds))
+    if (!schan_imp_allocate_certificate_credentials(creds, cert))
     {
         schan_free_handle(handle, SCHAN_HANDLE_CRED);
         goto fail;
@@ -447,7 +440,7 @@ static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schan
         ptsExpiry->HighPart = 0;
     }
 
-    return st;
+    return status;
 
 fail:
     heap_free(creds);
@@ -457,14 +450,15 @@ fail:
 static SECURITY_STATUS schan_AcquireServerCredentials(const SCHANNEL_CRED *schanCred,
  PCredHandle phCredential, PTimeStamp ptsExpiry)
 {
-    SECURITY_STATUS st;
+    SECURITY_STATUS status;
+    const CERT_CONTEXT *cert = NULL;
 
     TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
 
     if (!schanCred) return SEC_E_NO_CREDENTIALS;
 
-    st = schan_CheckCreds(schanCred);
-    if (st == SEC_E_OK)
+    status = get_cert(schanCred, &cert);
+    if (status == SEC_E_OK)
     {
         ULONG_PTR handle;
         struct schan_credentials *creds;
@@ -485,7 +479,7 @@ static SECURITY_STATUS schan_AcquireServerCredentials(const SCHANNEL_CRED *schan
 
         /* FIXME: get expiry from cert */
     }
-    return st;
+    return status;
 }
 
 static SECURITY_STATUS schan_AcquireCredentialsHandle(ULONG fCredentialUse,
diff --git a/dlls/secur32/schannel_gnutls.c b/dlls/secur32/schannel_gnutls.c
index cb90d1c40d..ddb10aca8c 100644
--- a/dlls/secur32/schannel_gnutls.c
+++ b/dlls/secur32/schannel_gnutls.c
@@ -24,18 +24,24 @@
 
 #include <stdarg.h>
 #include <stdio.h>
+#include <assert.h>
 #ifdef SONAME_LIBGNUTLS
 #include <gnutls/gnutls.h>
 #include <gnutls/crypto.h>
+#include <gnutls/abstract.h>
 #endif
 
 #include "windef.h"
 #include "winbase.h"
 #include "sspi.h"
 #include "schannel.h"
+#include "lmcons.h"
+#include "winreg.h"
 #include "secur32_priv.h"
+
 #include "wine/debug.h"
 #include "wine/library.h"
+#include "wine/unicode.h"
 
 #if defined(SONAME_LIBGNUTLS) && !defined(HAVE_SECURITY_SECURITY_H)
 
@@ -43,7 +49,10 @@ WINE_DEFAULT_DEBUG_CHANNEL(secur32);
 WINE_DECLARE_DEBUG_CHANNEL(winediag);
 
 /* Not present in gnutls version < 2.9.10. */
-static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t algorithm);
+static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t);
+
+/* Not present in gnutls version < 3.4.0. */
+static int (*pgnutls_privkey_export_x509)(gnutls_privkey_t, gnutls_x509_privkey_t *);
 
 static void *libgnutls_handle;
 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
@@ -52,6 +61,7 @@ MAKE_FUNCPTR(gnutls_alert_get_name);
 MAKE_FUNCPTR(gnutls_certificate_allocate_credentials);
 MAKE_FUNCPTR(gnutls_certificate_free_credentials);
 MAKE_FUNCPTR(gnutls_certificate_get_peers);
+MAKE_FUNCPTR(gnutls_certificate_set_x509_key);
 MAKE_FUNCPTR(gnutls_cipher_get);
 MAKE_FUNCPTR(gnutls_cipher_get_key_size);
 MAKE_FUNCPTR(gnutls_credentials_set);
@@ -68,6 +78,9 @@ MAKE_FUNCPTR(gnutls_mac_get_key_size);
 MAKE_FUNCPTR(gnutls_perror);
 MAKE_FUNCPTR(gnutls_protocol_get_version);
 MAKE_FUNCPTR(gnutls_priority_set_direct);
+MAKE_FUNCPTR(gnutls_privkey_deinit);
+MAKE_FUNCPTR(gnutls_privkey_import_rsa_raw);
+MAKE_FUNCPTR(gnutls_privkey_init);
 MAKE_FUNCPTR(gnutls_record_get_max_size);
 MAKE_FUNCPTR(gnutls_record_recv);
 MAKE_FUNCPTR(gnutls_record_send);
@@ -77,6 +90,10 @@ MAKE_FUNCPTR(gnutls_transport_set_errno);
 MAKE_FUNCPTR(gnutls_transport_set_ptr);
 MAKE_FUNCPTR(gnutls_transport_set_pull_function);
 MAKE_FUNCPTR(gnutls_transport_set_push_function);
+MAKE_FUNCPTR(gnutls_x509_crt_deinit);
+MAKE_FUNCPTR(gnutls_x509_crt_import);
+MAKE_FUNCPTR(gnutls_x509_crt_init);
+MAKE_FUNCPTR(gnutls_x509_privkey_deinit);
 #undef MAKE_FUNCPTR
 
 #if GNUTLS_VERSION_MAJOR < 3
@@ -115,6 +132,12 @@ static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
     }
 }
 
+static int compat_gnutls_privkey_export_x509(gnutls_privkey_t privkey, gnutls_x509_privkey_t *key)
+{
+    FIXME("\n");
+    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
+}
+
 static ssize_t schan_pull_adapter(gnutls_transport_ptr_t transport,
                                       void *buff, size_t buff_len)
 {
@@ -556,12 +579,271 @@ again:
     return SEC_E_OK;
 }
 
-BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c)
+static WCHAR *get_key_container_path(const CERT_CONTEXT *ctx)
+{
+    static const WCHAR rsabaseW[] =
+        {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','C','r','y','p','t','o','\\','R','S','A','\\',0};
+    DWORD size;
+    CERT_KEY_CONTEXT keyctx;
+    CRYPT_KEY_PROV_INFO *prov;
+    WCHAR username[UNLEN + 1], *ret = NULL;
+    DWORD len = ARRAY_SIZE(username);
+
+    size = sizeof(keyctx);
+    if (CertGetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, &keyctx, &size))
+    {
+        char *str;
+        if (!CryptGetProvParam(keyctx.hCryptProv, PP_CONTAINER, NULL, &size, 0)) return NULL;
+        if (!(str = heap_alloc(size))) return NULL;
+        if (!CryptGetProvParam(keyctx.hCryptProv, PP_CONTAINER, (BYTE *)str, &size, 0)) return NULL;
+
+        len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
+        if (!(ret = heap_alloc(sizeof(rsabaseW) + len * sizeof(WCHAR))))
+        {
+            heap_free(str);
+            return NULL;
+        }
+        strcpyW(ret, rsabaseW);
+        MultiByteToWideChar(CP_ACP, 0, str, -1, ret + strlenW(ret), len);
+        heap_free(str);
+    }
+    else
+    {
+        size = 0;
+        if (!CertGetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) return NULL;
+        if (!(prov = heap_alloc(size))) return NULL;
+        if (!CertGetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, prov, &size))
+        {
+            heap_free(prov);
+            return NULL;
+        }
+        if (!(ret = heap_alloc(sizeof(rsabaseW) + strlenW(prov->pwszContainerName) * sizeof(WCHAR))))
+        {
+            heap_free(prov);
+            return NULL;
+        }
+        strcpyW(ret, rsabaseW);
+        strcatW(ret, prov->pwszContainerName);
+        heap_free(prov);
+    }
+
+    if (!ret && GetUserNameW(username, &len) && (ret = heap_alloc(sizeof(rsabaseW) + len * sizeof(WCHAR))))
+    {
+        strcpyW(ret, rsabaseW);
+        strcatW(ret, username);
+    }
+
+    return ret;
+}
+
+#define MAX_LEAD_BYTES 8
+static BYTE *get_key_blob(const CERT_CONTEXT *ctx, ULONG *size)
+{
+    static const WCHAR keyexchangeW[] =
+        {'K','e','y','E','x','c','h','a','n','g','e','K','e','y','P','a','i','r',0};
+    static const WCHAR signatureW[] =
+        {'S','i','g','n','a','t','u','r','e','K','e','y','P','a','i','r',0};
+    BYTE *buf, *ret = NULL;
+    DATA_BLOB blob_in, blob_out;
+    DWORD spec = 0, type, len;
+    WCHAR *path;
+    HKEY hkey;
+
+    if (!(path = get_key_container_path(ctx))) return NULL;
+    if (RegOpenKeyExW(HKEY_CURRENT_USER, path, 0, KEY_READ, &hkey))
+    {
+        heap_free(path);
+        return NULL;
+    }
+
+    if (!RegQueryValueExW(hkey, keyexchangeW, 0, &type, NULL, &len)) spec = AT_KEYEXCHANGE;
+    else if (!RegQueryValueExW(hkey, signatureW, 0, &type, NULL, &len)) spec = AT_SIGNATURE;
+    else
+    {
+        RegCloseKey(hkey);
+        return NULL;
+    }
+
+    if (!(buf = heap_alloc(len + MAX_LEAD_BYTES)))
+    {
+        RegCloseKey(hkey);
+        return NULL;
+    }
+
+    if (!RegQueryValueExW(hkey, (spec == AT_KEYEXCHANGE) ? keyexchangeW : signatureW, 0, &type, buf, &len))
+    {
+        blob_in.pbData = buf;
+        blob_in.cbData = len;
+        if (CryptUnprotectData(&blob_in, NULL, NULL, NULL, NULL, 0, &blob_out))
+        {
+            assert(blob_in.cbData >= blob_out.cbData);
+            memcpy(buf, blob_out.pbData, blob_out.cbData);
+            LocalFree(blob_out.pbData);
+            *size = blob_out.cbData + MAX_LEAD_BYTES;
+            ret = buf;
+        }
+    }
+    else heap_free(buf);
+
+    RegCloseKey(hkey);
+    heap_free(path);
+    return ret;
+}
+
+static inline void reverse_bytes(BYTE *buf, ULONG len)
 {
-    int ret = pgnutls_certificate_allocate_credentials((gnutls_certificate_credentials_t*)&c->credentials);
+    BYTE tmp;
+    ULONG i;
+    for (i = 0; i < len / 2; i++)
+    {
+        tmp = buf[i];
+        buf[i] = buf[len - i - 1];
+        buf[len - i - 1] = tmp;
+    }
+}
+
+static ULONG set_component(gnutls_datum_t *comp, BYTE *data, ULONG len, ULONG *buflen)
+{
+    comp->data = data;
+    comp->size = len;
+    reverse_bytes(comp->data, comp->size);
+    if (comp->data[0] & 0x80) /* add leading 0 byte if most significant bit is set */
+    {
+        memmove(comp->data + 1, comp->data, *buflen);
+        comp->data[0] = 0;
+        comp->size++;
+    }
+    *buflen -= comp->size;
+    return comp->size;
+}
+
+static gnutls_x509_privkey_t get_x509_key(const CERT_CONTEXT *ctx)
+{
+    gnutls_privkey_t key = NULL;
+    gnutls_x509_privkey_t x509key = NULL;
+    gnutls_datum_t m, e, d, p, q, u, e1, e2;
+    BYTE *ptr, *buffer;
+    RSAPUBKEY *rsakey;
+    DWORD size;
+    int ret;
+
+    if (!(buffer = get_key_blob(ctx, &size))) return NULL;
+    if (size < sizeof(BLOBHEADER)) goto done;
+
+    rsakey = (RSAPUBKEY *)(buffer + sizeof(BLOBHEADER));
+    TRACE("RSA key bitlen %u pubexp %u\n", rsakey->bitlen, rsakey->pubexp);
+
+    size -= sizeof(BLOBHEADER) + FIELD_OFFSET(RSAPUBKEY, pubexp);
+    set_component(&e, (BYTE *)&rsakey->pubexp, sizeof(rsakey->pubexp), &size);
+
+    ptr = (BYTE *)(rsakey + 1);
+    ptr += set_component(&m, ptr, rsakey->bitlen / 8, &size);
+    ptr += set_component(&p, ptr, rsakey->bitlen / 16, &size);
+    ptr += set_component(&q, ptr, rsakey->bitlen / 16, &size);
+    ptr += set_component(&e1, ptr, rsakey->bitlen / 16, &size);
+    ptr += set_component(&e2, ptr, rsakey->bitlen / 16, &size);
+    ptr += set_component(&u, ptr, rsakey->bitlen / 16, &size);
+    ptr += set_component(&d, ptr, rsakey->bitlen / 8, &size);
+
+    if ((ret = pgnutls_privkey_init(&key)) < 0)
+    {
+        pgnutls_perror(ret);
+        goto done;
+    }
+
+    if ((ret = pgnutls_privkey_import_rsa_raw(key, &m, &e, &d, &p, &q, &u, &e1, &e2)) < 0)
+    {
+        pgnutls_perror(ret);
+        goto done;
+    }
+
+    if ((ret = pgnutls_privkey_export_x509(key, &x509key)) < 0)
+    {
+        pgnutls_perror(ret);
+    }
+
+done:
+    heap_free(buffer);
+    pgnutls_privkey_deinit(key);
+    return x509key;
+}
+
+static gnutls_x509_crt_t get_x509_crt(const CERT_CONTEXT *ctx)
+{
+    gnutls_datum_t data;
+    gnutls_x509_crt_t crt;
+    int ret;
+
+    if (!ctx) return FALSE;
+    if (ctx->dwCertEncodingType != X509_ASN_ENCODING)
+    {
+        FIXME("encoding type %u not supported\n", ctx->dwCertEncodingType);
+        return NULL;
+    }
+
+    if ((ret = pgnutls_x509_crt_init(&crt)) < 0)
+    {
+        pgnutls_perror(ret);
+        return NULL;
+    }
+
+    data.data = ctx->pbCertEncoded;
+    data.size = ctx->cbCertEncoded;
+    if ((ret = pgnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_DER)) < 0)
+    {
+        pgnutls_perror(ret);
+        pgnutls_x509_crt_deinit(crt);
+        return NULL;
+    }
+
+    return crt;
+}
+
+BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c, const CERT_CONTEXT *ctx)
+{
+    gnutls_certificate_credentials_t creds;
+    gnutls_x509_crt_t crt;
+    gnutls_x509_privkey_t key;
+    int ret;
+
+    ret = pgnutls_certificate_allocate_credentials(&creds);
+    if (ret != GNUTLS_E_SUCCESS)
+    {
+        pgnutls_perror(ret);
+        return FALSE;
+    }
+
+    if (!ctx)
+    {
+        c->credentials = creds;
+        return TRUE;
+    }
+
+    if (!(crt = get_x509_crt(ctx)))
+    {
+        pgnutls_certificate_free_credentials(creds);
+        return FALSE;
+    }
+
+    if (!(key = get_x509_key(ctx)))
+    {
+        pgnutls_x509_crt_deinit(crt);
+        pgnutls_certificate_free_credentials(creds);
+        return FALSE;
+    }
+
+    ret = pgnutls_certificate_set_x509_key(creds, &crt, 1, key);
+    pgnutls_x509_privkey_deinit(key);
+    pgnutls_x509_crt_deinit(crt);
     if (ret != GNUTLS_E_SUCCESS)
+    {
         pgnutls_perror(ret);
-    return (ret == GNUTLS_E_SUCCESS);
+        pgnutls_certificate_free_credentials(creds);
+        return FALSE;
+    }
+
+    c->credentials = creds;
+    return TRUE;
 }
 
 void schan_imp_free_certificate_credentials(schan_credentials *c)
@@ -597,6 +879,7 @@ BOOL schan_imp_init(void)
     LOAD_FUNCPTR(gnutls_certificate_allocate_credentials)
     LOAD_FUNCPTR(gnutls_certificate_free_credentials)
     LOAD_FUNCPTR(gnutls_certificate_get_peers)
+    LOAD_FUNCPTR(gnutls_certificate_set_x509_key)
     LOAD_FUNCPTR(gnutls_cipher_get)
     LOAD_FUNCPTR(gnutls_cipher_get_key_size)
     LOAD_FUNCPTR(gnutls_credentials_set)
@@ -613,6 +896,9 @@ BOOL schan_imp_init(void)
     LOAD_FUNCPTR(gnutls_perror)
     LOAD_FUNCPTR(gnutls_protocol_get_version)
     LOAD_FUNCPTR(gnutls_priority_set_direct)
+    LOAD_FUNCPTR(gnutls_privkey_deinit)
+    LOAD_FUNCPTR(gnutls_privkey_import_rsa_raw)
+    LOAD_FUNCPTR(gnutls_privkey_init)
     LOAD_FUNCPTR(gnutls_record_get_max_size);
     LOAD_FUNCPTR(gnutls_record_recv);
     LOAD_FUNCPTR(gnutls_record_send);
@@ -622,6 +908,10 @@ BOOL schan_imp_init(void)
     LOAD_FUNCPTR(gnutls_transport_set_ptr)
     LOAD_FUNCPTR(gnutls_transport_set_pull_function)
     LOAD_FUNCPTR(gnutls_transport_set_push_function)
+    LOAD_FUNCPTR(gnutls_x509_crt_deinit)
+    LOAD_FUNCPTR(gnutls_x509_crt_import)
+    LOAD_FUNCPTR(gnutls_x509_crt_init)
+    LOAD_FUNCPTR(gnutls_x509_privkey_deinit)
 #undef LOAD_FUNCPTR
 
     if (!(pgnutls_cipher_get_block_size = wine_dlsym(libgnutls_handle, "gnutls_cipher_get_block_size", NULL, 0)))
@@ -629,6 +919,11 @@ BOOL schan_imp_init(void)
         WARN("gnutls_cipher_get_block_size not found\n");
         pgnutls_cipher_get_block_size = compat_cipher_get_block_size;
     }
+    if (!(pgnutls_privkey_export_x509 = wine_dlsym(libgnutls_handle, "gnutls_privkey_export_x509", NULL, 0)))
+    {
+        WARN("gnutls_privkey_export_x509 not found\n");
+        pgnutls_privkey_export_x509 = compat_gnutls_privkey_export_x509;
+    }
 
     ret = pgnutls_global_init();
     if (ret != GNUTLS_E_SUCCESS)
diff --git a/dlls/secur32/schannel_macosx.c b/dlls/secur32/schannel_macosx.c
index 29d2f65172..981e4fb5ad 100644
--- a/dlls/secur32/schannel_macosx.c
+++ b/dlls/secur32/schannel_macosx.c
@@ -1185,9 +1185,9 @@ SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
     return SEC_E_OK;
 }
 
-BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c)
+BOOL schan_imp_allocate_certificate_credentials(schan_credentials *c, const CERT_CONTEXT *cert)
 {
-    /* The certificate is never really used for anything. */
+    if (cert) FIXME("no support for certificate credentials on this platform\n");
     c->credentials = NULL;
     return TRUE;
 }
diff --git a/dlls/secur32/secur32_priv.h b/dlls/secur32/secur32_priv.h
index 4566fe2446..a62d6cb2df 100644
--- a/dlls/secur32/secur32_priv.h
+++ b/dlls/secur32/secur32_priv.h
@@ -247,7 +247,7 @@ extern SECURITY_STATUS schan_imp_send(schan_imp_session session, const void *buf
                                       SIZE_T *length) DECLSPEC_HIDDEN;
 extern SECURITY_STATUS schan_imp_recv(schan_imp_session session, void *buffer,
                                       SIZE_T *length) DECLSPEC_HIDDEN;
-extern BOOL schan_imp_allocate_certificate_credentials(schan_credentials*) DECLSPEC_HIDDEN;
+extern BOOL schan_imp_allocate_certificate_credentials(schan_credentials *, const CERT_CONTEXT *) DECLSPEC_HIDDEN;
 extern void schan_imp_free_certificate_credentials(schan_credentials*) DECLSPEC_HIDDEN;
 extern DWORD schan_imp_enabled_protocols(void) DECLSPEC_HIDDEN;
 extern BOOL schan_imp_init(void) DECLSPEC_HIDDEN;
-- 
2.11.0




More information about the wine-devel mailing list