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.
26 #define NONAMELESSUNION
34 #define SCHANNEL_USE_BLACKLISTS
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
57 enum schan_handle_type type
;
62 schan_session session
;
64 const CERT_CONTEXT
*cert
;
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
;
100 if (!(schan_handle_count
< schan_handle_table_size
))
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
));
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
;
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
;
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
);
135 object
= handle
->object
;
136 handle
->object
= schan_free_handles
;
137 handle
->type
= SCHAN_HANDLE_FREE
;
138 schan_free_handles
= handle
;
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
);
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];
167 static BOOL config_read
= FALSE
;
168 static const struct {
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 */
188 res
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
,
189 L
"SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols", 0, KEY_READ
,
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
)
204 if(protocol_config_keys
[i
].disabled_by_default
)
205 default_disabled
|= flag
;
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
)
214 }else if(protocol_config_keys
[i
].enabled
) {
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
;
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
)
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
;
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
;
256 cred
= schan_get_object(phCredential
->dwLower
, SCHAN_HANDLE_CRED
);
258 return SEC_E_INVALID_HANDLE
;
262 case SECPKG_ATTR_SUPPORTED_ALGS
:
265 /* FIXME: get from CryptoAPI */
266 FIXME("SECPKG_ATTR_SUPPORTED_ALGS: stub\n");
267 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
270 ret
= SEC_E_INTERNAL_ERROR
;
272 case SECPKG_ATTR_CIPHER_STRENGTHS
:
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;
284 ret
= SEC_E_INTERNAL_ERROR
;
286 case SECPKG_ATTR_SUPPORTED_PROTOCOLS
:
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
;
293 ret
= SEC_E_INTERNAL_ERROR
;
297 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
302 static SECURITY_STATUS SEC_ENTRY
schan_QueryCredentialsAttributesA(
303 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
307 TRACE("(%p, %ld, %p)\n", phCredential
, ulAttribute
, pBuffer
);
311 case SECPKG_CRED_ATTR_NAMES
:
312 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
313 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
316 ret
= schan_QueryCredentialsAttributes(phCredential
, ulAttribute
,
322 static SECURITY_STATUS SEC_ENTRY
schan_QueryCredentialsAttributesW(
323 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
327 TRACE("(%p, %ld, %p)\n", phCredential
, ulAttribute
, pBuffer
);
331 case SECPKG_CRED_ATTR_NAMES
:
332 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
333 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
336 ret
= schan_QueryCredentialsAttributes(phCredential
, ulAttribute
,
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
;
350 switch (cred
->dwVersion
)
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
;
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
;
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
;
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];
412 else status
= SEC_E_UNKNOWN_CREDENTIALS
;
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
)
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");
435 FIXME("unhandled version %lu\n", cred
->dwVersion
);
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
))
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
))))
461 wcscpy(ret
, L
"Software\\Wine\\Crypto\\RSA\\");
462 MultiByteToWideChar(CP_ACP
, 0, str
, -1, ret
+ wcslen(ret
), len
);
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
))
473 if (!(ret
= malloc(sizeof(L
"Software\\Wine\\Crypto\\RSA\\") + wcslen(prov
->pwszContainerName
) * sizeof(WCHAR
))))
478 wcscpy(ret
, L
"Software\\Wine\\Crypto\\RSA\\");
479 wcscat(ret
, prov
->pwszContainerName
);
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
);
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
;
502 if (!(path
= get_key_container_path(ctx
))) return NULL
;
503 retval
= RegOpenKeyExW(HKEY_CURRENT_USER
, path
, 0, KEY_READ
, &hkey
);
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
;
516 if (!(buf
= malloc(len
+ MAX_LEAD_BYTES
)))
522 if (!RegQueryValueExW(hkey
, (spec
== AT_KEYEXCHANGE
) ? L
"KeyExchangeKeyPair" : L
"SignatureKeyPair", 0,
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
;
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
;
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
;
554 TRACE("fCredentialUse %#lx, schanCred %p, phCredential %p, ptsExpiry %p\n", fCredentialUse
, schanCred
, phCredential
, ptsExpiry
);
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
)
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
;
571 else if (fCredentialUse
& SECPKG_CRED_INBOUND
)
573 return SEC_E_NO_CREDENTIALS
;
577 if(schanCred
&& cred_enabled_protocols
)
578 enabled_protocols
= cred_enabled_protocols
& config_enabled_protocols
;
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
;
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
, ¶ms
);
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;
616 if (fCredentialUse
& SECPKG_CRED_INBOUND
)
618 /* FIXME: get expiry from cert */
622 /* Outbound credentials have no expiry */
623 ptsExpiry
->LowPart
= 0;
624 ptsExpiry
->HighPart
= 0;
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
, ¶ms
);
680 static int schan_find_sec_buffer_idx(const SecBufferDesc
*desc
, unsigned int start_idx
, ULONG buffer_type
)
685 for (i
= start_idx
; i
< desc
->cBuffers
; ++i
)
687 buffer
= &desc
->pBuffers
[i
];
688 if ((buffer
->BufferType
| SECBUFFER_ATTRMASK
) == (buffer_type
| SECBUFFER_ATTRMASK
))
695 static void dump_buffer_desc(SecBufferDesc
*desc
)
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");
727 SecBuffer
*buffer
= &input
->pBuffers
[idx
];
728 buffer
->BufferType
= SECBUFFER_MISSING
;
729 buffer
->cbBuffer
= size
;
733 static BOOL
validate_input_buffers(SecBufferDesc
*desc
)
736 for (i
= 0; i
< desc
->cBuffers
; i
++)
738 SecBuffer
*buffer
= &desc
->pBuffers
[i
];
739 if (buffer
->BufferType
== SECBUFFER_EMPTY
&& buffer
->cbBuffer
) return FALSE
;
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;
756 SecBuffer alloc_buffer
= { 0 };
757 struct handshake_params params
= { 0 };
758 int output_buffer_idx
= -1;
760 ULONG input_offset
= 0, output_offset
= 0;
761 SecBufferDesc input_desc
, output_desc
;
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
;
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
)
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
);
812 return SEC_E_INTERNAL_ERROR
;
815 if (cred
->enabled_protocols
& SP_PROT_DTLS1_X
)
816 ctx
->header_size
= HEADER_SIZE_DTLS
;
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
);
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
, ¶ms
);
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
, ¶ms
);
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
, ¶ms
);
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
, ¶ms
);
858 phNewContext
->dwLower
= handle
;
859 phNewContext
->dwUpper
= 0;
862 if (bIsServer
|| phContext
)
864 SIZE_T record_size
= 0;
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
;
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
);
922 idx
= schan_find_sec_buffer_idx(pOutput
, 0, SECBUFFER_EMPTY
);
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
, ¶ms
);
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
;
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;
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
;
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
;
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
);
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
);
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 {
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" },
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
);
1096 static SECURITY_STATUS
ensure_remote_cert(struct schan_context
*ctx
)
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
, ¶ms
);
1109 if (status
!= SEC_E_BUFFER_TOO_SMALL
) goto done
;
1110 if (!(params
.buffer
= malloc( size
)))
1112 status
= SEC_E_INSUFFICIENT_MEMORY
;
1115 status
= GNUTLS_CALL( get_session_peer_certificate
, ¶ms
);
1116 if (status
== SEC_E_OK
)
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();
1136 free(params
.buffer
);
1139 CertCloseStore(store
, 0);
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
;
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
, ¶ms
);
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
, ¶ms
);
1168 unsigned int message_size
= GNUTLS_CALL( get_max_message_size
, ¶ms
);
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
;
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
, ¶ms
);
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
, ¶ms
);
1194 info
->EncryptAlgorithm
= conn_info
.aiCipher
;
1195 info
->sSignatureAlgorithmName
= get_alg_name(info
->SignatureAlgorithm
, TRUE
);
1196 info
->sEncryptAlgorithmName
= get_alg_name(info
->EncryptAlgorithm
, TRUE
);
1200 case SECPKG_ATTR_REMOTE_CERT_CONTEXT
:
1202 PCCERT_CONTEXT
*cert
= buffer
;
1204 status
= ensure_remote_cert(ctx
);
1205 if(status
!= SEC_E_OK
)
1208 *cert
= CertDuplicateCertificateContext(ctx
->cert
);
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
, ¶ms
);
1217 case SECPKG_ATTR_ENDPOINT_BINDINGS
:
1219 SecPkgContext_Bindings
*bindings
= buffer
;
1220 CCRYPT_OID_INFO
*info
;
1221 ALG_ID hash_alg
= CALG_SHA_256
;
1227 static const char prefix
[] = "tls-server-end-point:";
1229 status
= ensure_remote_cert(ctx
);
1230 if(status
!= SEC_E_OK
)
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
);
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
);
1258 case SECPKG_ATTR_UNIQUE_BINDINGS
:
1260 static const char prefix
[] = "tls-unique:";
1261 SecPkgContext_Bindings
*bindings
= buffer
;
1264 struct get_unique_channel_binding_params params
= { ctx
->session
, NULL
, &size
};
1266 if (GNUTLS_CALL( get_unique_channel_binding
, ¶ms
) != 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;
1282 return GNUTLS_CALL( get_unique_channel_binding
, ¶ms
);
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
, ¶ms
);
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
, ¶ms
);
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
);
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
);
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
);
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
;
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
);
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
];
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
, ¶ms
);
1415 message
->pBuffers
[buffer_index
[output_buffer_idx
]].cbBuffer
= output_offset
;
1419 TRACE("Returning %#lx.\n", status
);
1424 static int schan_validate_decrypt_buffer_desc(PSecBufferDesc message
)
1427 unsigned int empty_count
= 0;
1430 if (message
->cBuffers
< 4)
1432 WARN("Less than four buffers passed\n");
1436 for (i
= 0; i
< message
->cBuffers
; ++i
)
1438 SecBuffer
*b
= &message
->pBuffers
[i
];
1439 if (b
->BufferType
== SECBUFFER_DATA
)
1443 WARN("More than one data buffer passed\n");
1448 else if (b
->BufferType
== SECBUFFER_EMPTY
)
1454 WARN("No data buffer passed\n");
1458 if (empty_count
< 3)
1460 WARN("Less than three empty buffers passed\n");
1467 static void schan_decrypt_fill_buffer(PSecBufferDesc message
, ULONG buffer_type
, void *data
, ULONG size
)
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
;
1489 unsigned expected_size
;
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
);
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
, ¶ms
);
1541 if (status
!= SEC_E_OK
&& status
!= SEC_I_RENEGOTIATE
)
1544 ERR("Returning %lx\n", status
);
1548 TRACE("Received %lu bytes\n", received
);
1550 memcpy(buf_ptr
+ ctx
->header_size
, data
, received
);
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
;
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
, ¶ms
);
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
;
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 */
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 */
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
|
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" );
1703 schan_handle_table
= malloc(64 * sizeof(*schan_handle_table
));
1704 if (!schan_handle_table
)
1706 ERR("Failed to allocate schannel handle table.\n");
1709 schan_handle_table_size
= 64;
1711 provider
= SECUR32_addProvider(&schanTableA
, &schanTableW
, L
"schannel.dll");
1714 ERR("Failed to add schannel provider.\n");
1718 SECUR32_addPackages(provider
, ARRAY_SIZE(info
), NULL
, info
);
1722 free(schan_handle_table
);
1723 schan_handle_table
= NULL
;
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. */
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
, ¶ms
);
1745 i
= schan_handle_count
;
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
, ¶ms
);
1756 free(schan_handle_table
);
1757 GNUTLS_CALL( process_detach
, NULL
);