xref: /reactos/dll/win32/schannel/schannel_wine.c (revision c2c66aff)
1 /* Copyright (C) 2005 Juan Lang
2  * Copyright 2008 Henri Verbeet
3  *
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.
8  *
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.
13  *
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
17  *
18  * This file implements the schannel provider, or, the SSL/TLS implementations.
19  */
20 
21 #include "precomp.h"
22 
23 #include <wine/config.h>
24 
25 #if defined(SONAME_LIBGNUTLS) || defined (HAVE_SECURITY_SECURITY_H) || defined (SONAME_LIBMBEDTLS)
26 
27 #define SCHAN_INVALID_HANDLE ~0UL
28 
29 enum schan_handle_type
30 {
31     SCHAN_HANDLE_CRED,
32     SCHAN_HANDLE_CTX,
33     SCHAN_HANDLE_FREE
34 };
35 
36 struct schan_handle
37 {
38     void *object;
39     enum schan_handle_type type;
40 };
41 
42 struct schan_context
43 {
44     schan_imp_session session;
45     ULONG req_ctx_attr;
46     const CERT_CONTEXT *cert;
47 };
48 
49 static struct schan_handle *schan_handle_table;
50 static struct schan_handle *schan_free_handles;
51 static SIZE_T schan_handle_table_size;
52 static SIZE_T schan_handle_count;
53 
54 /* Protocols enabled, only those may be used for the connection. */
55 static DWORD config_enabled_protocols;
56 
57 /* Protocols disabled by default. They are enabled for using, but disabled when caller asks for default settings. */
58 static DWORD config_default_disabled_protocols;
59 
60 static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type)
61 {
62     struct schan_handle *handle;
63 
64     if (schan_free_handles)
65     {
66         DWORD index = schan_free_handles - schan_handle_table;
67         /* Use a free handle */
68         handle = schan_free_handles;
69         if (handle->type != SCHAN_HANDLE_FREE)
70         {
71             ERR("Handle %d(%p) is in the free list, but has type %#x.\n", index, handle, handle->type);
72             return SCHAN_INVALID_HANDLE;
73         }
74         schan_free_handles = handle->object;
75         handle->object = object;
76         handle->type = type;
77 
78         return index;
79     }
80     if (!(schan_handle_count < schan_handle_table_size))
81     {
82         /* Grow the table */
83         SIZE_T new_size = schan_handle_table_size + (schan_handle_table_size >> 1);
84         struct schan_handle *new_table = HeapReAlloc(GetProcessHeap(), 0, schan_handle_table, new_size * sizeof(*schan_handle_table));
85         if (!new_table)
86         {
87             ERR("Failed to grow the handle table\n");
88             return SCHAN_INVALID_HANDLE;
89         }
90         schan_handle_table = new_table;
91         schan_handle_table_size = new_size;
92     }
93 
94     handle = &schan_handle_table[schan_handle_count++];
95     handle->object = object;
96     handle->type = type;
97 
98     return handle - schan_handle_table;
99 }
100 
101 static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type)
102 {
103     struct schan_handle *handle;
104     void *object;
105 
106     if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
107     if (handle_idx >= schan_handle_count) return NULL;
108     handle = &schan_handle_table[handle_idx];
109     if (handle->type != type)
110     {
111         ERR("Handle %ld(%p) is not of type %#x\n", handle_idx, handle, type);
112         return NULL;
113     }
114 
115     object = handle->object;
116     handle->object = schan_free_handles;
117     handle->type = SCHAN_HANDLE_FREE;
118     schan_free_handles = handle;
119 
120     return object;
121 }
122 
123 static void *schan_get_object(ULONG_PTR handle_idx, enum schan_handle_type type)
124 {
125     struct schan_handle *handle;
126 
127     if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
128     if (handle_idx >= schan_handle_count) return NULL;
129     handle = &schan_handle_table[handle_idx];
130     if (handle->type != type)
131     {
132         ERR("Handle %ld(%p) is not of type %#x\n", handle_idx, handle, type);
133         return NULL;
134     }
135 
136     return handle->object;
137 }
138 
139 static void read_config(void)
140 {
141     DWORD enabled = 0, default_disabled = 0;
142     HKEY protocols_key, key;
143     WCHAR subkey_name[64];
144     unsigned i;
145     DWORD res;
146 
147     static BOOL config_read = FALSE;
148 
149     static const WCHAR protocol_config_key_name[] = {
150         'S','Y','S','T','E','M','\\',
151         'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
152         'C','o','n','t','r','o','l','\\',
153         'S','e','c','u','r','i','t','y','P','r','o','v','i','d','e','r','s','\\',
154         'S','C','H','A','N','N','E','L','\\',
155         'P','r','o','t','o','c','o','l','s',0 };
156 
157     static const WCHAR clientW[] = {'\\','C','l','i','e','n','t',0};
158     static const WCHAR enabledW[] = {'e','n','a','b','l','e','d',0};
159     static const WCHAR disabledbydefaultW[] = {'D','i','s','a','b','l','e','d','B','y','D','e','f','a','u','l','t',0};
160 
161     static const struct {
162         WCHAR key_name[20];
163         DWORD prot_client_flag;
164         BOOL enabled; /* If no config is present, enable the protocol */
165         BOOL disabled_by_default; /* Disable if caller asks for default protocol set */
166     } protocol_config_keys[] = {
167         {{'S','S','L',' ','2','.','0',0}, SP_PROT_SSL2_CLIENT, FALSE, TRUE}, /* NOTE: TRUE, TRUE on Windows */
168         {{'S','S','L',' ','3','.','0',0}, SP_PROT_SSL3_CLIENT, TRUE, FALSE},
169         {{'T','L','S',' ','1','.','0',0}, SP_PROT_TLS1_0_CLIENT, TRUE, FALSE},
170         {{'T','L','S',' ','1','.','1',0}, SP_PROT_TLS1_1_CLIENT, TRUE, FALSE /* NOTE: not enabled by default on Windows */ },
171         {{'T','L','S',' ','1','.','2',0}, SP_PROT_TLS1_2_CLIENT, TRUE, FALSE /* NOTE: not enabled by default on Windows */ }
172     };
173 
174     /* No need for thread safety */
175     if(config_read)
176         return;
177 
178     res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, protocol_config_key_name, 0, KEY_READ, &protocols_key);
179     if(res == ERROR_SUCCESS) {
180         DWORD type, size, value;
181 
182         for(i=0; i < sizeof(protocol_config_keys)/sizeof(*protocol_config_keys); i++) {
183             strcpyW(subkey_name, protocol_config_keys[i].key_name);
184             strcatW(subkey_name, clientW);
185             res = RegOpenKeyExW(protocols_key, subkey_name, 0, KEY_READ, &key);
186             if(res != ERROR_SUCCESS) {
187                 if(protocol_config_keys[i].enabled)
188                     enabled |= protocol_config_keys[i].prot_client_flag;
189                 if(protocol_config_keys[i].disabled_by_default)
190                     default_disabled |= protocol_config_keys[i].prot_client_flag;
191                 continue;
192             }
193 
194             size = sizeof(value);
195             res = RegQueryValueExW(key, enabledW, NULL, &type, (BYTE*)&value, &size);
196             if(res == ERROR_SUCCESS) {
197                 if(type == REG_DWORD && value)
198                     enabled |= protocol_config_keys[i].prot_client_flag;
199             }else if(protocol_config_keys[i].enabled) {
200                 enabled |= protocol_config_keys[i].prot_client_flag;
201             }
202 
203             size = sizeof(value);
204             res = RegQueryValueExW(key, disabledbydefaultW, NULL, &type, (BYTE*)&value, &size);
205             if(res == ERROR_SUCCESS) {
206                 if(type != REG_DWORD || value)
207                     default_disabled |= protocol_config_keys[i].prot_client_flag;
208             }else if(protocol_config_keys[i].disabled_by_default) {
209                 default_disabled |= protocol_config_keys[i].prot_client_flag;
210             }
211 
212             RegCloseKey(key);
213         }
214     }else {
215         /* No config, enable all known protocols. */
216         for(i=0; i < sizeof(protocol_config_keys)/sizeof(*protocol_config_keys); i++) {
217             if(protocol_config_keys[i].enabled)
218                 enabled |= protocol_config_keys[i].prot_client_flag;
219             if(protocol_config_keys[i].disabled_by_default)
220                 default_disabled |= protocol_config_keys[i].prot_client_flag;
221         }
222     }
223 
224     RegCloseKey(protocols_key);
225 
226     config_enabled_protocols = enabled & schan_imp_enabled_protocols();
227     config_default_disabled_protocols = default_disabled;
228     config_read = TRUE;
229 
230     TRACE("enabled %x, disabled by default %x\n", config_enabled_protocols, config_default_disabled_protocols);
231 }
232 
233 static SECURITY_STATUS schan_QueryCredentialsAttributes(
234  PCredHandle phCredential, ULONG ulAttribute, VOID *pBuffer)
235 {
236     struct schan_credentials *cred;
237     SECURITY_STATUS ret;
238 
239     cred = schan_get_object(phCredential->dwLower, SCHAN_HANDLE_CRED);
240     if(!cred)
241         return SEC_E_INVALID_HANDLE;
242 
243     switch (ulAttribute)
244     {
245     case SECPKG_ATTR_SUPPORTED_ALGS:
246         if (pBuffer)
247         {
248             /* FIXME: get from CryptoAPI */
249             FIXME("SECPKG_ATTR_SUPPORTED_ALGS: stub\n");
250             ret = SEC_E_UNSUPPORTED_FUNCTION;
251         }
252         else
253             ret = SEC_E_INTERNAL_ERROR;
254         break;
255     case SECPKG_ATTR_CIPHER_STRENGTHS:
256         if (pBuffer)
257         {
258             SecPkgCred_CipherStrengths *r = pBuffer;
259 
260             /* FIXME: get from CryptoAPI */
261             FIXME("SECPKG_ATTR_CIPHER_STRENGTHS: semi-stub\n");
262             r->dwMinimumCipherStrength = 40;
263             r->dwMaximumCipherStrength = 168;
264             ret = SEC_E_OK;
265         }
266         else
267             ret = SEC_E_INTERNAL_ERROR;
268         break;
269     case SECPKG_ATTR_SUPPORTED_PROTOCOLS:
270         if(pBuffer) {
271             /* Regardless of MSDN documentation, tests show that this attribute takes into account
272              * what protocols are enabled for given credential. */
273             ((SecPkgCred_SupportedProtocols*)pBuffer)->grbitProtocol = cred->enabled_protocols;
274             ret = SEC_E_OK;
275         }else {
276             ret = SEC_E_INTERNAL_ERROR;
277         }
278         break;
279     default:
280         ret = SEC_E_UNSUPPORTED_FUNCTION;
281     }
282     return ret;
283 }
284 
285 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesA(
286  PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
287 {
288     SECURITY_STATUS ret;
289 
290     TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
291 
292     switch (ulAttribute)
293     {
294     case SECPKG_CRED_ATTR_NAMES:
295         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
296         ret = SEC_E_UNSUPPORTED_FUNCTION;
297         break;
298     default:
299         ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
300          pBuffer);
301     }
302     return ret;
303 }
304 
305 SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesW(
306  PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
307 {
308     SECURITY_STATUS ret;
309 
310     TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
311 
312     switch (ulAttribute)
313     {
314     case SECPKG_CRED_ATTR_NAMES:
315         FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
316         ret = SEC_E_UNSUPPORTED_FUNCTION;
317         break;
318     default:
319         ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
320          pBuffer);
321     }
322     return ret;
323 }
324 
325 static SECURITY_STATUS schan_CheckCreds(const SCHANNEL_CRED *schanCred)
326 {
327     SECURITY_STATUS st;
328     DWORD i;
329 
330     TRACE("dwVersion = %d\n", schanCred->dwVersion);
331     TRACE("cCreds = %d\n", schanCred->cCreds);
332     TRACE("hRootStore = %p\n", schanCred->hRootStore);
333     TRACE("cMappers = %d\n", schanCred->cMappers);
334     TRACE("cSupportedAlgs = %d:\n", schanCred->cSupportedAlgs);
335     for (i = 0; i < schanCred->cSupportedAlgs; i++)
336         TRACE("%08x\n", schanCred->palgSupportedAlgs[i]);
337     TRACE("grbitEnabledProtocols = %08x\n", schanCred->grbitEnabledProtocols);
338     TRACE("dwMinimumCipherStrength = %d\n", schanCred->dwMinimumCipherStrength);
339     TRACE("dwMaximumCipherStrength = %d\n", schanCred->dwMaximumCipherStrength);
340     TRACE("dwSessionLifespan = %d\n", schanCred->dwSessionLifespan);
341     TRACE("dwFlags = %08x\n", schanCred->dwFlags);
342     TRACE("dwCredFormat = %d\n", schanCred->dwCredFormat);
343 
344     switch (schanCred->dwVersion)
345     {
346     case SCH_CRED_V3:
347     case SCHANNEL_CRED_VERSION:
348         break;
349     default:
350         return SEC_E_INTERNAL_ERROR;
351     }
352 
353     if (schanCred->cCreds == 0)
354         st = SEC_E_NO_CREDENTIALS;
355     else if (schanCred->cCreds > 1)
356         st = SEC_E_UNKNOWN_CREDENTIALS;
357     else
358     {
359         DWORD keySpec;
360         HCRYPTPROV csp;
361         BOOL ret, freeCSP;
362 
363         ret = CryptAcquireCertificatePrivateKey(schanCred->paCred[0],
364          0, /* FIXME: what flags to use? */ NULL,
365          &csp, &keySpec, &freeCSP);
366         if (ret)
367         {
368             st = SEC_E_OK;
369             if (freeCSP)
370                 CryptReleaseContext(csp, 0);
371         }
372         else
373             st = SEC_E_UNKNOWN_CREDENTIALS;
374     }
375     return st;
376 }
377 
378 static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schanCred,
379  PCredHandle phCredential, PTimeStamp ptsExpiry)
380 {
381     struct schan_credentials *creds;
382     unsigned enabled_protocols;
383     ULONG_PTR handle;
384     SECURITY_STATUS st = SEC_E_OK;
385 
386     TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
387 
388     if (schanCred)
389     {
390         st = schan_CheckCreds(schanCred);
391         if (st != SEC_E_OK && st != SEC_E_NO_CREDENTIALS)
392             return st;
393 
394         st = SEC_E_OK;
395     }
396 
397     read_config();
398     if(schanCred && schanCred->grbitEnabledProtocols)
399         enabled_protocols = schanCred->grbitEnabledProtocols & config_enabled_protocols;
400     else
401         enabled_protocols = config_enabled_protocols & ~config_default_disabled_protocols;
402     if(!enabled_protocols) {
403         ERR("Could not find matching protocol\n");
404         return SEC_E_NO_AUTHENTICATING_AUTHORITY;
405     }
406 
407     /* For now, the only thing I'm interested in is the direction of the
408      * connection, so just store it.
409      */
410     creds = HeapAlloc(GetProcessHeap(), 0, sizeof(*creds));
411     if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
412 
413     handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
414     if (handle == SCHAN_INVALID_HANDLE) goto fail;
415 
416     creds->credential_use = SECPKG_CRED_OUTBOUND;
417     if (!schan_imp_allocate_certificate_credentials(creds))
418     {
419         schan_free_handle(handle, SCHAN_HANDLE_CRED);
420         goto fail;
421     }
422 
423     creds->enabled_protocols = enabled_protocols;
424     phCredential->dwLower = handle;
425     phCredential->dwUpper = 0;
426 
427     /* Outbound credentials have no expiry */
428     if (ptsExpiry)
429     {
430         ptsExpiry->LowPart = 0;
431         ptsExpiry->HighPart = 0;
432     }
433 
434     return st;
435 
436 fail:
437     HeapFree(GetProcessHeap(), 0, creds);
438     return SEC_E_INTERNAL_ERROR;
439 }
440 
441 static SECURITY_STATUS schan_AcquireServerCredentials(const SCHANNEL_CRED *schanCred,
442  PCredHandle phCredential, PTimeStamp ptsExpiry)
443 {
444     SECURITY_STATUS st;
445 
446     TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
447 
448     if (!schanCred) return SEC_E_NO_CREDENTIALS;
449 
450     st = schan_CheckCreds(schanCred);
451     if (st == SEC_E_OK)
452     {
453         ULONG_PTR handle;
454         struct schan_credentials *creds;
455 
456         creds = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*creds));
457         if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
458         creds->credential_use = SECPKG_CRED_INBOUND;
459 
460         handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
461         if (handle == SCHAN_INVALID_HANDLE)
462         {
463             HeapFree(GetProcessHeap(), 0, creds);
464             return SEC_E_INTERNAL_ERROR;
465         }
466 
467         phCredential->dwLower = handle;
468         phCredential->dwUpper = 0;
469 
470         /* FIXME: get expiry from cert */
471     }
472     return st;
473 }
474 
475 static SECURITY_STATUS schan_AcquireCredentialsHandle(ULONG fCredentialUse,
476  const SCHANNEL_CRED *schanCred, PCredHandle phCredential, PTimeStamp ptsExpiry)
477 {
478     SECURITY_STATUS ret;
479 
480     if (fCredentialUse == SECPKG_CRED_OUTBOUND)
481         ret = schan_AcquireClientCredentials(schanCred, phCredential,
482          ptsExpiry);
483     else
484         ret = schan_AcquireServerCredentials(schanCred, phCredential,
485          ptsExpiry);
486     return ret;
487 }
488 
489 SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleA(
490  SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
491  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
492  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
493 {
494     TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
495      debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
496      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
497     return schan_AcquireCredentialsHandle(fCredentialUse,
498      pAuthData, phCredential, ptsExpiry);
499 }
500 
501 SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleW(
502  SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
503  PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
504  PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
505 {
506     TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
507      debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
508      pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
509     return schan_AcquireCredentialsHandle(fCredentialUse,
510      pAuthData, phCredential, ptsExpiry);
511 }
512 
513 SECURITY_STATUS SEC_ENTRY schan_FreeCredentialsHandle(
514  PCredHandle phCredential)
515 {
516     struct schan_credentials *creds;
517 
518     TRACE("phCredential %p\n", phCredential);
519 
520     if (!phCredential) return SEC_E_INVALID_HANDLE;
521 
522     creds = schan_free_handle(phCredential->dwLower, SCHAN_HANDLE_CRED);
523     if (!creds) return SEC_E_INVALID_HANDLE;
524 
525     if (creds->credential_use == SECPKG_CRED_OUTBOUND)
526         schan_imp_free_certificate_credentials(creds);
527     HeapFree(GetProcessHeap(), 0, creds);
528 
529     return SEC_E_OK;
530 }
531 
532 static void init_schan_buffers(struct schan_buffers *s, const PSecBufferDesc desc,
533         int (*get_next_buffer)(const struct schan_transport *, struct schan_buffers *))
534 {
535     s->offset = 0;
536     s->limit = ~0UL;
537     s->desc = desc;
538     s->current_buffer_idx = -1;
539     s->allow_buffer_resize = FALSE;
540     s->get_next_buffer = get_next_buffer;
541 }
542 
543 static int schan_find_sec_buffer_idx(const SecBufferDesc *desc, unsigned int start_idx, ULONG buffer_type)
544 {
545     unsigned int i;
546     PSecBuffer buffer;
547 
548     for (i = start_idx; i < desc->cBuffers; ++i)
549     {
550         buffer = &desc->pBuffers[i];
551         if (buffer->BufferType == buffer_type) return i;
552     }
553 
554     return -1;
555 }
556 
557 static void schan_resize_current_buffer(const struct schan_buffers *s, SIZE_T min_size)
558 {
559     SecBuffer *b = &s->desc->pBuffers[s->current_buffer_idx];
560     SIZE_T new_size = b->cbBuffer ? b->cbBuffer * 2 : 128;
561     void *new_data;
562 
563     if (b->cbBuffer >= min_size || !s->allow_buffer_resize || min_size > UINT_MAX / 2) return;
564 
565     while (new_size < min_size) new_size *= 2;
566 
567     if (b->pvBuffer)
568         new_data = HeapReAlloc(GetProcessHeap(), 0, b->pvBuffer, new_size);
569     else
570         new_data = HeapAlloc(GetProcessHeap(), 0, new_size);
571 
572     if (!new_data)
573     {
574         TRACE("Failed to resize %p from %d to %ld\n", b->pvBuffer, b->cbBuffer, new_size);
575         return;
576     }
577 
578     b->cbBuffer = new_size;
579     b->pvBuffer = new_data;
580 }
581 
582 char *schan_get_buffer(const struct schan_transport *t, struct schan_buffers *s, SIZE_T *count)
583 {
584     SIZE_T max_count;
585     PSecBuffer buffer;
586 
587     if (!s->desc)
588     {
589         TRACE("No desc\n");
590         return NULL;
591     }
592 
593     if (s->current_buffer_idx == -1)
594     {
595         /* Initial buffer */
596         int buffer_idx = s->get_next_buffer(t, s);
597         if (buffer_idx == -1)
598         {
599             TRACE("No next buffer\n");
600             return NULL;
601         }
602         s->current_buffer_idx = buffer_idx;
603     }
604 
605     buffer = &s->desc->pBuffers[s->current_buffer_idx];
606     TRACE("Using buffer %d: cbBuffer %d, BufferType %#x, pvBuffer %p\n", s->current_buffer_idx, buffer->cbBuffer, buffer->BufferType, buffer->pvBuffer);
607 
608     schan_resize_current_buffer(s, s->offset + *count);
609     max_count = buffer->cbBuffer - s->offset;
610     if (s->limit != ~0UL && s->limit < max_count)
611         max_count = s->limit;
612     if (!max_count)
613     {
614         int buffer_idx;
615 
616         s->allow_buffer_resize = FALSE;
617         buffer_idx = s->get_next_buffer(t, s);
618         if (buffer_idx == -1)
619         {
620             TRACE("No next buffer\n");
621             return NULL;
622         }
623         s->current_buffer_idx = buffer_idx;
624         s->offset = 0;
625         return schan_get_buffer(t, s, count);
626     }
627 
628     if (*count > max_count)
629         *count = max_count;
630     if (s->limit != ~0UL)
631         s->limit -= *count;
632 
633     return (char *)buffer->pvBuffer + s->offset;
634 }
635 
636 /* schan_pull
637  *      Read data from the transport input buffer.
638  *
639  * t - The session transport object.
640  * buff - The buffer into which to store the read data.  Must be at least
641  *        *buff_len bytes in length.
642  * buff_len - On input, *buff_len is the desired length to read.  On successful
643  *            return, *buff_len is the number of bytes actually read.
644  *
645  * Returns:
646  *  0 on success, in which case:
647  *      *buff_len == 0 indicates end of file.
648  *      *buff_len > 0 indicates that some data was read.  May be less than
649  *          what was requested, in which case the caller should call again if/
650  *          when they want more.
651  *  EAGAIN when no data could be read without blocking
652  *  another errno-style error value on failure
653  *
654  */
655 int schan_pull(struct schan_transport *t, void *buff, size_t *buff_len)
656 {
657     char *b;
658     SIZE_T local_len = *buff_len;
659 
660     TRACE("Pull %lu bytes\n", local_len);
661 
662     *buff_len = 0;
663 
664     b = schan_get_buffer(t, &t->in, &local_len);
665     if (!b)
666         return EAGAIN;
667 
668     memcpy(buff, b, local_len);
669     t->in.offset += local_len;
670 
671     TRACE("Read %lu bytes\n", local_len);
672 
673     *buff_len = local_len;
674     return 0;
675 }
676 
677 /* schan_push
678  *      Write data to the transport output buffer.
679  *
680  * t - The session transport object.
681  * buff - The buffer of data to write.  Must be at least *buff_len bytes in length.
682  * buff_len - On input, *buff_len is the desired length to write.  On successful
683  *            return, *buff_len is the number of bytes actually written.
684  *
685  * Returns:
686  *  0 on success
687  *      *buff_len will be > 0 indicating how much data was written.  May be less
688  *          than what was requested, in which case the caller should call again
689             if/when they want to write more.
690  *  EAGAIN when no data could be written without blocking
691  *  another errno-style error value on failure
692  *
693  */
694 int schan_push(struct schan_transport *t, const void *buff, size_t *buff_len)
695 {
696     char *b;
697     SIZE_T local_len = *buff_len;
698 
699     TRACE("Push %lu bytes\n", local_len);
700 
701     *buff_len = 0;
702 
703     b = schan_get_buffer(t, &t->out, &local_len);
704     if (!b)
705         return EAGAIN;
706 
707     memcpy(b, buff, local_len);
708     t->out.offset += local_len;
709 
710     TRACE("Wrote %lu bytes\n", local_len);
711 
712     *buff_len = local_len;
713     return 0;
714 }
715 
716 schan_imp_session schan_session_for_transport(struct schan_transport* t)
717 {
718     return t->ctx->session;
719 }
720 
721 static int schan_init_sec_ctx_get_next_input_buffer(const struct schan_transport *t, struct schan_buffers *s)
722 {
723     if (s->current_buffer_idx != -1)
724         return -1;
725     return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
726 }
727 
728 static int schan_init_sec_ctx_get_next_output_buffer(const struct schan_transport *t, struct schan_buffers *s)
729 {
730     if (s->current_buffer_idx == -1)
731     {
732         int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
733         if (t->ctx->req_ctx_attr & ISC_REQ_ALLOCATE_MEMORY)
734         {
735             if (idx == -1)
736             {
737                 idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_EMPTY);
738                 if (idx != -1) s->desc->pBuffers[idx].BufferType = SECBUFFER_TOKEN;
739             }
740             if (idx != -1 && !s->desc->pBuffers[idx].pvBuffer)
741             {
742                 s->desc->pBuffers[idx].cbBuffer = 0;
743                 s->allow_buffer_resize = TRUE;
744             }
745         }
746         return idx;
747     }
748 
749     return -1;
750 }
751 
752 static void dump_buffer_desc(SecBufferDesc *desc)
753 {
754     unsigned int i;
755 
756     if (!desc) return;
757     TRACE("Buffer desc %p:\n", desc);
758     for (i = 0; i < desc->cBuffers; ++i)
759     {
760         SecBuffer *b = &desc->pBuffers[i];
761         TRACE("\tbuffer %u: cbBuffer %d, BufferType %#x pvBuffer %p\n", i, b->cbBuffer, b->BufferType, b->pvBuffer);
762     }
763 }
764 
765 /***********************************************************************
766  *              InitializeSecurityContextW
767  */
768 SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
769  PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
770  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
771  PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
772  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
773 {
774     struct schan_context *ctx;
775     struct schan_buffers *out_buffers;
776     struct schan_credentials *cred;
777     struct schan_transport transport;
778     SIZE_T expected_size = ~0UL;
779     SECURITY_STATUS ret;
780 
781     TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext,
782      debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
783      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
784 
785     dump_buffer_desc(pInput);
786     dump_buffer_desc(pOutput);
787 
788     if (!phContext)
789     {
790         ULONG_PTR handle;
791 
792         if (!phCredential) return SEC_E_INVALID_HANDLE;
793 
794         cred = schan_get_object(phCredential->dwLower, SCHAN_HANDLE_CRED);
795         if (!cred) return SEC_E_INVALID_HANDLE;
796 
797         if (!(cred->credential_use & SECPKG_CRED_OUTBOUND))
798         {
799             WARN("Invalid credential use %#x\n", cred->credential_use);
800             return SEC_E_INVALID_HANDLE;
801         }
802 
803         ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(*ctx));
804         if (!ctx) return SEC_E_INSUFFICIENT_MEMORY;
805 
806         ctx->cert = NULL;
807         handle = schan_alloc_handle(ctx, SCHAN_HANDLE_CTX);
808         if (handle == SCHAN_INVALID_HANDLE)
809         {
810             HeapFree(GetProcessHeap(), 0, ctx);
811             return SEC_E_INTERNAL_ERROR;
812         }
813 
814         if (!schan_imp_create_session(&ctx->session, cred))
815         {
816             schan_free_handle(handle, SCHAN_HANDLE_CTX);
817             HeapFree(GetProcessHeap(), 0, ctx);
818             return SEC_E_INTERNAL_ERROR;
819         }
820 
821         if (pszTargetName)
822         {
823             UINT len = WideCharToMultiByte( CP_UNIXCP, 0, pszTargetName, -1, NULL, 0, NULL, NULL );
824             char *target = HeapAlloc( GetProcessHeap(), 0, len );
825 
826             if (target)
827             {
828                 WideCharToMultiByte( CP_UNIXCP, 0, pszTargetName, -1, target, len, NULL, NULL );
829                 schan_imp_set_session_target( ctx->session, target );
830                 HeapFree( GetProcessHeap(), 0, target );
831             }
832         }
833         phNewContext->dwLower = handle;
834         phNewContext->dwUpper = 0;
835     }
836     else
837     {
838         SIZE_T record_size = 0;
839         unsigned char *ptr;
840         SecBuffer *buffer;
841         int idx;
842 
843         if (!pInput)
844             return SEC_E_INCOMPLETE_MESSAGE;
845 
846         idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN);
847         if (idx == -1)
848             return SEC_E_INCOMPLETE_MESSAGE;
849 
850         buffer = &pInput->pBuffers[idx];
851         ptr = buffer->pvBuffer;
852         expected_size = 0;
853 
854         while (buffer->cbBuffer > expected_size + 5)
855         {
856             record_size = 5 + ((ptr[3] << 8) | ptr[4]);
857 
858             if (buffer->cbBuffer < expected_size + record_size)
859                 break;
860 
861             expected_size += record_size;
862             ptr += record_size;
863         }
864 
865         if (!expected_size)
866         {
867             TRACE("Expected at least %lu bytes, but buffer only contains %u bytes.\n",
868                     max(6, record_size), buffer->cbBuffer);
869             return SEC_E_INCOMPLETE_MESSAGE;
870         }
871 
872         TRACE("Using expected_size %lu.\n", expected_size);
873 
874         ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX);
875     }
876 
877     ctx->req_ctx_attr = fContextReq;
878 
879     transport.ctx = ctx;
880     init_schan_buffers(&transport.in, pInput, schan_init_sec_ctx_get_next_input_buffer);
881     transport.in.limit = expected_size;
882     init_schan_buffers(&transport.out, pOutput, schan_init_sec_ctx_get_next_output_buffer);
883     schan_imp_set_session_transport(ctx->session, &transport);
884 
885     /* Perform the TLS handshake */
886     ret = schan_imp_handshake(ctx->session);
887 
888     if(transport.in.offset && transport.in.offset != pInput->pBuffers[0].cbBuffer) {
889         if(pInput->cBuffers<2 || pInput->pBuffers[1].BufferType!=SECBUFFER_EMPTY)
890             return SEC_E_INVALID_TOKEN;
891 
892         pInput->pBuffers[1].BufferType = SECBUFFER_EXTRA;
893         pInput->pBuffers[1].cbBuffer = pInput->pBuffers[0].cbBuffer-transport.in.offset;
894     }
895 
896     out_buffers = &transport.out;
897     if (out_buffers->current_buffer_idx != -1)
898     {
899         SecBuffer *buffer = &out_buffers->desc->pBuffers[out_buffers->current_buffer_idx];
900         buffer->cbBuffer = out_buffers->offset;
901     }
902 
903     *pfContextAttr = 0;
904     if (ctx->req_ctx_attr & ISC_REQ_ALLOCATE_MEMORY)
905         *pfContextAttr |= ISC_RET_ALLOCATED_MEMORY;
906 
907     return ret;
908 }
909 
910 /***********************************************************************
911  *              InitializeSecurityContextA
912  */
913 SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextA(
914  PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
915  ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
916  PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
917  PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
918 {
919     SECURITY_STATUS ret;
920     SEC_WCHAR *target_name = NULL;
921 
922     TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
923      debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
924      Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
925 
926     if (pszTargetName)
927     {
928         INT len = MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, NULL, 0);
929         target_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(*target_name));
930         MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, target_name, len);
931     }
932 
933     ret = schan_InitializeSecurityContextW(phCredential, phContext, target_name,
934             fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
935             phNewContext, pOutput, pfContextAttr, ptsExpiry);
936 
937     HeapFree(GetProcessHeap(), 0, target_name);
938 
939     return ret;
940 }
941 
942 static
943 SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
944         PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
945 {
946     struct schan_context *ctx;
947 
948     TRACE("context_handle %p, attribute %#x, buffer %p\n",
949             context_handle, attribute, buffer);
950 
951     if (!context_handle) return SEC_E_INVALID_HANDLE;
952     ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
953 
954     switch(attribute)
955     {
956         case SECPKG_ATTR_STREAM_SIZES:
957         {
958             SecPkgContext_ConnectionInfo info;
959             SECURITY_STATUS status = schan_imp_get_connection_info(ctx->session, &info);
960             if (status == SEC_E_OK)
961             {
962                 SecPkgContext_StreamSizes *stream_sizes = buffer;
963                 SIZE_T mac_size = info.dwHashStrength;
964                 unsigned int block_size = schan_imp_get_session_cipher_block_size(ctx->session);
965                 unsigned int message_size = schan_imp_get_max_message_size(ctx->session);
966 
967                 TRACE("Using %lu mac bytes, message size %u, block size %u\n",
968                         mac_size, message_size, block_size);
969 
970                 /* These are defined by the TLS RFC */
971                 stream_sizes->cbHeader = 5;
972                 stream_sizes->cbTrailer = mac_size + 256; /* Max 255 bytes padding + 1 for padding size */
973                 stream_sizes->cbMaximumMessage = message_size;
974                 stream_sizes->cbBuffers = 4;
975                 stream_sizes->cbBlockSize = block_size;
976             }
977 
978             return status;
979         }
980         case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
981         {
982             PCCERT_CONTEXT *cert = buffer;
983 
984             if (!ctx->cert) {
985                 HCERTSTORE cert_store;
986                 SECURITY_STATUS status;
987 
988                 cert_store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
989                 if(!cert_store)
990                     return GetLastError();
991 
992                 status = schan_imp_get_session_peer_certificate(ctx->session, cert_store, &ctx->cert);
993                 CertCloseStore(cert_store, 0);
994                 if(status != SEC_E_OK)
995                     return status;
996             }
997 
998             *cert = CertDuplicateCertificateContext(ctx->cert);
999             return SEC_E_OK;
1000         }
1001         case SECPKG_ATTR_CONNECTION_INFO:
1002         {
1003             SecPkgContext_ConnectionInfo *info = buffer;
1004             return schan_imp_get_connection_info(ctx->session, info);
1005         }
1006 
1007         default:
1008             FIXME("Unhandled attribute %#x\n", attribute);
1009             return SEC_E_UNSUPPORTED_FUNCTION;
1010     }
1011 }
1012 
1013 static
1014 SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesA(
1015         PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
1016 {
1017     TRACE("context_handle %p, attribute %#x, buffer %p\n",
1018             context_handle, attribute, buffer);
1019 
1020     switch(attribute)
1021     {
1022         case SECPKG_ATTR_STREAM_SIZES:
1023             return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1024         case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
1025             return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1026         case SECPKG_ATTR_CONNECTION_INFO:
1027             return schan_QueryContextAttributesW(context_handle, attribute, buffer);
1028 
1029         default:
1030             FIXME("Unhandled attribute %#x\n", attribute);
1031             return SEC_E_UNSUPPORTED_FUNCTION;
1032     }
1033 }
1034 
1035 static int schan_encrypt_message_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
1036 {
1037     SecBuffer *b;
1038 
1039     if (s->current_buffer_idx == -1)
1040         return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_STREAM_HEADER);
1041 
1042     b = &s->desc->pBuffers[s->current_buffer_idx];
1043 
1044     if (b->BufferType == SECBUFFER_STREAM_HEADER)
1045         return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
1046 
1047     if (b->BufferType == SECBUFFER_DATA)
1048         return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_STREAM_TRAILER);
1049 
1050     return -1;
1051 }
1052 
1053 static int schan_encrypt_message_get_next_buffer_token(const struct schan_transport *t, struct schan_buffers *s)
1054 {
1055     SecBuffer *b;
1056 
1057     if (s->current_buffer_idx == -1)
1058         return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
1059 
1060     b = &s->desc->pBuffers[s->current_buffer_idx];
1061 
1062     if (b->BufferType == SECBUFFER_TOKEN)
1063     {
1064         int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
1065         if (idx != s->current_buffer_idx) return -1;
1066         return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
1067     }
1068 
1069     if (b->BufferType == SECBUFFER_DATA)
1070     {
1071         int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
1072         if (idx != -1)
1073             idx = schan_find_sec_buffer_idx(s->desc, idx + 1, SECBUFFER_TOKEN);
1074         return idx;
1075     }
1076 
1077     return -1;
1078 }
1079 
1080 static SECURITY_STATUS SEC_ENTRY schan_EncryptMessage(PCtxtHandle context_handle,
1081         ULONG quality, PSecBufferDesc message, ULONG message_seq_no)
1082 {
1083     struct schan_transport transport;
1084     struct schan_context *ctx;
1085     struct schan_buffers *b;
1086     SECURITY_STATUS status;
1087     SecBuffer *buffer;
1088     SIZE_T data_size;
1089     SIZE_T length;
1090     char *data;
1091     int idx;
1092 
1093     TRACE("context_handle %p, quality %d, message %p, message_seq_no %d\n",
1094             context_handle, quality, message, message_seq_no);
1095 
1096     if (!context_handle) return SEC_E_INVALID_HANDLE;
1097     ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
1098 
1099     dump_buffer_desc(message);
1100 
1101     idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_DATA);
1102     if (idx == -1)
1103     {
1104         WARN("No data buffer passed\n");
1105         return SEC_E_INTERNAL_ERROR;
1106     }
1107     buffer = &message->pBuffers[idx];
1108 
1109     data_size = buffer->cbBuffer;
1110     data = HeapAlloc(GetProcessHeap(), 0, data_size);
1111     memcpy(data, buffer->pvBuffer, data_size);
1112 
1113     transport.ctx = ctx;
1114     init_schan_buffers(&transport.in, NULL, NULL);
1115     if (schan_find_sec_buffer_idx(message, 0, SECBUFFER_STREAM_HEADER) != -1)
1116         init_schan_buffers(&transport.out, message, schan_encrypt_message_get_next_buffer);
1117     else
1118         init_schan_buffers(&transport.out, message, schan_encrypt_message_get_next_buffer_token);
1119     schan_imp_set_session_transport(ctx->session, &transport);
1120 
1121     length = data_size;
1122     status = schan_imp_send(ctx->session, data, &length);
1123 
1124     TRACE("Sent %ld bytes.\n", length);
1125 
1126     if (length != data_size)
1127         status = SEC_E_INTERNAL_ERROR;
1128 
1129     b = &transport.out;
1130     b->desc->pBuffers[b->current_buffer_idx].cbBuffer = b->offset;
1131     HeapFree(GetProcessHeap(), 0, data);
1132 
1133     TRACE("Returning %#x.\n", status);
1134 
1135     return status;
1136 }
1137 
1138 static int schan_decrypt_message_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
1139 {
1140     if (s->current_buffer_idx == -1)
1141         return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
1142 
1143     return -1;
1144 }
1145 
1146 static int schan_validate_decrypt_buffer_desc(PSecBufferDesc message)
1147 {
1148     int data_idx = -1;
1149     unsigned int empty_count = 0;
1150     unsigned int i;
1151 
1152     if (message->cBuffers < 4)
1153     {
1154         WARN("Less than four buffers passed\n");
1155         return -1;
1156     }
1157 
1158     for (i = 0; i < message->cBuffers; ++i)
1159     {
1160         SecBuffer *b = &message->pBuffers[i];
1161         if (b->BufferType == SECBUFFER_DATA)
1162         {
1163             if (data_idx != -1)
1164             {
1165                 WARN("More than one data buffer passed\n");
1166                 return -1;
1167             }
1168             data_idx = i;
1169         }
1170         else if (b->BufferType == SECBUFFER_EMPTY)
1171             ++empty_count;
1172     }
1173 
1174     if (data_idx == -1)
1175     {
1176         WARN("No data buffer passed\n");
1177         return -1;
1178     }
1179 
1180     if (empty_count < 3)
1181     {
1182         WARN("Less than three empty buffers passed\n");
1183         return -1;
1184     }
1185 
1186     return data_idx;
1187 }
1188 
1189 static void schan_decrypt_fill_buffer(PSecBufferDesc message, ULONG buffer_type, void *data, ULONG size)
1190 {
1191     int idx;
1192     SecBuffer *buffer;
1193 
1194     idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1195     buffer = &message->pBuffers[idx];
1196 
1197     buffer->BufferType = buffer_type;
1198     buffer->pvBuffer = data;
1199     buffer->cbBuffer = size;
1200 }
1201 
1202 static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle,
1203         PSecBufferDesc message, ULONG message_seq_no, PULONG quality)
1204 {
1205     struct schan_transport transport;
1206     struct schan_context *ctx;
1207     SecBuffer *buffer;
1208     SIZE_T data_size;
1209     char *data;
1210     unsigned expected_size;
1211     SSIZE_T received = 0;
1212     int idx;
1213     unsigned char *buf_ptr;
1214 
1215     TRACE("context_handle %p, message %p, message_seq_no %d, quality %p\n",
1216             context_handle, message, message_seq_no, quality);
1217 
1218     if (!context_handle) return SEC_E_INVALID_HANDLE;
1219     ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
1220 
1221     dump_buffer_desc(message);
1222 
1223     idx = schan_validate_decrypt_buffer_desc(message);
1224     if (idx == -1)
1225         return SEC_E_INVALID_TOKEN;
1226     buffer = &message->pBuffers[idx];
1227     buf_ptr = buffer->pvBuffer;
1228 
1229     expected_size = 5 + ((buf_ptr[3] << 8) | buf_ptr[4]);
1230     if(buffer->cbBuffer < expected_size)
1231     {
1232         TRACE("Expected %u bytes, but buffer only contains %u bytes\n", expected_size, buffer->cbBuffer);
1233         buffer->BufferType = SECBUFFER_MISSING;
1234         buffer->cbBuffer = expected_size - buffer->cbBuffer;
1235 
1236         /* This is a bit weird, but windows does it too */
1237         idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1238         buffer = &message->pBuffers[idx];
1239         buffer->BufferType = SECBUFFER_MISSING;
1240         buffer->cbBuffer = expected_size - buffer->cbBuffer;
1241 
1242         TRACE("Returning SEC_E_INCOMPLETE_MESSAGE\n");
1243         return SEC_E_INCOMPLETE_MESSAGE;
1244     }
1245 
1246     data_size = expected_size - 5;
1247     data = HeapAlloc(GetProcessHeap(), 0, data_size);
1248 
1249     transport.ctx = ctx;
1250     init_schan_buffers(&transport.in, message, schan_decrypt_message_get_next_buffer);
1251     transport.in.limit = expected_size;
1252     init_schan_buffers(&transport.out, NULL, NULL);
1253     schan_imp_set_session_transport(ctx->session, &transport);
1254 
1255     while (received < data_size)
1256     {
1257         SIZE_T length = data_size - received;
1258         SECURITY_STATUS status = schan_imp_recv(ctx->session, data + received, &length);
1259 
1260         if (status == SEC_I_CONTINUE_NEEDED)
1261             break;
1262 
1263         if (status != SEC_E_OK)
1264         {
1265             HeapFree(GetProcessHeap(), 0, data);
1266             ERR("Returning %x\n", status);
1267             return status;
1268         }
1269 
1270         if (!length)
1271             break;
1272 
1273         received += length;
1274     }
1275 
1276     TRACE("Received %ld bytes\n", received);
1277 
1278     memcpy(buf_ptr + 5, data, received);
1279     HeapFree(GetProcessHeap(), 0, data);
1280 
1281     schan_decrypt_fill_buffer(message, SECBUFFER_DATA,
1282         buf_ptr + 5, received);
1283 
1284     schan_decrypt_fill_buffer(message, SECBUFFER_STREAM_TRAILER,
1285         buf_ptr + 5 + received, buffer->cbBuffer - 5 - received);
1286 
1287     if(buffer->cbBuffer > expected_size)
1288         schan_decrypt_fill_buffer(message, SECBUFFER_EXTRA,
1289             buf_ptr + expected_size, buffer->cbBuffer - expected_size);
1290 
1291     buffer->BufferType = SECBUFFER_STREAM_HEADER;
1292     buffer->cbBuffer = 5;
1293 
1294     return SEC_E_OK;
1295 }
1296 
1297 SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context_handle)
1298 {
1299     struct schan_context *ctx;
1300 
1301     TRACE("context_handle %p\n", context_handle);
1302 
1303     if (!context_handle) return SEC_E_INVALID_HANDLE;
1304 
1305     ctx = schan_free_handle(context_handle->dwLower, SCHAN_HANDLE_CTX);
1306     if (!ctx) return SEC_E_INVALID_HANDLE;
1307 
1308     if (ctx->cert)
1309         CertFreeCertificateContext(ctx->cert);
1310     schan_imp_dispose_session(ctx->session);
1311     HeapFree(GetProcessHeap(), 0, ctx);
1312 
1313     return SEC_E_OK;
1314 }
1315 
1316 SecurityFunctionTableA schanTableA = {
1317     1,
1318     schan_EnumerateSecurityPackagesA,
1319     schan_QueryCredentialsAttributesA,
1320     schan_AcquireCredentialsHandleA,
1321     schan_FreeCredentialsHandle,
1322     NULL, /* Reserved2 */
1323     schan_InitializeSecurityContextA,
1324     NULL, /* AcceptSecurityContext */
1325     NULL, /* CompleteAuthToken */
1326     schan_DeleteSecurityContext,
1327     NULL, /* ApplyControlToken */
1328     schan_QueryContextAttributesA,
1329     NULL, /* ImpersonateSecurityContext */
1330     NULL, /* RevertSecurityContext */
1331     NULL, /* MakeSignature */
1332     NULL, /* VerifySignature */
1333     schan_FreeContextBuffer,
1334     NULL, /* QuerySecurityPackageInfoA */
1335     NULL, /* Reserved3 */
1336     NULL, /* Reserved4 */
1337     NULL, /* ExportSecurityContext */
1338     NULL, /* ImportSecurityContextA */
1339     NULL, /* AddCredentialsA */
1340     NULL, /* Reserved8 */
1341     NULL, /* QuerySecurityContextToken */
1342     schan_EncryptMessage,
1343     schan_DecryptMessage,
1344     NULL, /* SetContextAttributesA */
1345 };
1346 
1347 SecurityFunctionTableW schanTableW = {
1348     1,
1349     schan_EnumerateSecurityPackagesW,
1350     schan_QueryCredentialsAttributesW,
1351     schan_AcquireCredentialsHandleW,
1352     schan_FreeCredentialsHandle,
1353     NULL, /* Reserved2 */
1354     schan_InitializeSecurityContextW,
1355     NULL, /* AcceptSecurityContext */
1356     NULL, /* CompleteAuthToken */
1357     schan_DeleteSecurityContext,
1358     NULL, /* ApplyControlToken */
1359     schan_QueryContextAttributesW,
1360     NULL, /* ImpersonateSecurityContext */
1361     NULL, /* RevertSecurityContext */
1362     NULL, /* MakeSignature */
1363     NULL, /* VerifySignature */
1364     schan_FreeContextBuffer,
1365     NULL, /* QuerySecurityPackageInfoW */
1366     NULL, /* Reserved3 */
1367     NULL, /* Reserved4 */
1368     NULL, /* ExportSecurityContext */
1369     NULL, /* ImportSecurityContextW */
1370     NULL, /* AddCredentialsW */
1371     NULL, /* Reserved8 */
1372     NULL, /* QuerySecurityContextToken */
1373     schan_EncryptMessage,
1374     schan_DecryptMessage,
1375     NULL, /* SetContextAttributesW */
1376 };
1377 
1378 static const WCHAR schannelComment[] = { 'S','c','h','a','n','n','e','l',' ',
1379  'S','e','c','u','r','i','t','y',' ','P','a','c','k','a','g','e',0 };
1380 static const WCHAR schannelDllName[] = { 's','c','h','a','n','n','e','l','.','d','l','l',0 };
1381 
1382 void SECUR32_initSchannelSP(void)
1383 {
1384     /* This is what Windows reports.  This shouldn't break any applications
1385      * even though the functions are missing, because the wrapper will
1386      * return SEC_E_UNSUPPORTED_FUNCTION if our function is NULL.
1387      */
1388     static const LONG caps =
1389         SECPKG_FLAG_INTEGRITY |
1390         SECPKG_FLAG_PRIVACY |
1391         SECPKG_FLAG_CONNECTION |
1392         SECPKG_FLAG_MULTI_REQUIRED |
1393         SECPKG_FLAG_EXTENDED_ERROR |
1394         SECPKG_FLAG_IMPERSONATION |
1395         SECPKG_FLAG_ACCEPT_WIN32_NAME |
1396         SECPKG_FLAG_STREAM;
1397     static const short version = 1;
1398     static const LONG maxToken = 16384;
1399     SEC_WCHAR *uniSPName = (SEC_WCHAR *)UNISP_NAME_W,
1400               *schannel = (SEC_WCHAR *)SCHANNEL_NAME_W;
1401     const SecPkgInfoW info[] = {
1402         { caps, version, UNISP_RPC_ID, maxToken, uniSPName, uniSPName },
1403         { caps, version, UNISP_RPC_ID, maxToken, schannel,
1404             (SEC_WCHAR *)schannelComment },
1405     };
1406     SecureProvider *provider;
1407 
1408     if (!schan_imp_init())
1409         return;
1410 
1411     schan_handle_table = HeapAlloc(GetProcessHeap(), 0, 64 * sizeof(*schan_handle_table));
1412     if (!schan_handle_table)
1413     {
1414         ERR("Failed to allocate schannel handle table.\n");
1415         goto fail;
1416     }
1417     schan_handle_table_size = 64;
1418 
1419     provider = SECUR32_addProvider(&schanTableA, &schanTableW, schannelDllName);
1420     if (!provider)
1421     {
1422         ERR("Failed to add schannel provider.\n");
1423         goto fail;
1424     }
1425 
1426     SECUR32_addPackages(provider, sizeof(info) / sizeof(info[0]), NULL, info);
1427 
1428     return;
1429 
1430 fail:
1431     HeapFree(GetProcessHeap(), 0, schan_handle_table);
1432     schan_handle_table = NULL;
1433     schan_imp_deinit();
1434     return;
1435 }
1436 
1437 void SECUR32_deinitSchannelSP(void)
1438 {
1439     SIZE_T i = schan_handle_count;
1440 
1441     if (!schan_handle_table) return;
1442 
1443     /* deinitialized sessions first because a pointer to the credentials
1444      * may be stored for the session. */
1445     while (i--)
1446     {
1447         if (schan_handle_table[i].type == SCHAN_HANDLE_CTX)
1448         {
1449             struct schan_context *ctx = schan_free_handle(i, SCHAN_HANDLE_CTX);
1450             schan_imp_dispose_session(ctx->session);
1451             HeapFree(GetProcessHeap(), 0, ctx);
1452         }
1453     }
1454     i = schan_handle_count;
1455     while (i--)
1456     {
1457         if (schan_handle_table[i].type != SCHAN_HANDLE_FREE)
1458         {
1459             struct schan_credentials *cred;
1460             cred = schan_free_handle(i, SCHAN_HANDLE_CRED);
1461             schan_imp_free_certificate_credentials(cred);
1462             HeapFree(GetProcessHeap(), 0, cred);
1463         }
1464     }
1465     HeapFree(GetProcessHeap(), 0, schan_handle_table);
1466     schan_imp_deinit();
1467 }
1468 
1469 #else /* SONAME_LIBGNUTLS || HAVE_SECURITY_SECURITY_H || SONAME_LIBMBEDTLS  */
1470 
1471 void SECUR32_initSchannelSP(void)
1472 {
1473     ERR("TLS library not found, SSL connections will fail\n");
1474 }
1475 
1476 void SECUR32_deinitSchannelSP(void) {}
1477 
1478 #endif /* SONAME_LIBGNUTLS || HAVE_SECURITY_SECURITY_H || SONAME_LIBMBEDTLS  */
1479