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