Signed-off-by: Hans Leidekker hans@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;