rpcrt4: Avoid casting an object to IUnknown.
[wine.git] / dlls / secur32 / schannel.c
blob05994ed0753ac33ca5efcb1a1f4a1de1ebac3a6f
1 /* Copyright (C) 2005 Juan Lang
2 * Copyright 2008 Henri Verbeet
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 * This file implements the schannel provider, or, the SSL/TLS implementations.
21 #include <assert.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <errno.h>
26 #define NONAMELESSUNION
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winternl.h"
30 #include "winreg.h"
31 #include "winnls.h"
32 #include "lmcons.h"
33 #include "sspi.h"
34 #define SCHANNEL_USE_BLACKLISTS
35 #include "schannel.h"
37 #include "wine/unixlib.h"
38 #include "wine/debug.h"
39 #include "secur32_priv.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
43 #define GNUTLS_CALL( func, params ) WINE_UNIX_CALL( unix_ ## func, params )
45 #define SCHAN_INVALID_HANDLE ~0UL
47 enum schan_handle_type
49 SCHAN_HANDLE_CRED,
50 SCHAN_HANDLE_CTX,
51 SCHAN_HANDLE_FREE
54 struct schan_handle
56 void *object;
57 enum schan_handle_type type;
60 struct schan_context
62 schan_session session;
63 ULONG req_ctx_attr;
64 const CERT_CONTEXT *cert;
65 SIZE_T header_size;
66 BOOL shutdown_requested;
69 static struct schan_handle *schan_handle_table;
70 static struct schan_handle *schan_free_handles;
71 static SIZE_T schan_handle_table_size;
72 static SIZE_T schan_handle_count;
74 /* Protocols enabled, only those may be used for the connection. */
75 static DWORD config_enabled_protocols;
77 /* Protocols disabled by default. They are enabled for using, but disabled when caller asks for default settings. */
78 static DWORD config_default_disabled_protocols;
80 static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type)
82 struct schan_handle *handle;
84 if (schan_free_handles)
86 DWORD index = schan_free_handles - schan_handle_table;
87 /* Use a free handle */
88 handle = schan_free_handles;
89 if (handle->type != SCHAN_HANDLE_FREE)
91 ERR("Handle %ld(%p) is in the free list, but has type %#x.\n", index, handle, handle->type);
92 return SCHAN_INVALID_HANDLE;
94 schan_free_handles = handle->object;
95 handle->object = object;
96 handle->type = type;
98 return index;
100 if (!(schan_handle_count < schan_handle_table_size))
102 /* Grow the table */
103 SIZE_T new_size = schan_handle_table_size + (schan_handle_table_size >> 1);
104 struct schan_handle *new_table = realloc(schan_handle_table, new_size * sizeof(*schan_handle_table));
105 if (!new_table)
107 ERR("Failed to grow the handle table\n");
108 return SCHAN_INVALID_HANDLE;
110 schan_handle_table = new_table;
111 schan_handle_table_size = new_size;
114 handle = &schan_handle_table[schan_handle_count++];
115 handle->object = object;
116 handle->type = type;
118 return handle - schan_handle_table;
121 static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type)
123 struct schan_handle *handle;
124 void *object;
126 if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
127 if (handle_idx >= schan_handle_count) return NULL;
128 handle = &schan_handle_table[handle_idx];
129 if (handle->type != type)
131 ERR("Handle %Id(%p) is not of type %#x\n", handle_idx, handle, type);
132 return NULL;
135 object = handle->object;
136 handle->object = schan_free_handles;
137 handle->type = SCHAN_HANDLE_FREE;
138 schan_free_handles = handle;
140 return object;
143 static void *schan_get_object(ULONG_PTR handle_idx, enum schan_handle_type type)
145 struct schan_handle *handle;
147 if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
148 if (handle_idx >= schan_handle_count) return NULL;
149 handle = &schan_handle_table[handle_idx];
150 if (handle->type != type)
152 ERR("Handle %Id(%p) is not of type %#x\n", handle_idx, handle, type);
153 return NULL;
156 return handle->object;
159 static void read_config(void)
161 DWORD enabled = 0, default_disabled = 0;
162 HKEY protocols_key, key;
163 WCHAR subkey_name[64];
164 unsigned i, server;
165 DWORD res;
167 static BOOL config_read = FALSE;
168 static const struct {
169 WCHAR key_name[20];
170 DWORD prot_client_flag;
171 DWORD prot_server_flag;
172 BOOL enabled; /* If no config is present, enable the protocol */
173 BOOL disabled_by_default; /* Disable if caller asks for default protocol set */
174 } protocol_config_keys[] = {
175 { L"SSL 2.0", SP_PROT_SSL2_CLIENT, SP_PROT_SSL2_SERVER, FALSE, TRUE }, /* NOTE: TRUE, TRUE on Windows */
176 { L"SSL 3.0", SP_PROT_SSL3_CLIENT, SP_PROT_SSL3_SERVER, TRUE, FALSE },
177 { L"TLS 1.0", SP_PROT_TLS1_0_CLIENT, SP_PROT_TLS1_0_SERVER, TRUE, FALSE },
178 { L"TLS 1.1", SP_PROT_TLS1_1_CLIENT, SP_PROT_TLS1_1_SERVER, TRUE, FALSE /* NOTE: not enabled by default on Windows */ },
179 { L"TLS 1.2", SP_PROT_TLS1_2_CLIENT, SP_PROT_TLS1_2_SERVER, TRUE, FALSE /* NOTE: not enabled by default on Windows */ },
180 { L"DTLS 1.0", SP_PROT_DTLS1_0_CLIENT, SP_PROT_DTLS1_0_SERVER, TRUE, TRUE },
181 { L"DTLS 1.2", SP_PROT_DTLS1_2_CLIENT, SP_PROT_DTLS1_2_SERVER, TRUE, TRUE },
184 /* No need for thread safety */
185 if(config_read)
186 return;
188 res = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
189 L"SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols", 0, KEY_READ,
190 &protocols_key);
191 if(res == ERROR_SUCCESS) {
192 DWORD type, size, value;
194 for(i = 0; i < ARRAY_SIZE(protocol_config_keys); i++) {
195 for (server = 0; server < 2; server++) {
196 DWORD flag = server ? protocol_config_keys[i].prot_server_flag
197 : protocol_config_keys[i].prot_client_flag;
198 wcscpy(subkey_name, protocol_config_keys[i].key_name);
199 wcscat(subkey_name, server ? L"\\Server" : L"\\Client");
200 res = RegOpenKeyExW(protocols_key, subkey_name, 0, KEY_READ, &key);
201 if(res != ERROR_SUCCESS) {
202 if(protocol_config_keys[i].enabled)
203 enabled |= flag;
204 if(protocol_config_keys[i].disabled_by_default)
205 default_disabled |= flag;
206 continue;
209 size = sizeof(value);
210 res = RegQueryValueExW(key, L"enabled", NULL, &type, (BYTE *)&value, &size);
211 if(res == ERROR_SUCCESS) {
212 if(type == REG_DWORD && value)
213 enabled |= flag;
214 }else if(protocol_config_keys[i].enabled) {
215 enabled |= flag;
218 size = sizeof(value);
219 res = RegQueryValueExW(key, L"DisabledByDefault", NULL, &type, (BYTE *)&value, &size);
220 if(res == ERROR_SUCCESS) {
221 if(type != REG_DWORD || value)
222 default_disabled |= flag;
223 }else if(protocol_config_keys[i].disabled_by_default) {
224 default_disabled |= flag;
227 RegCloseKey(key);
230 }else {
231 /* No config, enable all known protocols. */
232 for(i = 0; i < ARRAY_SIZE(protocol_config_keys); i++) {
233 DWORD flag = protocol_config_keys[i].prot_client_flag | protocol_config_keys[i].prot_server_flag;
234 if(protocol_config_keys[i].enabled)
235 enabled |= flag;
236 if(protocol_config_keys[i].disabled_by_default)
237 default_disabled |= flag;
241 RegCloseKey(protocols_key);
243 config_enabled_protocols = enabled & GNUTLS_CALL( get_enabled_protocols, NULL );
244 config_default_disabled_protocols = default_disabled;
245 config_read = TRUE;
247 TRACE("enabled %lx, disabled by default %lx\n", config_enabled_protocols, config_default_disabled_protocols);
250 static SECURITY_STATUS schan_QueryCredentialsAttributes(
251 PCredHandle phCredential, ULONG ulAttribute, VOID *pBuffer)
253 struct schan_credentials *cred;
254 SECURITY_STATUS ret;
256 cred = schan_get_object(phCredential->dwLower, SCHAN_HANDLE_CRED);
257 if(!cred)
258 return SEC_E_INVALID_HANDLE;
260 switch (ulAttribute)
262 case SECPKG_ATTR_SUPPORTED_ALGS:
263 if (pBuffer)
265 /* FIXME: get from CryptoAPI */
266 FIXME("SECPKG_ATTR_SUPPORTED_ALGS: stub\n");
267 ret = SEC_E_UNSUPPORTED_FUNCTION;
269 else
270 ret = SEC_E_INTERNAL_ERROR;
271 break;
272 case SECPKG_ATTR_CIPHER_STRENGTHS:
273 if (pBuffer)
275 SecPkgCred_CipherStrengths *r = pBuffer;
277 /* FIXME: get from CryptoAPI */
278 FIXME("SECPKG_ATTR_CIPHER_STRENGTHS: semi-stub\n");
279 r->dwMinimumCipherStrength = 40;
280 r->dwMaximumCipherStrength = 168;
281 ret = SEC_E_OK;
283 else
284 ret = SEC_E_INTERNAL_ERROR;
285 break;
286 case SECPKG_ATTR_SUPPORTED_PROTOCOLS:
287 if(pBuffer) {
288 /* Regardless of MSDN documentation, tests show that this attribute takes into account
289 * what protocols are enabled for given credential. */
290 ((SecPkgCred_SupportedProtocols*)pBuffer)->grbitProtocol = cred->enabled_protocols;
291 ret = SEC_E_OK;
292 }else {
293 ret = SEC_E_INTERNAL_ERROR;
295 break;
296 default:
297 ret = SEC_E_UNSUPPORTED_FUNCTION;
299 return ret;
302 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesA(
303 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
305 SECURITY_STATUS ret;
307 TRACE("(%p, %ld, %p)\n", phCredential, ulAttribute, pBuffer);
309 switch (ulAttribute)
311 case SECPKG_CRED_ATTR_NAMES:
312 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
313 ret = SEC_E_UNSUPPORTED_FUNCTION;
314 break;
315 default:
316 ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
317 pBuffer);
319 return ret;
322 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesW(
323 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
325 SECURITY_STATUS ret;
327 TRACE("(%p, %ld, %p)\n", phCredential, ulAttribute, pBuffer);
329 switch (ulAttribute)
331 case SECPKG_CRED_ATTR_NAMES:
332 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
333 ret = SEC_E_UNSUPPORTED_FUNCTION;
334 break;
335 default:
336 ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
337 pBuffer);
339 return ret;
342 static SECURITY_STATUS get_cert(const void *credentials, CERT_CONTEXT const **cert)
344 SECURITY_STATUS status;
345 const SCHANNEL_CRED *cred_old;
346 const SCH_CREDENTIALS *cred = credentials;
347 PCCERT_CONTEXT *cert_list;
348 DWORD i, cert_count;
350 switch (cred->dwVersion)
352 case SCH_CRED_V3:
353 case SCHANNEL_CRED_VERSION:
354 cred_old = credentials;
355 TRACE("dwVersion = %lu\n", cred_old->dwVersion);
356 TRACE("cCreds = %lu\n", cred_old->cCreds);
357 TRACE("paCred = %p\n", cred_old->paCred);
358 TRACE("hRootStore = %p\n", cred_old->hRootStore);
359 TRACE("cMappers = %lu\n", cred_old->cMappers);
360 TRACE("cSupportedAlgs = %lu:\n", cred_old->cSupportedAlgs);
361 for (i = 0; i < cred_old->cSupportedAlgs; i++) TRACE("%08x\n", cred_old->palgSupportedAlgs[i]);
362 TRACE("grbitEnabledProtocols = %08lx\n", cred_old->grbitEnabledProtocols);
363 TRACE("dwMinimumCipherStrength = %lu\n", cred_old->dwMinimumCipherStrength);
364 TRACE("dwMaximumCipherStrength = %lu\n", cred_old->dwMaximumCipherStrength);
365 TRACE("dwSessionLifespan = %lu\n", cred_old->dwSessionLifespan);
366 TRACE("dwFlags = %08lx\n", cred_old->dwFlags);
367 TRACE("dwCredFormat = %lu\n", cred_old->dwCredFormat);
368 cert_list = cred_old->paCred;
369 cert_count = cred_old->cCreds;
370 break;
372 case SCH_CREDENTIALS_VERSION:
373 TRACE("dwVersion = %lu\n", cred->dwVersion);
374 TRACE("dwCredFormat = %lu\n", cred->dwCredFormat);
375 TRACE("cCreds = %lu\n", cred->cCreds);
376 TRACE("paCred = %p\n", cred->paCred);
377 TRACE("hRootStore = %p\n", cred->hRootStore);
378 TRACE("cMappers = %lu\n", cred->cMappers);
379 TRACE("dwSessionLifespan = %lu\n", cred->dwSessionLifespan);
380 TRACE("dwFlags = %08lx\n", cred->dwFlags);
381 TRACE("cTlsParameters = %lu:\n", cred->cTlsParameters);
382 for (i = 0; i < cred->cTlsParameters; i++)
384 TRACE(" cAlpnIds %lu\n", cred->pTlsParameters[i].cAlpnIds);
385 TRACE(" grbitDisabledProtocols %08lx\n", cred->pTlsParameters[i].grbitDisabledProtocols);
386 TRACE(" cDisabledCrypto %lu\n", cred->pTlsParameters[i].cDisabledCrypto);
387 TRACE(" dwFlags %08lx\n", cred->pTlsParameters[i].dwFlags);
389 cert_list = cred->paCred;
390 cert_count = cred->cCreds;
391 break;
393 default:
394 FIXME("unhandled version %lu\n", cred->dwVersion);
395 return SEC_E_INTERNAL_ERROR;
398 if (!cert_count) status = SEC_E_NO_CREDENTIALS;
399 else if (cert_count > 1) status = SEC_E_UNKNOWN_CREDENTIALS;
400 else
402 DWORD spec;
403 HCRYPTPROV prov;
404 BOOL free;
406 if (CryptAcquireCertificatePrivateKey(cert_list[0], CRYPT_ACQUIRE_CACHE_FLAG, NULL, &prov, &spec, &free))
408 if (free) CryptReleaseContext(prov, 0);
409 *cert = cert_list[0];
410 status = SEC_E_OK;
412 else status = SEC_E_UNKNOWN_CREDENTIALS;
415 return status;
418 static DWORD get_enabled_protocols(const void *credentials)
420 const SCHANNEL_CRED *cred_old;
421 const SCH_CREDENTIALS *cred = credentials;
423 switch (cred->dwVersion)
425 case SCH_CRED_V3:
426 case SCHANNEL_CRED_VERSION:
427 cred_old = credentials;
428 return cred_old->grbitEnabledProtocols;
430 case SCH_CREDENTIALS_VERSION:
431 if (cred->cTlsParameters) FIXME("handle TLS parameters\n");
432 return 0;
434 default:
435 FIXME("unhandled version %lu\n", cred->dwVersion);
436 return 0;
440 static WCHAR *get_key_container_path(const CERT_CONTEXT *ctx)
442 CERT_KEY_CONTEXT keyctx;
443 DWORD size = sizeof(keyctx), prov_size = 0;
444 CRYPT_KEY_PROV_INFO *prov;
445 WCHAR username[UNLEN + 1], *ret = NULL;
446 DWORD len = ARRAY_SIZE(username);
448 if (CertGetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, &keyctx, &size))
450 char *str;
451 if (!CryptGetProvParam(keyctx.hCryptProv, PP_CONTAINER, NULL, &size, 0)) return NULL;
452 if (!(str = malloc(size))) return NULL;
453 if (!CryptGetProvParam(keyctx.hCryptProv, PP_CONTAINER, (BYTE *)str, &size, 0)) return NULL;
455 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
456 if (!(ret = malloc(sizeof(L"Software\\Wine\\Crypto\\RSA\\") + len * sizeof(WCHAR))))
458 free(str);
459 return NULL;
461 wcscpy(ret, L"Software\\Wine\\Crypto\\RSA\\");
462 MultiByteToWideChar(CP_ACP, 0, str, -1, ret + wcslen(ret), len);
463 free(str);
465 else if (CertGetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, NULL, &prov_size))
467 if (!(prov = malloc(prov_size))) return NULL;
468 if (!CertGetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, prov, &prov_size))
470 free(prov);
471 return NULL;
473 if (!(ret = malloc(sizeof(L"Software\\Wine\\Crypto\\RSA\\") + wcslen(prov->pwszContainerName) * sizeof(WCHAR))))
475 free(prov);
476 return NULL;
478 wcscpy(ret, L"Software\\Wine\\Crypto\\RSA\\");
479 wcscat(ret, prov->pwszContainerName);
480 free(prov);
483 if (!ret && GetUserNameW(username, &len) && (ret = malloc(sizeof(L"Software\\Wine\\Crypto\\RSA\\") + len * sizeof(WCHAR))))
485 wcscpy(ret, L"Software\\Wine\\Crypto\\RSA\\");
486 wcscat(ret, username);
489 return ret;
492 #define MAX_LEAD_BYTES 8
493 static BYTE *get_key_blob(const CERT_CONTEXT *ctx, DWORD *size)
495 BYTE *buf, *ret = NULL;
496 DATA_BLOB blob_in, blob_out;
497 DWORD spec = 0, type, len;
498 LSTATUS retval;
499 WCHAR *path;
500 HKEY hkey;
502 if (!(path = get_key_container_path(ctx))) return NULL;
503 retval = RegOpenKeyExW(HKEY_CURRENT_USER, path, 0, KEY_READ, &hkey);
504 free(path);
505 if (retval)
506 return NULL;
508 if (!RegQueryValueExW(hkey, L"KeyExchangeKeyPair", 0, &type, NULL, &len)) spec = AT_KEYEXCHANGE;
509 else if (!RegQueryValueExW(hkey, L"SignatureKeyPair", 0, &type, NULL, &len)) spec = AT_SIGNATURE;
510 else
512 RegCloseKey(hkey);
513 return NULL;
516 if (!(buf = malloc(len + MAX_LEAD_BYTES)))
518 RegCloseKey(hkey);
519 return NULL;
522 if (!RegQueryValueExW(hkey, (spec == AT_KEYEXCHANGE) ? L"KeyExchangeKeyPair" : L"SignatureKeyPair", 0,
523 &type, buf, &len))
525 blob_in.pbData = buf;
526 blob_in.cbData = len;
527 if (CryptUnprotectData(&blob_in, NULL, NULL, NULL, NULL, 0, &blob_out))
529 assert(blob_in.cbData >= blob_out.cbData);
530 memcpy(buf, blob_out.pbData, blob_out.cbData);
531 LocalFree(blob_out.pbData);
532 *size = blob_out.cbData + MAX_LEAD_BYTES;
533 ret = buf;
536 else free(buf);
538 RegCloseKey(hkey);
539 return ret;
542 static SECURITY_STATUS acquire_credentials_handle(ULONG fCredentialUse,
543 const SCHANNEL_CRED *schanCred, PCredHandle phCredential, PTimeStamp ptsExpiry)
545 struct schan_credentials *creds;
546 DWORD enabled_protocols, cred_enabled_protocols;
547 ULONG_PTR handle;
548 SECURITY_STATUS status = SEC_E_OK;
549 const CERT_CONTEXT *cert = NULL;
550 struct allocate_certificate_credentials_params params = { 0 };
551 BYTE *key_blob = NULL;
552 ULONG key_size = 0;
554 TRACE("fCredentialUse %#lx, schanCred %p, phCredential %p, ptsExpiry %p\n", fCredentialUse, schanCred, phCredential, ptsExpiry);
556 if (schanCred)
558 const unsigned dtls_protocols = SP_PROT_DTLS1_X;
559 const unsigned non_dtls_protocols = (SP_PROT_X_CLIENTS | SP_PROT_X_SERVERS) & ~SP_PROT_DTLS1_X;
561 status = get_cert(schanCred, &cert);
562 if (status != SEC_E_OK && status != SEC_E_NO_CREDENTIALS)
563 return status;
565 cred_enabled_protocols = get_enabled_protocols(schanCred);
566 if ((cred_enabled_protocols & non_dtls_protocols) &&
567 (cred_enabled_protocols & dtls_protocols)) return SEC_E_ALGORITHM_MISMATCH;
569 status = SEC_E_OK;
571 else if (fCredentialUse & SECPKG_CRED_INBOUND)
573 return SEC_E_NO_CREDENTIALS;
576 read_config();
577 if(schanCred && cred_enabled_protocols)
578 enabled_protocols = cred_enabled_protocols & config_enabled_protocols;
579 else
580 enabled_protocols = config_enabled_protocols & ~config_default_disabled_protocols;
581 if (!(fCredentialUse & SECPKG_CRED_OUTBOUND))
582 enabled_protocols &= ~SP_PROT_X_CLIENTS;
583 if (!(fCredentialUse & SECPKG_CRED_INBOUND))
584 enabled_protocols &= ~SP_PROT_X_SERVERS;
585 if(!enabled_protocols) {
586 ERR("Could not find matching protocol\n");
587 return SEC_E_ALGORITHM_MISMATCH;
590 if (!(creds = malloc(sizeof(*creds)))) return SEC_E_INSUFFICIENT_MEMORY;
591 creds->credential_use = fCredentialUse;
592 creds->enabled_protocols = enabled_protocols;
594 if (cert && !(key_blob = get_key_blob(cert, &key_size))) goto fail;
595 params.c = creds;
596 if (cert)
598 params.cert_encoding = cert->dwCertEncodingType;
599 params.cert_size = cert->cbCertEncoded;
600 params.cert_blob = cert->pbCertEncoded;
602 params.key_size = key_size;
603 params.key_blob = key_blob;
604 status = GNUTLS_CALL( allocate_certificate_credentials, &params );
605 free(key_blob);
606 if (status) goto fail;
608 handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
609 if (handle == SCHAN_INVALID_HANDLE) goto fail;
611 phCredential->dwLower = handle;
612 phCredential->dwUpper = 0;
614 if (ptsExpiry)
616 if (fCredentialUse & SECPKG_CRED_INBOUND)
618 /* FIXME: get expiry from cert */
620 else
622 /* Outbound credentials have no expiry */
623 ptsExpiry->LowPart = 0;
624 ptsExpiry->HighPart = 0;
628 return status;
630 fail:
631 free(creds);
632 return SEC_E_INTERNAL_ERROR;
635 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleA(
636 SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
637 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
638 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
640 TRACE("(%s, %s, 0x%08lx, %p, %p, %p, %p, %p, %p)\n",
641 debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
642 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
643 return acquire_credentials_handle(fCredentialUse,
644 pAuthData, phCredential, ptsExpiry);
647 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleW(
648 SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
649 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
650 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
652 TRACE("(%s, %s, 0x%08lx, %p, %p, %p, %p, %p, %p)\n",
653 debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
654 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
655 return acquire_credentials_handle(fCredentialUse,
656 pAuthData, phCredential, ptsExpiry);
659 static SECURITY_STATUS SEC_ENTRY schan_FreeCredentialsHandle(
660 PCredHandle phCredential)
662 struct schan_credentials *creds;
664 TRACE("phCredential %p\n", phCredential);
666 if (!phCredential) return SEC_E_INVALID_HANDLE;
668 creds = schan_free_handle(phCredential->dwLower, SCHAN_HANDLE_CRED);
669 if (!creds) return SEC_E_INVALID_HANDLE;
671 if (creds->credential_use == SECPKG_CRED_OUTBOUND)
673 struct free_certificate_credentials_params params = { creds };
674 GNUTLS_CALL( free_certificate_credentials, &params );
676 free(creds);
677 return SEC_E_OK;
680 static int schan_find_sec_buffer_idx(const SecBufferDesc *desc, unsigned int start_idx, ULONG buffer_type)
682 unsigned int i;
683 PSecBuffer buffer;
685 for (i = start_idx; i < desc->cBuffers; ++i)
687 buffer = &desc->pBuffers[i];
688 if ((buffer->BufferType | SECBUFFER_ATTRMASK) == (buffer_type | SECBUFFER_ATTRMASK))
689 return i;
692 return -1;
695 static void dump_buffer_desc(SecBufferDesc *desc)
697 unsigned int i;
699 if (!desc) return;
700 TRACE("Buffer desc %p:\n", desc);
701 for (i = 0; i < desc->cBuffers; ++i)
703 SecBuffer *b = &desc->pBuffers[i];
704 TRACE("\tbuffer %u: cbBuffer %ld, BufferType %#lx pvBuffer %p\n", i, b->cbBuffer, b->BufferType, b->pvBuffer);
708 #define HEADER_SIZE_TLS 5
709 #define HEADER_SIZE_DTLS 13
711 static inline SIZE_T read_record_size(const BYTE *buf, SIZE_T header_size)
713 return (buf[header_size - 2] << 8) | buf[header_size - 1];
716 static inline BOOL is_dtls_context(const struct schan_context *ctx)
718 return ctx->header_size == HEADER_SIZE_DTLS;
721 static void fill_missing_sec_buffer(SecBufferDesc *input, DWORD size)
723 int idx = schan_find_sec_buffer_idx(input, 0, SECBUFFER_EMPTY);
724 if (idx == -1) WARN("no empty buffer\n");
725 else
727 SecBuffer *buffer = &input->pBuffers[idx];
728 buffer->BufferType = SECBUFFER_MISSING;
729 buffer->cbBuffer = size;
733 static BOOL validate_input_buffers(SecBufferDesc *desc)
735 int i;
736 for (i = 0; i < desc->cBuffers; i++)
738 SecBuffer *buffer = &desc->pBuffers[i];
739 if (buffer->BufferType == SECBUFFER_EMPTY && buffer->cbBuffer) return FALSE;
741 return TRUE;
744 static SECURITY_STATUS establish_context(
745 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
746 PSecBufferDesc pInput, ULONG fContextReq, ULONG TargetDataRep,
747 PCtxtHandle phNewContext, PSecBufferDesc pOutput, ULONG *pfContextAttr,
748 PTimeStamp ptsTimeStamp, BOOL bIsServer)
750 const ULONG extra_size = 0x10000;
751 struct schan_context *ctx;
752 struct schan_credentials *cred;
753 SIZE_T expected_size = 0;
754 SECURITY_STATUS ret;
755 SecBuffer *buffer;
756 SecBuffer alloc_buffer = { 0 };
757 struct handshake_params params = { 0 };
758 int output_buffer_idx = -1;
759 int idx, i;
760 ULONG input_offset = 0, output_offset = 0;
761 SecBufferDesc input_desc, output_desc;
763 if (ptsTimeStamp)
765 ptsTimeStamp->LowPart = 0;
766 ptsTimeStamp->HighPart = 0;
769 if (!pOutput || !pOutput->cBuffers) return SEC_E_INVALID_TOKEN;
770 for (i = 0; i < pOutput->cBuffers; i++)
772 ULONG type = pOutput->pBuffers[i].BufferType;
773 ULONG allocate_memory_flag = bIsServer ? ASC_REQ_ALLOCATE_MEMORY : ISC_REQ_ALLOCATE_MEMORY;
775 if (type != SECBUFFER_TOKEN && type != SECBUFFER_ALERT) continue;
776 if (!pOutput->pBuffers[i].cbBuffer && !(fContextReq & allocate_memory_flag))
777 return SEC_E_INSUFFICIENT_MEMORY;
780 if (!phContext)
782 ULONG_PTR handle;
783 struct create_session_params create_params;
784 ULONG credential_use = bIsServer ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND;
786 if (!phCredential) return SEC_E_INVALID_HANDLE;
788 cred = schan_get_object(phCredential->dwLower, SCHAN_HANDLE_CRED);
789 if (!cred) return SEC_E_INVALID_HANDLE;
791 if (!(cred->credential_use & credential_use))
793 WARN("Invalid credential use %#lx, expected %#lx\n", cred->credential_use, credential_use);
794 return SEC_E_INVALID_HANDLE;
797 if (!(ctx = calloc(1, sizeof(*ctx)))) return SEC_E_INSUFFICIENT_MEMORY;
799 handle = schan_alloc_handle(ctx, SCHAN_HANDLE_CTX);
800 if (handle == SCHAN_INVALID_HANDLE)
802 free(ctx);
803 return SEC_E_INTERNAL_ERROR;
806 create_params.cred = cred;
807 create_params.session = &ctx->session;
808 if (GNUTLS_CALL( create_session, &create_params ))
810 schan_free_handle(handle, SCHAN_HANDLE_CTX);
811 free(ctx);
812 return SEC_E_INTERNAL_ERROR;
815 if (cred->enabled_protocols & SP_PROT_DTLS1_X)
816 ctx->header_size = HEADER_SIZE_DTLS;
817 else
818 ctx->header_size = HEADER_SIZE_TLS;
820 if (pszTargetName && *pszTargetName)
822 UINT len = WideCharToMultiByte( CP_UNIXCP, 0, pszTargetName, -1, NULL, 0, NULL, NULL );
823 char *target = malloc( len );
825 if (target)
827 struct set_session_target_params params = { ctx->session, target };
828 WideCharToMultiByte( CP_UNIXCP, 0, pszTargetName, -1, target, len, NULL, NULL );
829 GNUTLS_CALL( set_session_target, &params );
830 free( target );
834 if (pInput && (idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_APPLICATION_PROTOCOLS)) != -1)
836 struct set_application_protocols_params params = { ctx->session, pInput->pBuffers[idx].pvBuffer,
837 pInput->pBuffers[idx].cbBuffer };
838 GNUTLS_CALL( set_application_protocols, &params );
841 if (pInput && (idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_DTLS_MTU)) != -1)
843 buffer = &pInput->pBuffers[idx];
844 if (buffer->cbBuffer >= sizeof(WORD))
846 struct set_dtls_mtu_params params = { ctx->session, *(WORD *)buffer->pvBuffer };
847 GNUTLS_CALL( set_dtls_mtu, &params );
849 else WARN("invalid buffer size %lu\n", buffer->cbBuffer);
852 if (is_dtls_context(ctx))
854 struct set_dtls_timeouts_params params = { ctx->session, 0, 60000 };
855 GNUTLS_CALL( set_dtls_timeouts, &params );
858 phNewContext->dwLower = handle;
859 phNewContext->dwUpper = 0;
862 if (bIsServer || phContext)
864 SIZE_T record_size = 0;
865 unsigned char *ptr;
867 if (phContext && !(ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX))) return SEC_E_INVALID_HANDLE;
868 if (!pInput && !ctx->shutdown_requested && !is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE;
870 if (!ctx->shutdown_requested && pInput)
872 if (!validate_input_buffers(pInput)) return SEC_E_INVALID_TOKEN;
873 if ((idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN)) == -1) return SEC_E_INCOMPLETE_MESSAGE;
875 buffer = &pInput->pBuffers[idx];
876 ptr = buffer->pvBuffer;
878 if (buffer->cbBuffer < ctx->header_size)
880 TRACE("Expected at least %Iu bytes, but buffer only contains %lu bytes.\n",
881 ctx->header_size, buffer->cbBuffer);
882 fill_missing_sec_buffer(pInput, ctx->header_size - buffer->cbBuffer);
883 return SEC_E_INCOMPLETE_MESSAGE;
886 while (buffer->cbBuffer >= expected_size + ctx->header_size)
888 record_size = ctx->header_size + read_record_size(ptr, ctx->header_size);
890 if (buffer->cbBuffer < expected_size + record_size) break;
891 expected_size += record_size;
892 ptr += record_size;
895 if (!expected_size)
897 TRACE("Expected at least %Iu bytes, but buffer only contains %lu bytes.\n",
898 max(ctx->header_size, record_size), buffer->cbBuffer);
899 fill_missing_sec_buffer(pInput, record_size - buffer->cbBuffer);
900 return SEC_E_INCOMPLETE_MESSAGE;
903 TRACE("Using expected_size %Iu.\n", expected_size);
906 if (phNewContext && phContext) *phNewContext = *phContext;
909 ctx->req_ctx_attr = fContextReq;
911 /* Perform the TLS handshake */
912 memset(&input_desc, 0, sizeof(input_desc));
913 if (pInput && (idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN)) != -1)
915 input_desc.cBuffers = 1;
916 input_desc.pBuffers = &pInput->pBuffers[idx];
919 memset(&output_desc, 0, sizeof(output_desc));
920 idx = schan_find_sec_buffer_idx(pOutput, 0, SECBUFFER_TOKEN);
921 if (idx == -1)
922 idx = schan_find_sec_buffer_idx(pOutput, 0, SECBUFFER_EMPTY);
923 if (idx != -1)
925 output_desc.cBuffers = 1;
926 output_desc.pBuffers = &pOutput->pBuffers[idx];
927 if (!output_desc.pBuffers->pvBuffer || (fContextReq & ISC_REQ_ALLOCATE_MEMORY))
929 alloc_buffer.cbBuffer = extra_size;
930 alloc_buffer.BufferType = SECBUFFER_TOKEN;
931 alloc_buffer.pvBuffer = RtlAllocateHeap( GetProcessHeap(), 0, extra_size );
932 output_desc.pBuffers = &alloc_buffer;
936 params.session = ctx->session;
937 params.input = pInput ? &input_desc : NULL;
938 params.input_size = expected_size;
939 params.output = &output_desc;
940 params.input_offset = &input_offset;
941 params.output_buffer_idx = &output_buffer_idx;
942 params.output_offset = &output_offset;
943 params.control_token = ctx->shutdown_requested ? control_token_shutdown : control_token_none;
944 ctx->shutdown_requested = FALSE;
945 ret = GNUTLS_CALL( handshake, &params );
947 if (output_buffer_idx != -1)
949 SecBuffer *buffer = &pOutput->pBuffers[idx];
950 buffer->BufferType = SECBUFFER_TOKEN;
951 buffer->cbBuffer = output_offset;
952 if (output_desc.pBuffers == &alloc_buffer)
954 RtlReAllocateHeap( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
955 alloc_buffer.pvBuffer, buffer->cbBuffer );
957 buffer->pvBuffer = alloc_buffer.pvBuffer;
958 alloc_buffer.pvBuffer = NULL;
961 else
963 pOutput->pBuffers[0].cbBuffer = 0;
965 RtlFreeHeap( GetProcessHeap(), 0, alloc_buffer.pvBuffer );
967 if (input_offset && input_offset != pInput->pBuffers[0].cbBuffer)
969 if(pInput->cBuffers<2 || pInput->pBuffers[1].BufferType!=SECBUFFER_EMPTY)
970 return SEC_E_INVALID_TOKEN;
972 pInput->pBuffers[1].BufferType = SECBUFFER_EXTRA;
973 pInput->pBuffers[1].cbBuffer = pInput->pBuffers[0].cbBuffer - input_offset;
976 for (i = 0; i < pOutput->cBuffers; i++)
978 SecBuffer *buffer = &pOutput->pBuffers[i];
979 if (buffer->BufferType == SECBUFFER_ALERT) buffer->cbBuffer = 0;
982 if (bIsServer)
984 *pfContextAttr = ASC_RET_REPLAY_DETECT | ASC_RET_SEQUENCE_DETECT | ASC_RET_CONFIDENTIALITY | ASC_RET_STREAM;
985 if (ctx->req_ctx_attr & ASC_REQ_EXTENDED_ERROR) *pfContextAttr |= ASC_RET_EXTENDED_ERROR;
986 if (ctx->req_ctx_attr & ASC_REQ_DATAGRAM) *pfContextAttr |= ASC_RET_DATAGRAM;
987 if (ctx->req_ctx_attr & ASC_REQ_ALLOCATE_MEMORY) *pfContextAttr |= ASC_RET_ALLOCATED_MEMORY;
989 else
991 *pfContextAttr = ISC_RET_REPLAY_DETECT | ISC_RET_SEQUENCE_DETECT | ISC_RET_CONFIDENTIALITY | ISC_RET_STREAM;
992 if (ctx->req_ctx_attr & ISC_REQ_EXTENDED_ERROR) *pfContextAttr |= ISC_RET_EXTENDED_ERROR;
993 if (ctx->req_ctx_attr & ISC_REQ_DATAGRAM) *pfContextAttr |= ISC_RET_DATAGRAM;
994 if (ctx->req_ctx_attr & ISC_REQ_ALLOCATE_MEMORY) *pfContextAttr |= ISC_RET_ALLOCATED_MEMORY;
995 if (ctx->req_ctx_attr & ISC_REQ_USE_SUPPLIED_CREDS) *pfContextAttr |= ISC_RET_USED_SUPPLIED_CREDS;
996 if (ctx->req_ctx_attr & ISC_REQ_MANUAL_CRED_VALIDATION) *pfContextAttr |= ISC_RET_MANUAL_CRED_VALIDATION;
999 return ret;
1002 /***********************************************************************
1003 * InitializeSecurityContextW
1005 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
1006 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
1007 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
1008 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
1009 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
1011 TRACE("%p %p %s 0x%08lx %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
1012 debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
1013 Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
1015 dump_buffer_desc(pInput);
1016 dump_buffer_desc(pOutput);
1018 return establish_context(phCredential, phContext, pszTargetName, pInput, fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr, ptsExpiry, FALSE);
1021 /***********************************************************************
1022 * InitializeSecurityContextA
1024 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextA(
1025 PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
1026 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
1027 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
1028 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
1030 SECURITY_STATUS ret;
1031 SEC_WCHAR *target_name = NULL;
1033 TRACE("%p %p %s 0x%08lx %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
1034 debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
1035 Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
1037 if (pszTargetName)
1039 INT len = MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, NULL, 0);
1040 if (!(target_name = malloc(len * sizeof(*target_name)))) return SEC_E_INSUFFICIENT_MEMORY;
1041 MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, target_name, len);
1044 ret = schan_InitializeSecurityContextW(phCredential, phContext, target_name,
1045 fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
1046 phNewContext, pOutput, pfContextAttr, ptsExpiry);
1048 free(target_name);
1049 return ret;
1052 /***********************************************************************
1053 * AcceptSecurityContext
1055 static SECURITY_STATUS SEC_ENTRY schan_AcceptSecurityContext(
1056 PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
1057 ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext,
1058 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsTimeStamp)
1060 TRACE("%p %p %p 0x%08lx %ld %p %p %p %p\n", phCredential, phContext, pInput,
1061 fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr, ptsTimeStamp);
1063 dump_buffer_desc(pInput);
1064 dump_buffer_desc(pOutput);
1066 return establish_context(phCredential, phContext, NULL, pInput, fContextReq, TargetDataRep, phNewContext, pOutput, pfContextAttr, ptsTimeStamp, TRUE);
1069 static void *get_alg_name(ALG_ID id, BOOL wide)
1071 static const struct {
1072 ALG_ID alg_id;
1073 const char* name;
1074 const WCHAR nameW[8];
1075 } alg_name_map[] = {
1076 { CALG_ECDSA, "ECDSA", L"ECDSA" },
1077 { CALG_RSA_SIGN, "RSA", L"RSA" },
1078 { CALG_DES, "DES", L"DES" },
1079 { CALG_RC2, "RC2", L"RC2" },
1080 { CALG_3DES, "3DES", L"3DES" },
1081 { CALG_AES_128, "AES", L"AES" },
1082 { CALG_AES_192, "AES", L"AES" },
1083 { CALG_AES_256, "AES", L"AES" },
1084 { CALG_RC4, "RC4", L"RC4" },
1086 unsigned i;
1088 for (i = 0; i < ARRAY_SIZE(alg_name_map); i++)
1089 if (alg_name_map[i].alg_id == id)
1090 return wide ? (void*)alg_name_map[i].nameW : (void*)alg_name_map[i].name;
1092 FIXME("Unknown ALG_ID %04x\n", id);
1093 return NULL;
1096 static SECURITY_STATUS ensure_remote_cert(struct schan_context *ctx)
1098 HCERTSTORE store;
1099 PCCERT_CONTEXT cert = NULL;
1100 SECURITY_STATUS status;
1101 ULONG count, size = 0;
1102 struct get_session_peer_certificate_params params = { ctx->session, NULL, &size, &count };
1104 if (ctx->cert) return SEC_E_OK;
1105 if (!(store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL)))
1106 return GetLastError();
1108 status = GNUTLS_CALL( get_session_peer_certificate, &params );
1109 if (status != SEC_E_BUFFER_TOO_SMALL) goto done;
1110 if (!(params.buffer = malloc( size )))
1112 status = SEC_E_INSUFFICIENT_MEMORY;
1113 goto done;
1115 status = GNUTLS_CALL( get_session_peer_certificate, &params );
1116 if (status == SEC_E_OK)
1118 unsigned int i;
1119 ULONG *sizes;
1120 BYTE *blob;
1122 sizes = (ULONG *)params.buffer;
1123 blob = params.buffer + count * sizeof(*sizes);
1125 for (i = 0; i < count; i++)
1127 if (!CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING, blob, sizes[i],
1128 CERT_STORE_ADD_REPLACE_EXISTING, i ? NULL : &cert))
1130 if (i) CertFreeCertificateContext(cert);
1131 return GetLastError();
1133 blob += sizes[i];
1136 free(params.buffer);
1137 done:
1138 ctx->cert = cert;
1139 CertCloseStore(store, 0);
1140 return status;
1143 static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
1144 PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
1146 struct schan_context *ctx;
1147 SECURITY_STATUS status;
1149 TRACE("context_handle %p, attribute %#lx, buffer %p\n",
1150 context_handle, attribute, buffer);
1152 if (!context_handle || !(ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX)))
1153 return SEC_E_INVALID_HANDLE;
1155 switch(attribute)
1157 case SECPKG_ATTR_STREAM_SIZES:
1159 SecPkgContext_ConnectionInfo info;
1160 struct get_connection_info_params params = { ctx->session, &info };
1161 status = GNUTLS_CALL( get_connection_info, &params );
1162 if (status == SEC_E_OK)
1164 struct session_params params = { ctx->session };
1165 SecPkgContext_StreamSizes *stream_sizes = buffer;
1166 SIZE_T mac_size = info.dwHashStrength;
1167 unsigned int block_size = GNUTLS_CALL( get_session_cipher_block_size, &params );
1168 unsigned int message_size = GNUTLS_CALL( get_max_message_size, &params );
1170 TRACE("Using header size %Iu mac bytes %Iu, message size %u, block size %u\n",
1171 ctx->header_size, mac_size, message_size, block_size);
1173 /* These are defined by the TLS RFC */
1174 stream_sizes->cbHeader = ctx->header_size;
1175 stream_sizes->cbTrailer = mac_size + 256; /* Max 255 bytes padding + 1 for padding size */
1176 stream_sizes->cbMaximumMessage = message_size;
1177 stream_sizes->cBuffers = 4;
1178 stream_sizes->cbBlockSize = block_size;
1181 return status;
1183 case SECPKG_ATTR_KEY_INFO:
1185 SecPkgContext_ConnectionInfo conn_info;
1186 struct get_connection_info_params params = { ctx->session, &conn_info };
1187 status = GNUTLS_CALL( get_connection_info, &params );
1188 if (status == SEC_E_OK)
1190 struct session_params params = { ctx->session };
1191 SecPkgContext_KeyInfoW *info = buffer;
1192 info->KeySize = conn_info.dwCipherStrength;
1193 info->SignatureAlgorithm = GNUTLS_CALL( get_key_signature_algorithm, &params );
1194 info->EncryptAlgorithm = conn_info.aiCipher;
1195 info->sSignatureAlgorithmName = get_alg_name(info->SignatureAlgorithm, TRUE);
1196 info->sEncryptAlgorithmName = get_alg_name(info->EncryptAlgorithm, TRUE);
1198 return status;
1200 case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
1202 PCCERT_CONTEXT *cert = buffer;
1204 status = ensure_remote_cert(ctx);
1205 if(status != SEC_E_OK)
1206 return status;
1208 *cert = CertDuplicateCertificateContext(ctx->cert);
1209 return SEC_E_OK;
1211 case SECPKG_ATTR_CONNECTION_INFO:
1213 SecPkgContext_ConnectionInfo *info = buffer;
1214 struct get_connection_info_params params = { ctx->session, info };
1215 return GNUTLS_CALL( get_connection_info, &params );
1217 case SECPKG_ATTR_ENDPOINT_BINDINGS:
1219 SecPkgContext_Bindings *bindings = buffer;
1220 CCRYPT_OID_INFO *info;
1221 ALG_ID hash_alg = CALG_SHA_256;
1222 BYTE hash[1024];
1223 DWORD hash_size;
1224 char *p;
1225 BOOL r;
1227 static const char prefix[] = "tls-server-end-point:";
1229 status = ensure_remote_cert(ctx);
1230 if(status != SEC_E_OK)
1231 return status;
1233 /* RFC 5929 */
1234 info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, ctx->cert->pCertInfo->SignatureAlgorithm.pszObjId, 0);
1235 if(info && info->u.Algid != CALG_SHA1 && info->u.Algid != CALG_MD5)
1236 hash_alg = info->u.Algid;
1238 hash_size = sizeof(hash);
1239 r = CryptHashCertificate(0, hash_alg, 0, ctx->cert->pbCertEncoded, ctx->cert->cbCertEncoded, hash, &hash_size);
1240 if(!r)
1241 return GetLastError();
1243 bindings->BindingsLength = sizeof(*bindings->Bindings) + sizeof(prefix)-1 + hash_size;
1244 /* freed with FreeContextBuffer */
1245 bindings->Bindings = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, bindings->BindingsLength);
1246 if(!bindings->Bindings)
1247 return SEC_E_INSUFFICIENT_MEMORY;
1249 bindings->Bindings->cbApplicationDataLength = sizeof(prefix)-1 + hash_size;
1250 bindings->Bindings->dwApplicationDataOffset = sizeof(*bindings->Bindings);
1252 p = (char*)(bindings->Bindings+1);
1253 memcpy(p, prefix, sizeof(prefix)-1);
1254 p += sizeof(prefix)-1;
1255 memcpy(p, hash, hash_size);
1256 return SEC_E_OK;
1258 case SECPKG_ATTR_UNIQUE_BINDINGS:
1260 static const char prefix[] = "tls-unique:";
1261 SecPkgContext_Bindings *bindings = buffer;
1262 ULONG size;
1263 char *p;
1264 struct get_unique_channel_binding_params params = { ctx->session, NULL, &size };
1266 if (GNUTLS_CALL( get_unique_channel_binding, &params ) != SEC_E_BUFFER_TOO_SMALL)
1267 return SEC_E_INTERNAL_ERROR;
1269 bindings->BindingsLength = sizeof(*bindings->Bindings) + sizeof(prefix)-1 + size;
1270 /* freed with FreeContextBuffer */
1271 bindings->Bindings = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, bindings->BindingsLength);
1272 if(!bindings->Bindings)
1273 return SEC_E_INSUFFICIENT_MEMORY;
1275 bindings->Bindings->cbApplicationDataLength = sizeof(prefix)-1 + size;
1276 bindings->Bindings->dwApplicationDataOffset = sizeof(*bindings->Bindings);
1278 p = (char*)(bindings->Bindings+1);
1279 memcpy(p, prefix, sizeof(prefix)-1);
1280 p += sizeof(prefix)-1;
1281 params.buffer = p;
1282 return GNUTLS_CALL( get_unique_channel_binding, &params );
1284 case SECPKG_ATTR_APPLICATION_PROTOCOL:
1286 SecPkgContext_ApplicationProtocol *protocol = buffer;
1287 struct get_application_protocol_params params = { ctx->session, protocol };
1288 return GNUTLS_CALL( get_application_protocol, &params );
1290 case SECPKG_ATTR_CIPHER_INFO:
1292 SecPkgContext_CipherInfo *info = buffer;
1293 struct get_cipher_info_params params = { ctx->session, info };
1294 return GNUTLS_CALL( get_cipher_info, &params );
1297 default:
1298 FIXME("Unhandled attribute %#lx\n", attribute);
1299 return SEC_E_UNSUPPORTED_FUNCTION;
1303 static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesA(
1304 PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
1306 TRACE("context_handle %p, attribute %#lx, buffer %p\n",
1307 context_handle, attribute, buffer);
1309 switch(attribute)
1311 case SECPKG_ATTR_STREAM_SIZES:
1312 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1313 case SECPKG_ATTR_KEY_INFO:
1315 SECURITY_STATUS status = schan_QueryContextAttributesW(context_handle, attribute, buffer);
1316 if (status == SEC_E_OK)
1318 SecPkgContext_KeyInfoA *info = buffer;
1319 info->sSignatureAlgorithmName = get_alg_name(info->SignatureAlgorithm, FALSE);
1320 info->sEncryptAlgorithmName = get_alg_name(info->EncryptAlgorithm, FALSE);
1322 return status;
1324 case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
1325 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1326 case SECPKG_ATTR_CONNECTION_INFO:
1327 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1328 case SECPKG_ATTR_ENDPOINT_BINDINGS:
1329 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1330 case SECPKG_ATTR_UNIQUE_BINDINGS:
1331 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1332 case SECPKG_ATTR_APPLICATION_PROTOCOL:
1333 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1334 case SECPKG_ATTR_CIPHER_INFO:
1335 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1337 default:
1338 FIXME("Unhandled attribute %#lx\n", attribute);
1339 return SEC_E_UNSUPPORTED_FUNCTION;
1343 static SECURITY_STATUS SEC_ENTRY schan_EncryptMessage(PCtxtHandle context_handle,
1344 ULONG quality, PSecBufferDesc message, ULONG message_seq_no)
1346 struct schan_context *ctx;
1347 struct send_params params;
1348 SECURITY_STATUS status;
1349 SecBuffer *buffer;
1350 SIZE_T data_size;
1351 char *data;
1352 int output_buffer_idx = -1;
1353 ULONG output_offset = 0;
1354 SecBufferDesc output_desc = { 0 };
1355 SecBuffer output_buffers[3];
1356 int header_idx, data_idx, trailer_idx = -1;
1357 int buffer_index[3];
1359 TRACE("context_handle %p, quality %ld, message %p, message_seq_no %ld\n",
1360 context_handle, quality, message, message_seq_no);
1362 if (!context_handle) return SEC_E_INVALID_HANDLE;
1363 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
1365 dump_buffer_desc(message);
1367 data_idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_DATA);
1368 if (data_idx == -1)
1370 WARN("No data buffer passed\n");
1371 return SEC_E_INTERNAL_ERROR;
1373 buffer = &message->pBuffers[data_idx];
1375 data_size = buffer->cbBuffer;
1376 data = malloc(data_size);
1377 memcpy(data, buffer->pvBuffer, data_size);
1379 /* Use { STREAM_HEADER, DATA, STREAM_TRAILER } or { TOKEN, DATA, TOKEN } buffers. */
1381 output_desc.pBuffers = output_buffers;
1382 if ((header_idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_STREAM_HEADER)) == -1)
1384 if ((header_idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_TOKEN)) != -1)
1386 output_buffers[output_desc.cBuffers++] = message->pBuffers[header_idx];
1387 output_buffers[output_desc.cBuffers++] = message->pBuffers[data_idx];
1388 trailer_idx = schan_find_sec_buffer_idx(message, header_idx + 1, SECBUFFER_TOKEN);
1389 if (trailer_idx != -1)
1390 output_buffers[output_desc.cBuffers++] = message->pBuffers[trailer_idx];
1393 else
1395 output_buffers[output_desc.cBuffers++] = message->pBuffers[header_idx];
1396 output_buffers[output_desc.cBuffers++] = message->pBuffers[data_idx];
1397 trailer_idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_STREAM_TRAILER);
1398 if (trailer_idx != -1)
1399 output_buffers[output_desc.cBuffers++] = message->pBuffers[trailer_idx];
1402 buffer_index[0] = header_idx;
1403 buffer_index[1] = data_idx;
1404 buffer_index[2] = trailer_idx;
1406 params.session = ctx->session;
1407 params.output = &output_desc;
1408 params.buffer = data;
1409 params.length = data_size;
1410 params.output_buffer_idx = &output_buffer_idx;
1411 params.output_offset = &output_offset;
1412 status = GNUTLS_CALL( send, &params );
1414 if (!status)
1415 message->pBuffers[buffer_index[output_buffer_idx]].cbBuffer = output_offset;
1417 free(data);
1419 TRACE("Returning %#lx.\n", status);
1421 return status;
1424 static int schan_validate_decrypt_buffer_desc(PSecBufferDesc message)
1426 int data_idx = -1;
1427 unsigned int empty_count = 0;
1428 unsigned int i;
1430 if (message->cBuffers < 4)
1432 WARN("Less than four buffers passed\n");
1433 return -1;
1436 for (i = 0; i < message->cBuffers; ++i)
1438 SecBuffer *b = &message->pBuffers[i];
1439 if (b->BufferType == SECBUFFER_DATA)
1441 if (data_idx != -1)
1443 WARN("More than one data buffer passed\n");
1444 return -1;
1446 data_idx = i;
1448 else if (b->BufferType == SECBUFFER_EMPTY)
1449 ++empty_count;
1452 if (data_idx == -1)
1454 WARN("No data buffer passed\n");
1455 return -1;
1458 if (empty_count < 3)
1460 WARN("Less than three empty buffers passed\n");
1461 return -1;
1464 return data_idx;
1467 static void schan_decrypt_fill_buffer(PSecBufferDesc message, ULONG buffer_type, void *data, ULONG size)
1469 int idx;
1470 SecBuffer *buffer;
1472 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1473 buffer = &message->pBuffers[idx];
1475 buffer->BufferType = buffer_type;
1476 buffer->pvBuffer = data;
1477 buffer->cbBuffer = size;
1480 static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle,
1481 PSecBufferDesc message, ULONG message_seq_no, PULONG quality)
1483 SECURITY_STATUS status = SEC_E_OK;
1484 struct schan_context *ctx;
1485 struct recv_params params;
1486 SecBuffer *buffer;
1487 SIZE_T data_size;
1488 char *data;
1489 unsigned expected_size;
1490 ULONG received = 0;
1491 int idx;
1492 unsigned char *buf_ptr;
1493 SecBufferDesc input_desc = { 0 };
1495 TRACE("context_handle %p, message %p, message_seq_no %ld, quality %p\n",
1496 context_handle, message, message_seq_no, quality);
1498 if (!context_handle) return SEC_E_INVALID_HANDLE;
1499 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
1501 dump_buffer_desc(message);
1503 idx = schan_validate_decrypt_buffer_desc(message);
1504 if (idx == -1)
1505 return SEC_E_INVALID_TOKEN;
1506 buffer = &message->pBuffers[idx];
1507 buf_ptr = buffer->pvBuffer;
1509 expected_size = ctx->header_size + read_record_size(buf_ptr, ctx->header_size);
1510 if(buffer->cbBuffer < expected_size)
1512 TRACE("Expected %u bytes, but buffer only contains %lu bytes\n", expected_size, buffer->cbBuffer);
1513 buffer->BufferType = SECBUFFER_MISSING;
1514 buffer->cbBuffer = expected_size - buffer->cbBuffer;
1516 /* This is a bit weird, but windows does it too */
1517 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1518 buffer = &message->pBuffers[idx];
1519 buffer->BufferType = SECBUFFER_MISSING;
1520 buffer->cbBuffer = expected_size - buffer->cbBuffer;
1522 TRACE("Returning SEC_E_INCOMPLETE_MESSAGE\n");
1523 return SEC_E_INCOMPLETE_MESSAGE;
1526 data_size = expected_size - ctx->header_size;
1527 data = malloc(data_size);
1529 received = data_size;
1531 input_desc.cBuffers = 1;
1532 input_desc.pBuffers = &message->pBuffers[idx];
1534 params.session = ctx->session;
1535 params.input = &input_desc;
1536 params.input_size = expected_size;
1537 params.buffer = data;
1538 params.length = &received;
1539 status = GNUTLS_CALL( recv, &params );
1541 if (status != SEC_E_OK && status != SEC_I_RENEGOTIATE)
1543 free(data);
1544 ERR("Returning %lx\n", status);
1545 return status;
1548 TRACE("Received %lu bytes\n", received);
1550 memcpy(buf_ptr + ctx->header_size, data, received);
1551 free(data);
1553 schan_decrypt_fill_buffer(message, SECBUFFER_DATA,
1554 buf_ptr + ctx->header_size, received);
1556 schan_decrypt_fill_buffer(message, SECBUFFER_STREAM_TRAILER,
1557 buf_ptr + ctx->header_size + received, buffer->cbBuffer - ctx->header_size - received);
1559 if(buffer->cbBuffer > expected_size)
1560 schan_decrypt_fill_buffer(message, SECBUFFER_EXTRA,
1561 buf_ptr + expected_size, buffer->cbBuffer - expected_size);
1563 buffer->BufferType = SECBUFFER_STREAM_HEADER;
1564 buffer->cbBuffer = ctx->header_size;
1566 return status;
1569 static SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context_handle)
1571 struct schan_context *ctx;
1572 struct session_params params;
1574 TRACE("context_handle %p\n", context_handle);
1576 if (!context_handle) return SEC_E_INVALID_HANDLE;
1578 ctx = schan_free_handle(context_handle->dwLower, SCHAN_HANDLE_CTX);
1579 if (!ctx) return SEC_E_INVALID_HANDLE;
1581 if (ctx->cert) CertFreeCertificateContext(ctx->cert);
1582 params.session = ctx->session;
1583 GNUTLS_CALL( dispose_session, &params );
1584 free(ctx);
1585 return SEC_E_OK;
1588 static SECURITY_STATUS SEC_ENTRY schan_ApplyControlToken(PCtxtHandle context_handle, PSecBufferDesc input)
1590 struct schan_context *ctx;
1592 TRACE("%p %p\n", context_handle, input);
1594 dump_buffer_desc(input);
1596 if (!context_handle) return SEC_E_INVALID_HANDLE;
1597 if (!input) return SEC_E_INTERNAL_ERROR;
1599 if (input->cBuffers != 1) return SEC_E_INVALID_TOKEN;
1600 if (input->pBuffers[0].BufferType != SECBUFFER_TOKEN) return SEC_E_INVALID_TOKEN;
1601 if (input->pBuffers[0].cbBuffer < sizeof(DWORD)) return SEC_E_UNSUPPORTED_FUNCTION;
1602 if (*(DWORD *)input->pBuffers[0].pvBuffer != SCHANNEL_SHUTDOWN) return SEC_E_UNSUPPORTED_FUNCTION;
1604 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
1605 ctx->shutdown_requested = TRUE;
1607 return SEC_E_OK;
1610 static const SecurityFunctionTableA schanTableA = {
1612 NULL, /* EnumerateSecurityPackagesA */
1613 schan_QueryCredentialsAttributesA,
1614 schan_AcquireCredentialsHandleA,
1615 schan_FreeCredentialsHandle,
1616 NULL, /* Reserved2 */
1617 schan_InitializeSecurityContextA,
1618 schan_AcceptSecurityContext,
1619 NULL, /* CompleteAuthToken */
1620 schan_DeleteSecurityContext,
1621 schan_ApplyControlToken, /* ApplyControlToken */
1622 schan_QueryContextAttributesA,
1623 NULL, /* ImpersonateSecurityContext */
1624 NULL, /* RevertSecurityContext */
1625 NULL, /* MakeSignature */
1626 NULL, /* VerifySignature */
1627 FreeContextBuffer,
1628 NULL, /* QuerySecurityPackageInfoA */
1629 NULL, /* Reserved3 */
1630 NULL, /* Reserved4 */
1631 NULL, /* ExportSecurityContext */
1632 NULL, /* ImportSecurityContextA */
1633 NULL, /* AddCredentialsA */
1634 NULL, /* Reserved8 */
1635 NULL, /* QuerySecurityContextToken */
1636 schan_EncryptMessage,
1637 schan_DecryptMessage,
1638 NULL, /* SetContextAttributesA */
1641 static const SecurityFunctionTableW schanTableW = {
1643 NULL, /* EnumerateSecurityPackagesW */
1644 schan_QueryCredentialsAttributesW,
1645 schan_AcquireCredentialsHandleW,
1646 schan_FreeCredentialsHandle,
1647 NULL, /* Reserved2 */
1648 schan_InitializeSecurityContextW,
1649 schan_AcceptSecurityContext,
1650 NULL, /* CompleteAuthToken */
1651 schan_DeleteSecurityContext,
1652 schan_ApplyControlToken, /* ApplyControlToken */
1653 schan_QueryContextAttributesW,
1654 NULL, /* ImpersonateSecurityContext */
1655 NULL, /* RevertSecurityContext */
1656 NULL, /* MakeSignature */
1657 NULL, /* VerifySignature */
1658 FreeContextBuffer,
1659 NULL, /* QuerySecurityPackageInfoW */
1660 NULL, /* Reserved3 */
1661 NULL, /* Reserved4 */
1662 NULL, /* ExportSecurityContext */
1663 NULL, /* ImportSecurityContextW */
1664 NULL, /* AddCredentialsW */
1665 NULL, /* Reserved8 */
1666 NULL, /* QuerySecurityContextToken */
1667 schan_EncryptMessage,
1668 schan_DecryptMessage,
1669 NULL, /* SetContextAttributesW */
1672 void SECUR32_initSchannelSP(void)
1674 /* This is what Windows reports. This shouldn't break any applications
1675 * even though the functions are missing, because the wrapper will
1676 * return SEC_E_UNSUPPORTED_FUNCTION if our function is NULL.
1678 static const LONG caps =
1679 SECPKG_FLAG_INTEGRITY |
1680 SECPKG_FLAG_PRIVACY |
1681 SECPKG_FLAG_CONNECTION |
1682 SECPKG_FLAG_MULTI_REQUIRED |
1683 SECPKG_FLAG_EXTENDED_ERROR |
1684 SECPKG_FLAG_IMPERSONATION |
1685 SECPKG_FLAG_ACCEPT_WIN32_NAME |
1686 SECPKG_FLAG_STREAM;
1687 static const short version = 1;
1688 static const LONG maxToken = 16384;
1689 SEC_WCHAR *uniSPName = (SEC_WCHAR *)UNISP_NAME_W,
1690 *schannel = (SEC_WCHAR *)SCHANNEL_NAME_W;
1691 const SecPkgInfoW info[] = {
1692 { caps, version, UNISP_RPC_ID, maxToken, uniSPName, uniSPName },
1693 { caps, version, UNISP_RPC_ID, maxToken, schannel, (SEC_WCHAR *)L"Schannel Security Package" },
1695 SecureProvider *provider;
1697 if (__wine_init_unix_call() || GNUTLS_CALL( process_attach, NULL ))
1699 ERR( "no schannel support, expect problems\n" );
1700 return;
1703 schan_handle_table = malloc(64 * sizeof(*schan_handle_table));
1704 if (!schan_handle_table)
1706 ERR("Failed to allocate schannel handle table.\n");
1707 goto fail;
1709 schan_handle_table_size = 64;
1711 provider = SECUR32_addProvider(&schanTableA, &schanTableW, L"schannel.dll");
1712 if (!provider)
1714 ERR("Failed to add schannel provider.\n");
1715 goto fail;
1718 SECUR32_addPackages(provider, ARRAY_SIZE(info), NULL, info);
1719 return;
1721 fail:
1722 free(schan_handle_table);
1723 schan_handle_table = NULL;
1724 return;
1727 void SECUR32_deinitSchannelSP(void)
1729 SIZE_T i = schan_handle_count;
1731 if (!schan_handle_table) return;
1733 /* deinitialized sessions first because a pointer to the credentials
1734 * may be stored for the session. */
1735 while (i--)
1737 if (schan_handle_table[i].type == SCHAN_HANDLE_CTX)
1739 struct schan_context *ctx = schan_free_handle(i, SCHAN_HANDLE_CTX);
1740 struct session_params params = { ctx->session };
1741 GNUTLS_CALL( dispose_session, &params );
1742 free(ctx);
1745 i = schan_handle_count;
1746 while (i--)
1748 if (schan_handle_table[i].type != SCHAN_HANDLE_FREE)
1750 struct schan_credentials *cred = schan_free_handle(i, SCHAN_HANDLE_CRED);
1751 struct free_certificate_credentials_params params = { cred };
1752 GNUTLS_CALL( free_certificate_credentials, &params );
1753 free(cred);
1756 free(schan_handle_table);
1757 GNUTLS_CALL( process_detach, NULL );