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