wbemprox: Add a partial implementation of Win32_DiskPartition.
[wine.git] / dlls / secur32 / schannel.c
blobbf64fcd255e80434564eb4ed9c68ab03141ae623
1 /* Copyright (C) 2005 Juan Lang
2 * Copyright 2008 Henri Verbeet
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 * This file implements the schannel provider, or, the SSL/TLS implementations.
20 #include "config.h"
21 #include "wine/port.h"
23 #include <stdarg.h>
24 #include <errno.h>
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"
34 WINE_DEFAULT_DEBUG_CHANNEL(secur32);
36 #if defined(SONAME_LIBGNUTLS) || defined (HAVE_SECURITY_SECURITY_H)
38 #define SCHAN_INVALID_HANDLE ~0UL
40 enum schan_handle_type
42 SCHAN_HANDLE_CRED,
43 SCHAN_HANDLE_CTX,
44 SCHAN_HANDLE_FREE
47 struct schan_handle
49 void *object;
50 enum schan_handle_type type;
53 struct schan_credentials
55 ULONG credential_use;
56 schan_imp_certificate_credentials credentials;
59 struct schan_context
61 schan_imp_session session;
62 ULONG req_ctx_attr;
63 HCERTSTORE cert_store;
66 static struct schan_handle *schan_handle_table;
67 static struct schan_handle *schan_free_handles;
68 static SIZE_T schan_handle_table_size;
69 static SIZE_T schan_handle_count;
71 static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type)
73 struct schan_handle *handle;
75 if (schan_free_handles)
77 DWORD index = schan_free_handles - schan_handle_table;
78 /* Use a free handle */
79 handle = schan_free_handles;
80 if (handle->type != SCHAN_HANDLE_FREE)
82 ERR("Handle %d(%p) is in the free list, but has type %#x.\n", index, handle, handle->type);
83 return SCHAN_INVALID_HANDLE;
85 schan_free_handles = handle->object;
86 handle->object = object;
87 handle->type = type;
89 return index;
91 if (!(schan_handle_count < schan_handle_table_size))
93 /* Grow the table */
94 SIZE_T new_size = schan_handle_table_size + (schan_handle_table_size >> 1);
95 struct schan_handle *new_table = HeapReAlloc(GetProcessHeap(), 0, schan_handle_table, new_size * sizeof(*schan_handle_table));
96 if (!new_table)
98 ERR("Failed to grow the handle table\n");
99 return SCHAN_INVALID_HANDLE;
101 schan_handle_table = new_table;
102 schan_handle_table_size = new_size;
105 handle = &schan_handle_table[schan_handle_count++];
106 handle->object = object;
107 handle->type = type;
109 return handle - schan_handle_table;
112 static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type)
114 struct schan_handle *handle;
115 void *object;
117 if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
118 if (handle_idx >= schan_handle_count) return NULL;
119 handle = &schan_handle_table[handle_idx];
120 if (handle->type != type)
122 ERR("Handle %ld(%p) is not of type %#x\n", handle_idx, handle, type);
123 return NULL;
126 object = handle->object;
127 handle->object = schan_free_handles;
128 handle->type = SCHAN_HANDLE_FREE;
129 schan_free_handles = handle;
131 return object;
134 static void *schan_get_object(ULONG_PTR handle_idx, enum schan_handle_type type)
136 struct schan_handle *handle;
138 if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
139 if (handle_idx >= schan_handle_count) return NULL;
140 handle = &schan_handle_table[handle_idx];
141 if (handle->type != type)
143 ERR("Handle %ld(%p) is not of type %#x\n", handle_idx, handle, type);
144 return NULL;
147 return handle->object;
150 static SECURITY_STATUS schan_QueryCredentialsAttributes(
151 PCredHandle phCredential, ULONG ulAttribute, VOID *pBuffer)
153 SECURITY_STATUS ret;
155 switch (ulAttribute)
157 case SECPKG_ATTR_SUPPORTED_ALGS:
158 if (pBuffer)
160 /* FIXME: get from CryptoAPI */
161 FIXME("SECPKG_ATTR_SUPPORTED_ALGS: stub\n");
162 ret = SEC_E_UNSUPPORTED_FUNCTION;
164 else
165 ret = SEC_E_INTERNAL_ERROR;
166 break;
167 case SECPKG_ATTR_CIPHER_STRENGTHS:
168 if (pBuffer)
170 SecPkgCred_CipherStrengths *r = pBuffer;
172 /* FIXME: get from CryptoAPI */
173 FIXME("SECPKG_ATTR_CIPHER_STRENGTHS: semi-stub\n");
174 r->dwMinimumCipherStrength = 40;
175 r->dwMaximumCipherStrength = 168;
176 ret = SEC_E_OK;
178 else
179 ret = SEC_E_INTERNAL_ERROR;
180 break;
181 case SECPKG_ATTR_SUPPORTED_PROTOCOLS:
182 if (pBuffer)
184 /* FIXME: get from OpenSSL? */
185 FIXME("SECPKG_ATTR_SUPPORTED_PROTOCOLS: stub\n");
186 ret = SEC_E_UNSUPPORTED_FUNCTION;
188 else
189 ret = SEC_E_INTERNAL_ERROR;
190 break;
191 default:
192 ret = SEC_E_UNSUPPORTED_FUNCTION;
194 return ret;
197 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesA(
198 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
200 SECURITY_STATUS ret;
202 TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
204 switch (ulAttribute)
206 case SECPKG_CRED_ATTR_NAMES:
207 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
208 ret = SEC_E_UNSUPPORTED_FUNCTION;
209 break;
210 default:
211 ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
212 pBuffer);
214 return ret;
217 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesW(
218 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
220 SECURITY_STATUS ret;
222 TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
224 switch (ulAttribute)
226 case SECPKG_CRED_ATTR_NAMES:
227 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
228 ret = SEC_E_UNSUPPORTED_FUNCTION;
229 break;
230 default:
231 ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
232 pBuffer);
234 return ret;
237 static SECURITY_STATUS schan_CheckCreds(const SCHANNEL_CRED *schanCred)
239 SECURITY_STATUS st;
240 DWORD i;
242 TRACE("dwVersion = %d\n", schanCred->dwVersion);
243 TRACE("cCreds = %d\n", schanCred->cCreds);
244 TRACE("hRootStore = %p\n", schanCred->hRootStore);
245 TRACE("cMappers = %d\n", schanCred->cMappers);
246 TRACE("cSupportedAlgs = %d:\n", schanCred->cSupportedAlgs);
247 for (i = 0; i < schanCred->cSupportedAlgs; i++)
248 TRACE("%08x\n", schanCred->palgSupportedAlgs[i]);
249 TRACE("grbitEnabledProtocols = %08x\n", schanCred->grbitEnabledProtocols);
250 TRACE("dwMinimumCipherStrength = %d\n", schanCred->dwMinimumCipherStrength);
251 TRACE("dwMaximumCipherStrength = %d\n", schanCred->dwMaximumCipherStrength);
252 TRACE("dwSessionLifespan = %d\n", schanCred->dwSessionLifespan);
253 TRACE("dwFlags = %08x\n", schanCred->dwFlags);
254 TRACE("dwCredFormat = %d\n", schanCred->dwCredFormat);
256 switch (schanCred->dwVersion)
258 case SCH_CRED_V3:
259 case SCHANNEL_CRED_VERSION:
260 break;
261 default:
262 return SEC_E_INTERNAL_ERROR;
265 if (schanCred->cCreds == 0)
266 st = SEC_E_NO_CREDENTIALS;
267 else if (schanCred->cCreds > 1)
268 st = SEC_E_UNKNOWN_CREDENTIALS;
269 else
271 DWORD keySpec;
272 HCRYPTPROV csp;
273 BOOL ret, freeCSP;
275 ret = CryptAcquireCertificatePrivateKey(schanCred->paCred[0],
276 0, /* FIXME: what flags to use? */ NULL,
277 &csp, &keySpec, &freeCSP);
278 if (ret)
280 st = SEC_E_OK;
281 if (freeCSP)
282 CryptReleaseContext(csp, 0);
284 else
285 st = SEC_E_UNKNOWN_CREDENTIALS;
287 return st;
290 static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schanCred,
291 PCredHandle phCredential, PTimeStamp ptsExpiry)
293 struct schan_credentials *creds;
294 SECURITY_STATUS st = SEC_E_OK;
296 TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
298 if (schanCred)
300 st = schan_CheckCreds(schanCred);
301 if (st == SEC_E_NO_CREDENTIALS)
302 st = SEC_E_OK;
305 /* For now, the only thing I'm interested in is the direction of the
306 * connection, so just store it.
308 if (st == SEC_E_OK)
310 ULONG_PTR handle;
312 creds = HeapAlloc(GetProcessHeap(), 0, sizeof(*creds));
313 if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
315 handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
316 if (handle == SCHAN_INVALID_HANDLE) goto fail;
318 creds->credential_use = SECPKG_CRED_OUTBOUND;
319 if (!schan_imp_allocate_certificate_credentials(&creds->credentials))
321 schan_free_handle(handle, SCHAN_HANDLE_CRED);
322 goto fail;
325 phCredential->dwLower = handle;
326 phCredential->dwUpper = 0;
328 /* Outbound credentials have no expiry */
329 if (ptsExpiry)
331 ptsExpiry->LowPart = 0;
332 ptsExpiry->HighPart = 0;
335 return st;
337 fail:
338 HeapFree(GetProcessHeap(), 0, creds);
339 return SEC_E_INTERNAL_ERROR;
342 static SECURITY_STATUS schan_AcquireServerCredentials(const SCHANNEL_CRED *schanCred,
343 PCredHandle phCredential, PTimeStamp ptsExpiry)
345 SECURITY_STATUS st;
347 TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
349 if (!schanCred) return SEC_E_NO_CREDENTIALS;
351 st = schan_CheckCreds(schanCred);
352 if (st == SEC_E_OK)
354 ULONG_PTR handle;
355 struct schan_credentials *creds;
357 creds = HeapAlloc(GetProcessHeap(), 0, sizeof(*creds));
358 if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
359 creds->credential_use = SECPKG_CRED_INBOUND;
361 handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
362 if (handle == SCHAN_INVALID_HANDLE)
364 HeapFree(GetProcessHeap(), 0, creds);
365 return SEC_E_INTERNAL_ERROR;
368 phCredential->dwLower = handle;
369 phCredential->dwUpper = 0;
371 /* FIXME: get expiry from cert */
373 return st;
376 static SECURITY_STATUS schan_AcquireCredentialsHandle(ULONG fCredentialUse,
377 const SCHANNEL_CRED *schanCred, PCredHandle phCredential, PTimeStamp ptsExpiry)
379 SECURITY_STATUS ret;
381 if (fCredentialUse == SECPKG_CRED_OUTBOUND)
382 ret = schan_AcquireClientCredentials(schanCred, phCredential,
383 ptsExpiry);
384 else
385 ret = schan_AcquireServerCredentials(schanCred, phCredential,
386 ptsExpiry);
387 return ret;
390 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleA(
391 SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
392 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
393 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
395 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
396 debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
397 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
398 return schan_AcquireCredentialsHandle(fCredentialUse,
399 pAuthData, phCredential, ptsExpiry);
402 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleW(
403 SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
404 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
405 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
407 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
408 debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
409 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
410 return schan_AcquireCredentialsHandle(fCredentialUse,
411 pAuthData, phCredential, ptsExpiry);
414 static SECURITY_STATUS SEC_ENTRY schan_FreeCredentialsHandle(
415 PCredHandle phCredential)
417 struct schan_credentials *creds;
419 TRACE("phCredential %p\n", phCredential);
421 if (!phCredential) return SEC_E_INVALID_HANDLE;
423 creds = schan_free_handle(phCredential->dwLower, SCHAN_HANDLE_CRED);
424 if (!creds) return SEC_E_INVALID_HANDLE;
426 if (creds->credential_use == SECPKG_CRED_OUTBOUND)
427 schan_imp_free_certificate_credentials(creds->credentials);
428 HeapFree(GetProcessHeap(), 0, creds);
430 return SEC_E_OK;
433 static void init_schan_buffers(struct schan_buffers *s, const PSecBufferDesc desc,
434 int (*get_next_buffer)(const struct schan_transport *, struct schan_buffers *))
436 s->offset = 0;
437 s->limit = ~0UL;
438 s->desc = desc;
439 s->current_buffer_idx = -1;
440 s->allow_buffer_resize = FALSE;
441 s->get_next_buffer = get_next_buffer;
444 static int schan_find_sec_buffer_idx(const SecBufferDesc *desc, unsigned int start_idx, ULONG buffer_type)
446 unsigned int i;
447 PSecBuffer buffer;
449 for (i = start_idx; i < desc->cBuffers; ++i)
451 buffer = &desc->pBuffers[i];
452 if (buffer->BufferType == buffer_type) return i;
455 return -1;
458 static void schan_resize_current_buffer(const struct schan_buffers *s, SIZE_T min_size)
460 SecBuffer *b = &s->desc->pBuffers[s->current_buffer_idx];
461 SIZE_T new_size = b->cbBuffer ? b->cbBuffer * 2 : 128;
462 void *new_data;
464 if (b->cbBuffer >= min_size || !s->allow_buffer_resize || min_size > UINT_MAX / 2) return;
466 while (new_size < min_size) new_size *= 2;
468 if (b->pvBuffer)
469 new_data = HeapReAlloc(GetProcessHeap(), 0, b->pvBuffer, new_size);
470 else
471 new_data = HeapAlloc(GetProcessHeap(), 0, new_size);
473 if (!new_data)
475 TRACE("Failed to resize %p from %d to %ld\n", b->pvBuffer, b->cbBuffer, new_size);
476 return;
479 b->cbBuffer = new_size;
480 b->pvBuffer = new_data;
483 char *schan_get_buffer(const struct schan_transport *t, struct schan_buffers *s, SIZE_T *count)
485 SIZE_T max_count;
486 PSecBuffer buffer;
488 if (!s->desc)
490 TRACE("No desc\n");
491 return NULL;
494 if (s->current_buffer_idx == -1)
496 /* Initial buffer */
497 int buffer_idx = s->get_next_buffer(t, s);
498 if (buffer_idx == -1)
500 TRACE("No next buffer\n");
501 return NULL;
503 s->current_buffer_idx = buffer_idx;
506 buffer = &s->desc->pBuffers[s->current_buffer_idx];
507 TRACE("Using buffer %d: cbBuffer %d, BufferType %#x, pvBuffer %p\n", s->current_buffer_idx, buffer->cbBuffer, buffer->BufferType, buffer->pvBuffer);
509 schan_resize_current_buffer(s, s->offset + *count);
510 max_count = buffer->cbBuffer - s->offset;
511 if (s->limit != ~0UL && s->limit < max_count)
512 max_count = s->limit;
513 if (!max_count)
515 int buffer_idx;
517 s->allow_buffer_resize = FALSE;
518 buffer_idx = s->get_next_buffer(t, s);
519 if (buffer_idx == -1)
521 TRACE("No next buffer\n");
522 return NULL;
524 s->current_buffer_idx = buffer_idx;
525 s->offset = 0;
526 return schan_get_buffer(t, s, count);
529 if (*count > max_count)
530 *count = max_count;
531 if (s->limit != ~0UL)
532 s->limit -= *count;
534 return (char *)buffer->pvBuffer + s->offset;
537 /* schan_pull
538 * Read data from the transport input buffer.
540 * t - The session transport object.
541 * buff - The buffer into which to store the read data. Must be at least
542 * *buff_len bytes in length.
543 * buff_len - On input, *buff_len is the desired length to read. On successful
544 * return, *buff_len is the number of bytes actually read.
546 * Returns:
547 * 0 on success, in which case:
548 * *buff_len == 0 indicates end of file.
549 * *buff_len > 0 indicates that some data was read. May be less than
550 * what was requested, in which case the caller should call again if/
551 * when they want more.
552 * EAGAIN when no data could be read without blocking
553 * another errno-style error value on failure
556 int schan_pull(struct schan_transport *t, void *buff, size_t *buff_len)
558 char *b;
559 SIZE_T local_len = *buff_len;
561 TRACE("Pull %lu bytes\n", local_len);
563 *buff_len = 0;
565 b = schan_get_buffer(t, &t->in, &local_len);
566 if (!b)
567 return EAGAIN;
569 memcpy(buff, b, local_len);
570 t->in.offset += local_len;
572 TRACE("Read %lu bytes\n", local_len);
574 *buff_len = local_len;
575 return 0;
578 /* schan_push
579 * Write data to the transport output buffer.
581 * t - The session transport object.
582 * buff - The buffer of data to write. Must be at least *buff_len bytes in length.
583 * buff_len - On input, *buff_len is the desired length to write. On successful
584 * return, *buff_len is the number of bytes actually written.
586 * Returns:
587 * 0 on success
588 * *buff_len will be > 0 indicating how much data was written. May be less
589 * than what was requested, in which case the caller should call again
590 if/when they want to write more.
591 * EAGAIN when no data could be written without blocking
592 * another errno-style error value on failure
595 int schan_push(struct schan_transport *t, const void *buff, size_t *buff_len)
597 char *b;
598 SIZE_T local_len = *buff_len;
600 TRACE("Push %lu bytes\n", local_len);
602 *buff_len = 0;
604 b = schan_get_buffer(t, &t->out, &local_len);
605 if (!b)
606 return EAGAIN;
608 memcpy(b, buff, local_len);
609 t->out.offset += local_len;
611 TRACE("Wrote %lu bytes\n", local_len);
613 *buff_len = local_len;
614 return 0;
617 schan_imp_session schan_session_for_transport(struct schan_transport* t)
619 return t->ctx->session;
622 static int schan_init_sec_ctx_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
624 if (s->current_buffer_idx == -1)
626 int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
627 if (t->ctx->req_ctx_attr & ISC_REQ_ALLOCATE_MEMORY)
629 if (idx == -1)
631 idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_EMPTY);
632 if (idx != -1) s->desc->pBuffers[idx].BufferType = SECBUFFER_TOKEN;
634 if (idx != -1 && !s->desc->pBuffers[idx].pvBuffer)
636 s->desc->pBuffers[idx].cbBuffer = 0;
637 s->allow_buffer_resize = TRUE;
640 return idx;
643 return -1;
646 static void dump_buffer_desc(SecBufferDesc *desc)
648 unsigned int i;
650 if (!desc) return;
651 TRACE("Buffer desc %p:\n", desc);
652 for (i = 0; i < desc->cBuffers; ++i)
654 SecBuffer *b = &desc->pBuffers[i];
655 TRACE("\tbuffer %u: cbBuffer %d, BufferType %#x pvBuffer %p\n", i, b->cbBuffer, b->BufferType, b->pvBuffer);
659 /***********************************************************************
660 * InitializeSecurityContextW
662 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
663 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
664 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
665 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
666 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
668 struct schan_context *ctx;
669 struct schan_buffers *out_buffers;
670 struct schan_credentials *cred;
671 struct schan_transport transport;
672 SIZE_T expected_size = ~0UL;
673 SECURITY_STATUS ret;
675 TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext,
676 debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
677 Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
679 dump_buffer_desc(pInput);
680 dump_buffer_desc(pOutput);
682 if (!phContext)
684 ULONG_PTR handle;
686 if (!phCredential) return SEC_E_INVALID_HANDLE;
688 cred = schan_get_object(phCredential->dwLower, SCHAN_HANDLE_CRED);
689 if (!cred) return SEC_E_INVALID_HANDLE;
691 if (!(cred->credential_use & SECPKG_CRED_OUTBOUND))
693 WARN("Invalid credential use %#x\n", cred->credential_use);
694 return SEC_E_INVALID_HANDLE;
697 ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(*ctx));
698 if (!ctx) return SEC_E_INSUFFICIENT_MEMORY;
700 ctx->cert_store = NULL;
701 handle = schan_alloc_handle(ctx, SCHAN_HANDLE_CTX);
702 if (handle == SCHAN_INVALID_HANDLE)
704 HeapFree(GetProcessHeap(), 0, ctx);
705 return SEC_E_INTERNAL_ERROR;
708 if (!schan_imp_create_session(&ctx->session, FALSE, cred->credentials))
710 schan_free_handle(handle, SCHAN_HANDLE_CTX);
711 HeapFree(GetProcessHeap(), 0, ctx);
712 return SEC_E_INTERNAL_ERROR;
715 phNewContext->dwLower = handle;
716 phNewContext->dwUpper = 0;
718 else
720 SIZE_T record_size = 0;
721 unsigned char *ptr;
722 SecBuffer *buffer;
723 int idx;
725 if (!pInput)
726 return SEC_E_INCOMPLETE_MESSAGE;
728 idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN);
729 if (idx == -1)
730 return SEC_E_INCOMPLETE_MESSAGE;
732 buffer = &pInput->pBuffers[idx];
733 ptr = buffer->pvBuffer;
734 expected_size = 0;
736 while (buffer->cbBuffer > expected_size + 5)
738 record_size = 5 + ((ptr[3] << 8) | ptr[4]);
740 if (buffer->cbBuffer < expected_size + record_size)
741 break;
743 expected_size += record_size;
744 ptr += record_size;
747 if (!expected_size)
749 TRACE("Expected at least %lu bytes, but buffer only contains %u bytes.\n",
750 max(6, record_size), buffer->cbBuffer);
751 return SEC_E_INCOMPLETE_MESSAGE;
754 TRACE("Using expected_size %lu.\n", expected_size);
756 ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX);
759 ctx->req_ctx_attr = fContextReq;
761 transport.ctx = ctx;
762 init_schan_buffers(&transport.in, pInput, schan_init_sec_ctx_get_next_buffer);
763 transport.in.limit = expected_size;
764 init_schan_buffers(&transport.out, pOutput, schan_init_sec_ctx_get_next_buffer);
765 schan_imp_set_session_transport(ctx->session, &transport);
767 /* Perform the TLS handshake */
768 ret = schan_imp_handshake(ctx->session);
770 if(transport.in.offset && transport.in.offset != pInput->pBuffers[0].cbBuffer) {
771 if(pInput->cBuffers<2 || pInput->pBuffers[1].BufferType!=SECBUFFER_EMPTY)
772 return SEC_E_INVALID_TOKEN;
774 pInput->pBuffers[1].BufferType = SECBUFFER_EXTRA;
775 pInput->pBuffers[1].cbBuffer = pInput->pBuffers[0].cbBuffer-transport.in.offset;
778 out_buffers = &transport.out;
779 if (out_buffers->current_buffer_idx != -1)
781 SecBuffer *buffer = &out_buffers->desc->pBuffers[out_buffers->current_buffer_idx];
782 buffer->cbBuffer = out_buffers->offset;
785 *pfContextAttr = 0;
786 if (ctx->req_ctx_attr & ISC_REQ_ALLOCATE_MEMORY)
787 *pfContextAttr |= ISC_RET_ALLOCATED_MEMORY;
789 return ret;
792 /***********************************************************************
793 * InitializeSecurityContextA
795 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextA(
796 PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
797 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
798 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
799 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
801 SECURITY_STATUS ret;
802 SEC_WCHAR *target_name = NULL;
804 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
805 debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
806 Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
808 if (pszTargetName)
810 INT len = MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, NULL, 0);
811 target_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(*target_name));
812 MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, target_name, len);
815 ret = schan_InitializeSecurityContextW(phCredential, phContext, target_name,
816 fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
817 phNewContext, pOutput, pfContextAttr, ptsExpiry);
819 HeapFree(GetProcessHeap(), 0, target_name);
821 return ret;
824 static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
825 PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
827 struct schan_context *ctx;
829 TRACE("context_handle %p, attribute %#x, buffer %p\n",
830 context_handle, attribute, buffer);
832 if (!context_handle) return SEC_E_INVALID_HANDLE;
833 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
835 switch(attribute)
837 case SECPKG_ATTR_STREAM_SIZES:
839 SecPkgContext_ConnectionInfo info;
840 SECURITY_STATUS status = schan_imp_get_connection_info(ctx->session, &info);
841 if (status == SEC_E_OK)
843 SecPkgContext_StreamSizes *stream_sizes = buffer;
844 SIZE_T mac_size = info.dwHashStrength;
845 unsigned int block_size = schan_imp_get_session_cipher_block_size(ctx->session);
846 unsigned int message_size = schan_imp_get_max_message_size(ctx->session);
848 TRACE("Using %lu mac bytes, message size %u, block size %u\n",
849 mac_size, message_size, block_size);
851 /* These are defined by the TLS RFC */
852 stream_sizes->cbHeader = 5;
853 stream_sizes->cbTrailer = mac_size + 256; /* Max 255 bytes padding + 1 for padding size */
854 stream_sizes->cbMaximumMessage = message_size;
855 stream_sizes->cbBuffers = 4;
856 stream_sizes->cbBlockSize = block_size;
859 return status;
861 case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
863 PCCERT_CONTEXT *cert = buffer;
865 if (!ctx->cert_store) {
866 ctx->cert_store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
867 if(!ctx->cert_store)
868 return GetLastError();
871 return schan_imp_get_session_peer_certificate(ctx->session, ctx->cert_store, cert);
873 case SECPKG_ATTR_CONNECTION_INFO:
875 SecPkgContext_ConnectionInfo *info = buffer;
876 return schan_imp_get_connection_info(ctx->session, info);
879 default:
880 FIXME("Unhandled attribute %#x\n", attribute);
881 return SEC_E_UNSUPPORTED_FUNCTION;
885 static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesA(
886 PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
888 TRACE("context_handle %p, attribute %#x, buffer %p\n",
889 context_handle, attribute, buffer);
891 switch(attribute)
893 case SECPKG_ATTR_STREAM_SIZES:
894 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
895 case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
896 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
897 case SECPKG_ATTR_CONNECTION_INFO:
898 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
900 default:
901 FIXME("Unhandled attribute %#x\n", attribute);
902 return SEC_E_UNSUPPORTED_FUNCTION;
906 static int schan_encrypt_message_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
908 SecBuffer *b;
910 if (s->current_buffer_idx == -1)
911 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_STREAM_HEADER);
913 b = &s->desc->pBuffers[s->current_buffer_idx];
915 if (b->BufferType == SECBUFFER_STREAM_HEADER)
916 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
918 if (b->BufferType == SECBUFFER_DATA)
919 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_STREAM_TRAILER);
921 return -1;
924 static int schan_encrypt_message_get_next_buffer_token(const struct schan_transport *t, struct schan_buffers *s)
926 SecBuffer *b;
928 if (s->current_buffer_idx == -1)
929 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
931 b = &s->desc->pBuffers[s->current_buffer_idx];
933 if (b->BufferType == SECBUFFER_TOKEN)
935 int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
936 if (idx != s->current_buffer_idx) return -1;
937 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
940 if (b->BufferType == SECBUFFER_DATA)
942 int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
943 if (idx != -1)
944 idx = schan_find_sec_buffer_idx(s->desc, idx + 1, SECBUFFER_TOKEN);
945 return idx;
948 return -1;
951 static SECURITY_STATUS SEC_ENTRY schan_EncryptMessage(PCtxtHandle context_handle,
952 ULONG quality, PSecBufferDesc message, ULONG message_seq_no)
954 struct schan_transport transport;
955 struct schan_context *ctx;
956 struct schan_buffers *b;
957 SECURITY_STATUS status;
958 SecBuffer *buffer;
959 SIZE_T data_size;
960 SIZE_T length;
961 char *data;
962 int idx;
964 TRACE("context_handle %p, quality %d, message %p, message_seq_no %d\n",
965 context_handle, quality, message, message_seq_no);
967 if (!context_handle) return SEC_E_INVALID_HANDLE;
968 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
970 dump_buffer_desc(message);
972 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_DATA);
973 if (idx == -1)
975 WARN("No data buffer passed\n");
976 return SEC_E_INTERNAL_ERROR;
978 buffer = &message->pBuffers[idx];
980 data_size = buffer->cbBuffer;
981 data = HeapAlloc(GetProcessHeap(), 0, data_size);
982 memcpy(data, buffer->pvBuffer, data_size);
984 transport.ctx = ctx;
985 init_schan_buffers(&transport.in, NULL, NULL);
986 if (schan_find_sec_buffer_idx(message, 0, SECBUFFER_STREAM_HEADER) != -1)
987 init_schan_buffers(&transport.out, message, schan_encrypt_message_get_next_buffer);
988 else
989 init_schan_buffers(&transport.out, message, schan_encrypt_message_get_next_buffer_token);
990 schan_imp_set_session_transport(ctx->session, &transport);
992 length = data_size;
993 status = schan_imp_send(ctx->session, data, &length);
995 TRACE("Sent %ld bytes.\n", length);
997 if (length != data_size)
998 status = SEC_E_INTERNAL_ERROR;
1000 b = &transport.out;
1001 b->desc->pBuffers[b->current_buffer_idx].cbBuffer = b->offset;
1002 HeapFree(GetProcessHeap(), 0, data);
1004 TRACE("Returning %#x.\n", status);
1006 return status;
1009 static int schan_decrypt_message_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
1011 if (s->current_buffer_idx == -1)
1012 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
1014 return -1;
1017 static int schan_validate_decrypt_buffer_desc(PSecBufferDesc message)
1019 int data_idx = -1;
1020 unsigned int empty_count = 0;
1021 unsigned int i;
1023 if (message->cBuffers < 4)
1025 WARN("Less than four buffers passed\n");
1026 return -1;
1029 for (i = 0; i < message->cBuffers; ++i)
1031 SecBuffer *b = &message->pBuffers[i];
1032 if (b->BufferType == SECBUFFER_DATA)
1034 if (data_idx != -1)
1036 WARN("More than one data buffer passed\n");
1037 return -1;
1039 data_idx = i;
1041 else if (b->BufferType == SECBUFFER_EMPTY)
1042 ++empty_count;
1045 if (data_idx == -1)
1047 WARN("No data buffer passed\n");
1048 return -1;
1051 if (empty_count < 3)
1053 WARN("Less than three empty buffers passed\n");
1054 return -1;
1057 return data_idx;
1060 static void schan_decrypt_fill_buffer(PSecBufferDesc message, ULONG buffer_type, void *data, ULONG size)
1062 int idx;
1063 SecBuffer *buffer;
1065 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1066 buffer = &message->pBuffers[idx];
1068 buffer->BufferType = buffer_type;
1069 buffer->pvBuffer = data;
1070 buffer->cbBuffer = size;
1073 static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle,
1074 PSecBufferDesc message, ULONG message_seq_no, PULONG quality)
1076 struct schan_transport transport;
1077 struct schan_context *ctx;
1078 SecBuffer *buffer;
1079 SIZE_T data_size;
1080 char *data;
1081 unsigned expected_size;
1082 SSIZE_T received = 0;
1083 int idx;
1084 unsigned char *buf_ptr;
1086 TRACE("context_handle %p, message %p, message_seq_no %d, quality %p\n",
1087 context_handle, message, message_seq_no, quality);
1089 if (!context_handle) return SEC_E_INVALID_HANDLE;
1090 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
1092 dump_buffer_desc(message);
1094 idx = schan_validate_decrypt_buffer_desc(message);
1095 if (idx == -1)
1096 return SEC_E_INVALID_TOKEN;
1097 buffer = &message->pBuffers[idx];
1098 buf_ptr = buffer->pvBuffer;
1100 expected_size = 5 + ((buf_ptr[3] << 8) | buf_ptr[4]);
1101 if(buffer->cbBuffer < expected_size)
1103 TRACE("Expected %u bytes, but buffer only contains %u bytes\n", expected_size, buffer->cbBuffer);
1104 buffer->BufferType = SECBUFFER_MISSING;
1105 buffer->cbBuffer = expected_size - buffer->cbBuffer;
1107 /* This is a bit weird, but windows does it too */
1108 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1109 buffer = &message->pBuffers[idx];
1110 buffer->BufferType = SECBUFFER_MISSING;
1111 buffer->cbBuffer = expected_size - buffer->cbBuffer;
1113 TRACE("Returning SEC_E_INCOMPLETE_MESSAGE\n");
1114 return SEC_E_INCOMPLETE_MESSAGE;
1117 data_size = expected_size - 5;
1118 data = HeapAlloc(GetProcessHeap(), 0, data_size);
1120 transport.ctx = ctx;
1121 init_schan_buffers(&transport.in, message, schan_decrypt_message_get_next_buffer);
1122 transport.in.limit = expected_size;
1123 init_schan_buffers(&transport.out, NULL, NULL);
1124 schan_imp_set_session_transport(ctx->session, &transport);
1126 while (received < data_size)
1128 SIZE_T length = data_size - received;
1129 SECURITY_STATUS status = schan_imp_recv(ctx->session, data + received, &length);
1131 if (status == SEC_I_CONTINUE_NEEDED)
1132 break;
1134 if (status != SEC_E_OK)
1136 HeapFree(GetProcessHeap(), 0, data);
1137 ERR("Returning %x\n", status);
1138 return status;
1141 if (!length)
1142 break;
1144 received += length;
1147 TRACE("Received %ld bytes\n", received);
1149 memcpy(buf_ptr + 5, data, received);
1150 HeapFree(GetProcessHeap(), 0, data);
1152 schan_decrypt_fill_buffer(message, SECBUFFER_DATA,
1153 buf_ptr + 5, received);
1155 schan_decrypt_fill_buffer(message, SECBUFFER_STREAM_TRAILER,
1156 buf_ptr + 5 + received, buffer->cbBuffer - 5 - received);
1158 if(buffer->cbBuffer > expected_size)
1159 schan_decrypt_fill_buffer(message, SECBUFFER_EXTRA,
1160 buf_ptr + expected_size, buffer->cbBuffer - expected_size);
1162 buffer->BufferType = SECBUFFER_STREAM_HEADER;
1163 buffer->cbBuffer = 5;
1165 return SEC_E_OK;
1168 static SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context_handle)
1170 struct schan_context *ctx;
1172 TRACE("context_handle %p\n", context_handle);
1174 if (!context_handle) return SEC_E_INVALID_HANDLE;
1176 ctx = schan_free_handle(context_handle->dwLower, SCHAN_HANDLE_CTX);
1177 if (!ctx) return SEC_E_INVALID_HANDLE;
1179 if (ctx->cert_store)
1180 CertCloseStore(ctx->cert_store, 0);
1181 schan_imp_dispose_session(ctx->session);
1182 HeapFree(GetProcessHeap(), 0, ctx);
1184 return SEC_E_OK;
1187 static const SecurityFunctionTableA schanTableA = {
1189 NULL, /* EnumerateSecurityPackagesA */
1190 schan_QueryCredentialsAttributesA,
1191 schan_AcquireCredentialsHandleA,
1192 schan_FreeCredentialsHandle,
1193 NULL, /* Reserved2 */
1194 schan_InitializeSecurityContextA,
1195 NULL, /* AcceptSecurityContext */
1196 NULL, /* CompleteAuthToken */
1197 schan_DeleteSecurityContext,
1198 NULL, /* ApplyControlToken */
1199 schan_QueryContextAttributesA,
1200 NULL, /* ImpersonateSecurityContext */
1201 NULL, /* RevertSecurityContext */
1202 NULL, /* MakeSignature */
1203 NULL, /* VerifySignature */
1204 FreeContextBuffer,
1205 NULL, /* QuerySecurityPackageInfoA */
1206 NULL, /* Reserved3 */
1207 NULL, /* Reserved4 */
1208 NULL, /* ExportSecurityContext */
1209 NULL, /* ImportSecurityContextA */
1210 NULL, /* AddCredentialsA */
1211 NULL, /* Reserved8 */
1212 NULL, /* QuerySecurityContextToken */
1213 schan_EncryptMessage,
1214 schan_DecryptMessage,
1215 NULL, /* SetContextAttributesA */
1218 static const SecurityFunctionTableW schanTableW = {
1220 NULL, /* EnumerateSecurityPackagesW */
1221 schan_QueryCredentialsAttributesW,
1222 schan_AcquireCredentialsHandleW,
1223 schan_FreeCredentialsHandle,
1224 NULL, /* Reserved2 */
1225 schan_InitializeSecurityContextW,
1226 NULL, /* AcceptSecurityContext */
1227 NULL, /* CompleteAuthToken */
1228 schan_DeleteSecurityContext,
1229 NULL, /* ApplyControlToken */
1230 schan_QueryContextAttributesW,
1231 NULL, /* ImpersonateSecurityContext */
1232 NULL, /* RevertSecurityContext */
1233 NULL, /* MakeSignature */
1234 NULL, /* VerifySignature */
1235 FreeContextBuffer,
1236 NULL, /* QuerySecurityPackageInfoW */
1237 NULL, /* Reserved3 */
1238 NULL, /* Reserved4 */
1239 NULL, /* ExportSecurityContext */
1240 NULL, /* ImportSecurityContextW */
1241 NULL, /* AddCredentialsW */
1242 NULL, /* Reserved8 */
1243 NULL, /* QuerySecurityContextToken */
1244 schan_EncryptMessage,
1245 schan_DecryptMessage,
1246 NULL, /* SetContextAttributesW */
1249 static const WCHAR schannelComment[] = { 'S','c','h','a','n','n','e','l',' ',
1250 'S','e','c','u','r','i','t','y',' ','P','a','c','k','a','g','e',0 };
1251 static const WCHAR schannelDllName[] = { 's','c','h','a','n','n','e','l','.','d','l','l',0 };
1253 void SECUR32_initSchannelSP(void)
1255 /* This is what Windows reports. This shouldn't break any applications
1256 * even though the functions are missing, because the wrapper will
1257 * return SEC_E_UNSUPPORTED_FUNCTION if our function is NULL.
1259 static const LONG caps =
1260 SECPKG_FLAG_INTEGRITY |
1261 SECPKG_FLAG_PRIVACY |
1262 SECPKG_FLAG_CONNECTION |
1263 SECPKG_FLAG_MULTI_REQUIRED |
1264 SECPKG_FLAG_EXTENDED_ERROR |
1265 SECPKG_FLAG_IMPERSONATION |
1266 SECPKG_FLAG_ACCEPT_WIN32_NAME |
1267 SECPKG_FLAG_STREAM;
1268 static const short version = 1;
1269 static const LONG maxToken = 16384;
1270 SEC_WCHAR *uniSPName = (SEC_WCHAR *)UNISP_NAME_W,
1271 *schannel = (SEC_WCHAR *)SCHANNEL_NAME_W;
1272 const SecPkgInfoW info[] = {
1273 { caps, version, UNISP_RPC_ID, maxToken, uniSPName, uniSPName },
1274 { caps, version, UNISP_RPC_ID, maxToken, schannel,
1275 (SEC_WCHAR *)schannelComment },
1277 SecureProvider *provider;
1279 if (!schan_imp_init())
1280 return;
1282 schan_handle_table = HeapAlloc(GetProcessHeap(), 0, 64 * sizeof(*schan_handle_table));
1283 if (!schan_handle_table)
1285 ERR("Failed to allocate schannel handle table.\n");
1286 goto fail;
1288 schan_handle_table_size = 64;
1290 provider = SECUR32_addProvider(&schanTableA, &schanTableW, schannelDllName);
1291 if (!provider)
1293 ERR("Failed to add schannel provider.\n");
1294 goto fail;
1297 SECUR32_addPackages(provider, sizeof(info) / sizeof(info[0]), NULL, info);
1299 return;
1301 fail:
1302 HeapFree(GetProcessHeap(), 0, schan_handle_table);
1303 schan_handle_table = NULL;
1304 schan_imp_deinit();
1305 return;
1308 void SECUR32_deinitSchannelSP(void)
1310 SIZE_T i = schan_handle_count;
1312 if (!schan_handle_table) return;
1314 /* deinitialized sessions first because a pointer to the credentials
1315 * may be stored for the session. */
1316 while (i--)
1318 if (schan_handle_table[i].type == SCHAN_HANDLE_CTX)
1320 struct schan_context *ctx = schan_free_handle(i, SCHAN_HANDLE_CTX);
1321 schan_imp_dispose_session(ctx->session);
1322 HeapFree(GetProcessHeap(), 0, ctx);
1325 i = schan_handle_count;
1326 while (i--)
1328 if (schan_handle_table[i].type != SCHAN_HANDLE_FREE)
1330 struct schan_credentials *cred;
1331 cred = schan_free_handle(i, SCHAN_HANDLE_CRED);
1332 schan_imp_free_certificate_credentials(cred->credentials);
1333 HeapFree(GetProcessHeap(), 0, cred);
1336 HeapFree(GetProcessHeap(), 0, schan_handle_table);
1337 schan_imp_deinit();
1340 #else /* SONAME_LIBGNUTLS || HAVE_SECURITY_SECURITY_H */
1342 void SECUR32_initSchannelSP(void)
1344 ERR("TLS library not found, SSL connections will fail\n");
1347 void SECUR32_deinitSchannelSP(void) {}
1349 #endif /* SONAME_LIBGNUTLS || HAVE_SECURITY_SECURITY_H */