secur32: Avoid size_t and fix some printf formats.
[wine/multimedia.git] / dlls / secur32 / schannel.c
blobf12baaa5ab60624806015010a9c27f1f2de4eda0
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;
65 struct schan_buffers
67 SIZE_T offset;
68 SIZE_T limit;
69 const SecBufferDesc *desc;
70 int current_buffer_idx;
71 BOOL allow_buffer_resize;
72 int (*get_next_buffer)(const struct schan_transport *, struct schan_buffers *);
75 struct schan_transport
77 struct schan_context *ctx;
78 struct schan_buffers in;
79 struct schan_buffers out;
82 static struct schan_handle *schan_handle_table;
83 static struct schan_handle *schan_free_handles;
84 static SIZE_T schan_handle_table_size;
85 static SIZE_T schan_handle_count;
87 static ULONG_PTR schan_alloc_handle(void *object, enum schan_handle_type type)
89 struct schan_handle *handle;
91 if (schan_free_handles)
93 DWORD index = schan_free_handles - schan_handle_table;
94 /* Use a free handle */
95 handle = schan_free_handles;
96 if (handle->type != SCHAN_HANDLE_FREE)
98 ERR("Handle %d(%p) is in the free list, but has type %#x.\n", index, handle, handle->type);
99 return SCHAN_INVALID_HANDLE;
101 schan_free_handles = handle->object;
102 handle->object = object;
103 handle->type = type;
105 return index;
107 if (!(schan_handle_count < schan_handle_table_size))
109 /* Grow the table */
110 SIZE_T new_size = schan_handle_table_size + (schan_handle_table_size >> 1);
111 struct schan_handle *new_table = HeapReAlloc(GetProcessHeap(), 0, schan_handle_table, new_size * sizeof(*schan_handle_table));
112 if (!new_table)
114 ERR("Failed to grow the handle table\n");
115 return SCHAN_INVALID_HANDLE;
117 schan_handle_table = new_table;
118 schan_handle_table_size = new_size;
121 handle = &schan_handle_table[schan_handle_count++];
122 handle->object = object;
123 handle->type = type;
125 return handle - schan_handle_table;
128 static void *schan_free_handle(ULONG_PTR handle_idx, enum schan_handle_type type)
130 struct schan_handle *handle;
131 void *object;
133 if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
134 if (handle_idx >= schan_handle_count) return NULL;
135 handle = &schan_handle_table[handle_idx];
136 if (handle->type != type)
138 ERR("Handle %ld(%p) is not of type %#x\n", handle_idx, handle, type);
139 return NULL;
142 object = handle->object;
143 handle->object = schan_free_handles;
144 handle->type = SCHAN_HANDLE_FREE;
145 schan_free_handles = handle;
147 return object;
150 static void *schan_get_object(ULONG_PTR handle_idx, enum schan_handle_type type)
152 struct schan_handle *handle;
154 if (handle_idx == SCHAN_INVALID_HANDLE) return NULL;
155 if (handle_idx >= schan_handle_count) return NULL;
156 handle = &schan_handle_table[handle_idx];
157 if (handle->type != type)
159 ERR("Handle %ld(%p) is not of type %#x\n", handle_idx, handle, type);
160 return NULL;
163 return handle->object;
166 static SECURITY_STATUS schan_QueryCredentialsAttributes(
167 PCredHandle phCredential, ULONG ulAttribute, VOID *pBuffer)
169 SECURITY_STATUS ret;
171 switch (ulAttribute)
173 case SECPKG_ATTR_SUPPORTED_ALGS:
174 if (pBuffer)
176 /* FIXME: get from CryptoAPI */
177 FIXME("SECPKG_ATTR_SUPPORTED_ALGS: stub\n");
178 ret = SEC_E_UNSUPPORTED_FUNCTION;
180 else
181 ret = SEC_E_INTERNAL_ERROR;
182 break;
183 case SECPKG_ATTR_CIPHER_STRENGTHS:
184 if (pBuffer)
186 SecPkgCred_CipherStrengths *r = pBuffer;
188 /* FIXME: get from CryptoAPI */
189 FIXME("SECPKG_ATTR_CIPHER_STRENGTHS: semi-stub\n");
190 r->dwMinimumCipherStrength = 40;
191 r->dwMaximumCipherStrength = 168;
192 ret = SEC_E_OK;
194 else
195 ret = SEC_E_INTERNAL_ERROR;
196 break;
197 case SECPKG_ATTR_SUPPORTED_PROTOCOLS:
198 if (pBuffer)
200 /* FIXME: get from OpenSSL? */
201 FIXME("SECPKG_ATTR_SUPPORTED_PROTOCOLS: stub\n");
202 ret = SEC_E_UNSUPPORTED_FUNCTION;
204 else
205 ret = SEC_E_INTERNAL_ERROR;
206 break;
207 default:
208 ret = SEC_E_UNSUPPORTED_FUNCTION;
210 return ret;
213 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesA(
214 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
216 SECURITY_STATUS ret;
218 TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
220 switch (ulAttribute)
222 case SECPKG_CRED_ATTR_NAMES:
223 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
224 ret = SEC_E_UNSUPPORTED_FUNCTION;
225 break;
226 default:
227 ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
228 pBuffer);
230 return ret;
233 static SECURITY_STATUS SEC_ENTRY schan_QueryCredentialsAttributesW(
234 PCredHandle phCredential, ULONG ulAttribute, PVOID pBuffer)
236 SECURITY_STATUS ret;
238 TRACE("(%p, %d, %p)\n", phCredential, ulAttribute, pBuffer);
240 switch (ulAttribute)
242 case SECPKG_CRED_ATTR_NAMES:
243 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
244 ret = SEC_E_UNSUPPORTED_FUNCTION;
245 break;
246 default:
247 ret = schan_QueryCredentialsAttributes(phCredential, ulAttribute,
248 pBuffer);
250 return ret;
253 static SECURITY_STATUS schan_CheckCreds(const SCHANNEL_CRED *schanCred)
255 SECURITY_STATUS st;
256 DWORD i;
258 TRACE("dwVersion = %d\n", schanCred->dwVersion);
259 TRACE("cCreds = %d\n", schanCred->cCreds);
260 TRACE("hRootStore = %p\n", schanCred->hRootStore);
261 TRACE("cMappers = %d\n", schanCred->cMappers);
262 TRACE("cSupportedAlgs = %d:\n", schanCred->cSupportedAlgs);
263 for (i = 0; i < schanCred->cSupportedAlgs; i++)
264 TRACE("%08x\n", schanCred->palgSupportedAlgs[i]);
265 TRACE("grbitEnabledProtocols = %08x\n", schanCred->grbitEnabledProtocols);
266 TRACE("dwMinimumCipherStrength = %d\n", schanCred->dwMinimumCipherStrength);
267 TRACE("dwMaximumCipherStrength = %d\n", schanCred->dwMaximumCipherStrength);
268 TRACE("dwSessionLifespan = %d\n", schanCred->dwSessionLifespan);
269 TRACE("dwFlags = %08x\n", schanCred->dwFlags);
270 TRACE("dwCredFormat = %d\n", schanCred->dwCredFormat);
272 switch (schanCred->dwVersion)
274 case SCH_CRED_V3:
275 case SCHANNEL_CRED_VERSION:
276 break;
277 default:
278 return SEC_E_INTERNAL_ERROR;
281 if (schanCred->cCreds == 0)
282 st = SEC_E_NO_CREDENTIALS;
283 else if (schanCred->cCreds > 1)
284 st = SEC_E_UNKNOWN_CREDENTIALS;
285 else
287 DWORD keySpec;
288 HCRYPTPROV csp;
289 BOOL ret, freeCSP;
291 ret = CryptAcquireCertificatePrivateKey(schanCred->paCred[0],
292 0, /* FIXME: what flags to use? */ NULL,
293 &csp, &keySpec, &freeCSP);
294 if (ret)
296 st = SEC_E_OK;
297 if (freeCSP)
298 CryptReleaseContext(csp, 0);
300 else
301 st = SEC_E_UNKNOWN_CREDENTIALS;
303 return st;
306 static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schanCred,
307 PCredHandle phCredential, PTimeStamp ptsExpiry)
309 struct schan_credentials *creds;
310 SECURITY_STATUS st = SEC_E_OK;
312 TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
314 if (schanCred)
316 st = schan_CheckCreds(schanCred);
317 if (st == SEC_E_NO_CREDENTIALS)
318 st = SEC_E_OK;
321 /* For now, the only thing I'm interested in is the direction of the
322 * connection, so just store it.
324 if (st == SEC_E_OK)
326 ULONG_PTR handle;
328 creds = HeapAlloc(GetProcessHeap(), 0, sizeof(*creds));
329 if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
331 handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
332 if (handle == SCHAN_INVALID_HANDLE) goto fail;
334 creds->credential_use = SECPKG_CRED_OUTBOUND;
335 if (!schan_imp_allocate_certificate_credentials(&creds->credentials))
337 schan_free_handle(handle, SCHAN_HANDLE_CRED);
338 goto fail;
341 phCredential->dwLower = handle;
342 phCredential->dwUpper = 0;
344 /* Outbound credentials have no expiry */
345 if (ptsExpiry)
347 ptsExpiry->LowPart = 0;
348 ptsExpiry->HighPart = 0;
351 return st;
353 fail:
354 HeapFree(GetProcessHeap(), 0, creds);
355 return SEC_E_INTERNAL_ERROR;
358 static SECURITY_STATUS schan_AcquireServerCredentials(const SCHANNEL_CRED *schanCred,
359 PCredHandle phCredential, PTimeStamp ptsExpiry)
361 SECURITY_STATUS st;
363 TRACE("schanCred %p, phCredential %p, ptsExpiry %p\n", schanCred, phCredential, ptsExpiry);
365 if (!schanCred) return SEC_E_NO_CREDENTIALS;
367 st = schan_CheckCreds(schanCred);
368 if (st == SEC_E_OK)
370 ULONG_PTR handle;
371 struct schan_credentials *creds;
373 creds = HeapAlloc(GetProcessHeap(), 0, sizeof(*creds));
374 if (!creds) return SEC_E_INSUFFICIENT_MEMORY;
375 creds->credential_use = SECPKG_CRED_INBOUND;
377 handle = schan_alloc_handle(creds, SCHAN_HANDLE_CRED);
378 if (handle == SCHAN_INVALID_HANDLE)
380 HeapFree(GetProcessHeap(), 0, creds);
381 return SEC_E_INTERNAL_ERROR;
384 phCredential->dwLower = handle;
385 phCredential->dwUpper = 0;
387 /* FIXME: get expiry from cert */
389 return st;
392 static SECURITY_STATUS schan_AcquireCredentialsHandle(ULONG fCredentialUse,
393 const SCHANNEL_CRED *schanCred, PCredHandle phCredential, PTimeStamp ptsExpiry)
395 SECURITY_STATUS ret;
397 if (fCredentialUse == SECPKG_CRED_OUTBOUND)
398 ret = schan_AcquireClientCredentials(schanCred, phCredential,
399 ptsExpiry);
400 else
401 ret = schan_AcquireServerCredentials(schanCred, phCredential,
402 ptsExpiry);
403 return ret;
406 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleA(
407 SEC_CHAR *pszPrincipal, SEC_CHAR *pszPackage, ULONG fCredentialUse,
408 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
409 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
411 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
412 debugstr_a(pszPrincipal), debugstr_a(pszPackage), fCredentialUse,
413 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
414 return schan_AcquireCredentialsHandle(fCredentialUse,
415 pAuthData, phCredential, ptsExpiry);
418 static SECURITY_STATUS SEC_ENTRY schan_AcquireCredentialsHandleW(
419 SEC_WCHAR *pszPrincipal, SEC_WCHAR *pszPackage, ULONG fCredentialUse,
420 PLUID pLogonID, PVOID pAuthData, SEC_GET_KEY_FN pGetKeyFn,
421 PVOID pGetKeyArgument, PCredHandle phCredential, PTimeStamp ptsExpiry)
423 TRACE("(%s, %s, 0x%08x, %p, %p, %p, %p, %p, %p)\n",
424 debugstr_w(pszPrincipal), debugstr_w(pszPackage), fCredentialUse,
425 pLogonID, pAuthData, pGetKeyFn, pGetKeyArgument, phCredential, ptsExpiry);
426 return schan_AcquireCredentialsHandle(fCredentialUse,
427 pAuthData, phCredential, ptsExpiry);
430 static SECURITY_STATUS SEC_ENTRY schan_FreeCredentialsHandle(
431 PCredHandle phCredential)
433 struct schan_credentials *creds;
435 TRACE("phCredential %p\n", phCredential);
437 if (!phCredential) return SEC_E_INVALID_HANDLE;
439 creds = schan_free_handle(phCredential->dwLower, SCHAN_HANDLE_CRED);
440 if (!creds) return SEC_E_INVALID_HANDLE;
442 if (creds->credential_use == SECPKG_CRED_OUTBOUND)
443 schan_imp_free_certificate_credentials(creds->credentials);
444 HeapFree(GetProcessHeap(), 0, creds);
446 return SEC_E_OK;
449 static void init_schan_buffers(struct schan_buffers *s, const PSecBufferDesc desc,
450 int (*get_next_buffer)(const struct schan_transport *, struct schan_buffers *))
452 s->offset = 0;
453 s->limit = 0;
454 s->desc = desc;
455 s->current_buffer_idx = -1;
456 s->allow_buffer_resize = FALSE;
457 s->get_next_buffer = get_next_buffer;
460 static int schan_find_sec_buffer_idx(const SecBufferDesc *desc, unsigned int start_idx, ULONG buffer_type)
462 unsigned int i;
463 PSecBuffer buffer;
465 for (i = start_idx; i < desc->cBuffers; ++i)
467 buffer = &desc->pBuffers[i];
468 if (buffer->BufferType == buffer_type) return i;
471 return -1;
474 static void schan_resize_current_buffer(const struct schan_buffers *s, SIZE_T min_size)
476 SecBuffer *b = &s->desc->pBuffers[s->current_buffer_idx];
477 SIZE_T new_size = b->cbBuffer ? b->cbBuffer * 2 : 128;
478 void *new_data;
480 if (b->cbBuffer >= min_size || !s->allow_buffer_resize || min_size > UINT_MAX / 2) return;
482 while (new_size < min_size) new_size *= 2;
484 if (b->pvBuffer)
485 new_data = HeapReAlloc(GetProcessHeap(), 0, b->pvBuffer, new_size);
486 else
487 new_data = HeapAlloc(GetProcessHeap(), 0, new_size);
489 if (!new_data)
491 TRACE("Failed to resize %p from %d to %ld\n", b->pvBuffer, b->cbBuffer, new_size);
492 return;
495 b->cbBuffer = new_size;
496 b->pvBuffer = new_data;
499 static char *schan_get_buffer(const struct schan_transport *t, struct schan_buffers *s, SIZE_T *count)
501 SIZE_T max_count;
502 PSecBuffer buffer;
504 if (!s->desc)
506 TRACE("No desc\n");
507 return NULL;
510 if (s->current_buffer_idx == -1)
512 /* Initial buffer */
513 int buffer_idx = s->get_next_buffer(t, s);
514 if (buffer_idx == -1)
516 TRACE("No next buffer\n");
517 return NULL;
519 s->current_buffer_idx = buffer_idx;
522 buffer = &s->desc->pBuffers[s->current_buffer_idx];
523 TRACE("Using buffer %d: cbBuffer %d, BufferType %#x, pvBuffer %p\n", s->current_buffer_idx, buffer->cbBuffer, buffer->BufferType, buffer->pvBuffer);
525 schan_resize_current_buffer(s, s->offset + *count);
526 max_count = buffer->cbBuffer - s->offset;
527 if (!max_count)
529 int buffer_idx;
531 s->allow_buffer_resize = FALSE;
532 buffer_idx = s->get_next_buffer(t, s);
533 if (buffer_idx == -1)
535 TRACE("No next buffer\n");
536 return NULL;
538 s->current_buffer_idx = buffer_idx;
539 s->offset = 0;
540 return schan_get_buffer(t, s, count);
543 if (*count > max_count) *count = max_count;
544 return (char *)buffer->pvBuffer + s->offset;
547 /* schan_pull
548 * Read data from the transport input buffer.
550 * t - The session transport object.
551 * buff - The buffer into which to store the read data. Must be at least
552 * *buff_len bytes in length.
553 * buff_len - On input, *buff_len is the desired length to read. On successful
554 * return, *buff_len is the number of bytes actually read.
556 * Returns:
557 * 0 on success, in which case:
558 * *buff_len == 0 indicates end of file.
559 * *buff_len > 0 indicates that some data was read. May be less than
560 * what was requested, in which case the caller should call again if/
561 * when they want more.
562 * EAGAIN when no data could be read without blocking
563 * another errno-style error value on failure
566 int schan_pull(struct schan_transport *t, void *buff, size_t *buff_len)
568 char *b;
569 SIZE_T local_len = *buff_len;
571 TRACE("Pull %lu bytes\n", local_len);
573 *buff_len = 0;
575 b = schan_get_buffer(t, &t->in, &local_len);
576 if (!b)
577 return EAGAIN;
579 if (t->in.limit != 0 && t->in.offset + local_len >= t->in.limit)
581 local_len = t->in.limit - t->in.offset;
582 if (local_len == 0)
583 return EAGAIN;
586 memcpy(buff, b, local_len);
587 t->in.offset += local_len;
589 TRACE("Read %lu bytes\n", local_len);
591 *buff_len = local_len;
592 return 0;
595 /* schan_push
596 * Write data to the transport output buffer.
598 * t - The session transport object.
599 * buff - The buffer of data to write. Must be at least *buff_len bytes in length.
600 * buff_len - On input, *buff_len is the desired length to write. On successful
601 * return, *buff_len is the number of bytes actually written.
603 * Returns:
604 * 0 on success
605 * *buff_len will be > 0 indicating how much data was written. May be less
606 * than what was requested, in which case the caller should call again
607 if/when they want to write more.
608 * EAGAIN when no data could be written without blocking
609 * another errno-style error value on failure
612 int schan_push(struct schan_transport *t, const void *buff, size_t *buff_len)
614 char *b;
615 SIZE_T local_len = *buff_len;
617 TRACE("Push %lu bytes\n", local_len);
619 *buff_len = 0;
621 b = schan_get_buffer(t, &t->out, &local_len);
622 if (!b)
623 return EAGAIN;
625 memcpy(b, buff, local_len);
626 t->out.offset += local_len;
628 TRACE("Wrote %lu bytes\n", local_len);
630 *buff_len = local_len;
631 return 0;
634 schan_imp_session schan_session_for_transport(struct schan_transport* t)
636 return t->ctx->session;
639 static int schan_init_sec_ctx_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
641 if (s->current_buffer_idx == -1)
643 int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
644 if (t->ctx->req_ctx_attr & ISC_REQ_ALLOCATE_MEMORY)
646 if (idx == -1)
648 idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_EMPTY);
649 if (idx != -1) s->desc->pBuffers[idx].BufferType = SECBUFFER_TOKEN;
651 if (idx != -1 && !s->desc->pBuffers[idx].pvBuffer)
653 s->desc->pBuffers[idx].cbBuffer = 0;
654 s->allow_buffer_resize = TRUE;
657 return idx;
660 return -1;
663 static void dump_buffer_desc(SecBufferDesc *desc)
665 unsigned int i;
667 if (!desc) return;
668 TRACE("Buffer desc %p:\n", desc);
669 for (i = 0; i < desc->cBuffers; ++i)
671 SecBuffer *b = &desc->pBuffers[i];
672 TRACE("\tbuffer %u: cbBuffer %d, BufferType %#x pvBuffer %p\n", i, b->cbBuffer, b->BufferType, b->pvBuffer);
676 /***********************************************************************
677 * InitializeSecurityContextW
679 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
680 PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR *pszTargetName,
681 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
682 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
683 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
685 struct schan_context *ctx;
686 struct schan_buffers *out_buffers;
687 struct schan_credentials *cred;
688 struct schan_transport transport;
689 SECURITY_STATUS ret;
691 TRACE("%p %p %s 0x%08x %d %d %p %d %p %p %p %p\n", phCredential, phContext,
692 debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
693 Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
695 dump_buffer_desc(pInput);
696 dump_buffer_desc(pOutput);
698 if (!phContext)
700 ULONG_PTR handle;
702 if (!phCredential) return SEC_E_INVALID_HANDLE;
704 cred = schan_get_object(phCredential->dwLower, SCHAN_HANDLE_CRED);
705 if (!cred) return SEC_E_INVALID_HANDLE;
707 if (!(cred->credential_use & SECPKG_CRED_OUTBOUND))
709 WARN("Invalid credential use %#x\n", cred->credential_use);
710 return SEC_E_INVALID_HANDLE;
713 ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(*ctx));
714 if (!ctx) return SEC_E_INSUFFICIENT_MEMORY;
716 handle = schan_alloc_handle(ctx, SCHAN_HANDLE_CTX);
717 if (handle == SCHAN_INVALID_HANDLE)
719 HeapFree(GetProcessHeap(), 0, ctx);
720 return SEC_E_INTERNAL_ERROR;
723 if (!schan_imp_create_session(&ctx->session, FALSE, cred->credentials))
725 schan_free_handle(handle, SCHAN_HANDLE_CTX);
726 HeapFree(GetProcessHeap(), 0, ctx);
727 return SEC_E_INTERNAL_ERROR;
730 phNewContext->dwLower = handle;
731 phNewContext->dwUpper = 0;
733 else
735 ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX);
738 ctx->req_ctx_attr = fContextReq;
740 transport.ctx = ctx;
741 init_schan_buffers(&transport.in, pInput, schan_init_sec_ctx_get_next_buffer);
742 init_schan_buffers(&transport.out, pOutput, schan_init_sec_ctx_get_next_buffer);
743 schan_imp_set_session_transport(ctx->session, &transport);
745 /* Perform the TLS handshake */
746 ret = schan_imp_handshake(ctx->session);
748 if(transport.in.offset && transport.in.offset != pInput->pBuffers[0].cbBuffer) {
749 if(pInput->cBuffers<2 || pInput->pBuffers[1].BufferType!=SECBUFFER_EMPTY)
750 return SEC_E_INVALID_TOKEN;
752 pInput->pBuffers[1].BufferType = SECBUFFER_EXTRA;
753 pInput->pBuffers[1].cbBuffer = pInput->pBuffers[0].cbBuffer-transport.in.offset;
756 out_buffers = &transport.out;
757 if (out_buffers->current_buffer_idx != -1)
759 SecBuffer *buffer = &out_buffers->desc->pBuffers[out_buffers->current_buffer_idx];
760 buffer->cbBuffer = out_buffers->offset;
763 *pfContextAttr = 0;
764 if (ctx->req_ctx_attr & ISC_REQ_ALLOCATE_MEMORY)
765 *pfContextAttr |= ISC_RET_ALLOCATED_MEMORY;
767 return ret;
770 /***********************************************************************
771 * InitializeSecurityContextA
773 static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextA(
774 PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR *pszTargetName,
775 ULONG fContextReq, ULONG Reserved1, ULONG TargetDataRep,
776 PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
777 PSecBufferDesc pOutput, ULONG *pfContextAttr, PTimeStamp ptsExpiry)
779 SECURITY_STATUS ret;
780 SEC_WCHAR *target_name = NULL;
782 TRACE("%p %p %s %d %d %d %p %d %p %p %p %p\n", phCredential, phContext,
783 debugstr_a(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
784 Reserved1, phNewContext, pOutput, pfContextAttr, ptsExpiry);
786 if (pszTargetName)
788 INT len = MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, NULL, 0);
789 target_name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(*target_name));
790 MultiByteToWideChar(CP_ACP, 0, pszTargetName, -1, target_name, len);
793 ret = schan_InitializeSecurityContextW(phCredential, phContext, target_name,
794 fContextReq, Reserved1, TargetDataRep, pInput, Reserved2,
795 phNewContext, pOutput, pfContextAttr, ptsExpiry);
797 HeapFree(GetProcessHeap(), 0, target_name);
799 return ret;
802 static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
803 PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
805 struct schan_context *ctx;
807 TRACE("context_handle %p, attribute %#x, buffer %p\n",
808 context_handle, attribute, buffer);
810 if (!context_handle) return SEC_E_INVALID_HANDLE;
811 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
813 switch(attribute)
815 case SECPKG_ATTR_STREAM_SIZES:
817 SecPkgContext_ConnectionInfo info;
818 SECURITY_STATUS status = schan_imp_get_connection_info(ctx->session, &info);
819 if (status == SEC_E_OK)
821 SecPkgContext_StreamSizes *stream_sizes = buffer;
822 SIZE_T mac_size = info.dwHashStrength;
823 unsigned int block_size = schan_imp_get_session_cipher_block_size(ctx->session);
825 TRACE("Using %lu mac bytes, block size %u\n", mac_size, block_size);
827 /* These are defined by the TLS RFC */
828 stream_sizes->cbHeader = 5;
829 stream_sizes->cbTrailer = mac_size + 256; /* Max 255 bytes padding + 1 for padding size */
830 stream_sizes->cbMaximumMessage = 1 << 14;
831 stream_sizes->cbBuffers = 4;
832 stream_sizes->cbBlockSize = block_size;
835 return status;
837 case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
839 PCCERT_CONTEXT *cert = buffer;
840 return schan_imp_get_session_peer_certificate(ctx->session, cert);
842 case SECPKG_ATTR_CONNECTION_INFO:
844 SecPkgContext_ConnectionInfo *info = buffer;
845 return schan_imp_get_connection_info(ctx->session, info);
848 default:
849 FIXME("Unhandled attribute %#x\n", attribute);
850 return SEC_E_UNSUPPORTED_FUNCTION;
854 static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesA(
855 PCtxtHandle context_handle, ULONG attribute, PVOID buffer)
857 TRACE("context_handle %p, attribute %#x, buffer %p\n",
858 context_handle, attribute, buffer);
860 switch(attribute)
862 case SECPKG_ATTR_STREAM_SIZES:
863 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
864 case SECPKG_ATTR_REMOTE_CERT_CONTEXT:
865 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
866 case SECPKG_ATTR_CONNECTION_INFO:
867 return schan_QueryContextAttributesW(context_handle, attribute, buffer);
869 default:
870 FIXME("Unhandled attribute %#x\n", attribute);
871 return SEC_E_UNSUPPORTED_FUNCTION;
875 static int schan_encrypt_message_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
877 SecBuffer *b;
879 if (s->current_buffer_idx == -1)
880 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_STREAM_HEADER);
882 b = &s->desc->pBuffers[s->current_buffer_idx];
884 if (b->BufferType == SECBUFFER_STREAM_HEADER)
885 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
887 if (b->BufferType == SECBUFFER_DATA)
888 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_STREAM_TRAILER);
890 return -1;
893 static int schan_encrypt_message_get_next_buffer_token(const struct schan_transport *t, struct schan_buffers *s)
895 SecBuffer *b;
897 if (s->current_buffer_idx == -1)
898 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
900 b = &s->desc->pBuffers[s->current_buffer_idx];
902 if (b->BufferType == SECBUFFER_TOKEN)
904 int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
905 if (idx != s->current_buffer_idx) return -1;
906 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
909 if (b->BufferType == SECBUFFER_DATA)
911 int idx = schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_TOKEN);
912 if (idx != -1)
913 idx = schan_find_sec_buffer_idx(s->desc, idx + 1, SECBUFFER_TOKEN);
914 return idx;
917 return -1;
920 static SECURITY_STATUS SEC_ENTRY schan_EncryptMessage(PCtxtHandle context_handle,
921 ULONG quality, PSecBufferDesc message, ULONG message_seq_no)
923 struct schan_transport transport;
924 struct schan_context *ctx;
925 struct schan_buffers *b;
926 SecBuffer *buffer;
927 SIZE_T data_size;
928 char *data;
929 SSIZE_T sent = 0;
930 int idx;
932 TRACE("context_handle %p, quality %d, message %p, message_seq_no %d\n",
933 context_handle, quality, message, message_seq_no);
935 if (!context_handle) return SEC_E_INVALID_HANDLE;
936 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
938 dump_buffer_desc(message);
940 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_DATA);
941 if (idx == -1)
943 WARN("No data buffer passed\n");
944 return SEC_E_INTERNAL_ERROR;
946 buffer = &message->pBuffers[idx];
948 data_size = buffer->cbBuffer;
949 data = HeapAlloc(GetProcessHeap(), 0, data_size);
950 memcpy(data, buffer->pvBuffer, data_size);
952 transport.ctx = ctx;
953 init_schan_buffers(&transport.in, NULL, NULL);
954 if (schan_find_sec_buffer_idx(message, 0, SECBUFFER_STREAM_HEADER) != -1)
955 init_schan_buffers(&transport.out, message, schan_encrypt_message_get_next_buffer);
956 else
957 init_schan_buffers(&transport.out, message, schan_encrypt_message_get_next_buffer_token);
958 schan_imp_set_session_transport(ctx->session, &transport);
960 while (sent < data_size)
962 SIZE_T length = data_size - sent;
963 SECURITY_STATUS status = schan_imp_send(ctx->session, data + sent, &length);
964 if (status == SEC_I_CONTINUE_NEEDED)
965 break;
966 else if (status != SEC_E_OK)
968 HeapFree(GetProcessHeap(), 0, data);
969 ERR("Returning %d\n", status);
970 return status;
973 sent += length;
976 TRACE("Sent %ld bytes\n", sent);
978 b = &transport.out;
979 b->desc->pBuffers[b->current_buffer_idx].cbBuffer = b->offset;
980 HeapFree(GetProcessHeap(), 0, data);
982 return SEC_E_OK;
985 static int schan_decrypt_message_get_next_buffer(const struct schan_transport *t, struct schan_buffers *s)
987 if (s->current_buffer_idx == -1)
988 return schan_find_sec_buffer_idx(s->desc, 0, SECBUFFER_DATA);
990 return -1;
993 static int schan_validate_decrypt_buffer_desc(PSecBufferDesc message)
995 int data_idx = -1;
996 unsigned int empty_count = 0;
997 unsigned int i;
999 if (message->cBuffers < 4)
1001 WARN("Less than four buffers passed\n");
1002 return -1;
1005 for (i = 0; i < message->cBuffers; ++i)
1007 SecBuffer *b = &message->pBuffers[i];
1008 if (b->BufferType == SECBUFFER_DATA)
1010 if (data_idx != -1)
1012 WARN("More than one data buffer passed\n");
1013 return -1;
1015 data_idx = i;
1017 else if (b->BufferType == SECBUFFER_EMPTY)
1018 ++empty_count;
1021 if (data_idx == -1)
1023 WARN("No data buffer passed\n");
1024 return -1;
1027 if (empty_count < 3)
1029 WARN("Less than three empty buffers passed\n");
1030 return -1;
1033 return data_idx;
1036 static void schan_decrypt_fill_buffer(PSecBufferDesc message, ULONG buffer_type, void *data, ULONG size)
1038 int idx;
1039 SecBuffer *buffer;
1041 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1042 buffer = &message->pBuffers[idx];
1044 buffer->BufferType = buffer_type;
1045 buffer->pvBuffer = data;
1046 buffer->cbBuffer = size;
1049 static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle,
1050 PSecBufferDesc message, ULONG message_seq_no, PULONG quality)
1052 struct schan_transport transport;
1053 struct schan_context *ctx;
1054 SecBuffer *buffer;
1055 SIZE_T data_size;
1056 char *data;
1057 unsigned expected_size;
1058 SSIZE_T received = 0;
1059 int idx;
1060 unsigned char *buf_ptr;
1062 TRACE("context_handle %p, message %p, message_seq_no %d, quality %p\n",
1063 context_handle, message, message_seq_no, quality);
1065 if (!context_handle) return SEC_E_INVALID_HANDLE;
1066 ctx = schan_get_object(context_handle->dwLower, SCHAN_HANDLE_CTX);
1068 dump_buffer_desc(message);
1070 idx = schan_validate_decrypt_buffer_desc(message);
1071 if (idx == -1)
1072 return SEC_E_INVALID_TOKEN;
1073 buffer = &message->pBuffers[idx];
1074 buf_ptr = buffer->pvBuffer;
1076 expected_size = 5 + ((buf_ptr[3] << 8) | buf_ptr[4]);
1077 if(buffer->cbBuffer < expected_size)
1079 TRACE("Expected %u bytes, but buffer only contains %u bytes\n", expected_size, buffer->cbBuffer);
1080 buffer->BufferType = SECBUFFER_MISSING;
1081 buffer->cbBuffer = expected_size - buffer->cbBuffer;
1083 /* This is a bit weird, but windows does it too */
1084 idx = schan_find_sec_buffer_idx(message, 0, SECBUFFER_EMPTY);
1085 buffer = &message->pBuffers[idx];
1086 buffer->BufferType = SECBUFFER_MISSING;
1087 buffer->cbBuffer = expected_size - buffer->cbBuffer;
1089 TRACE("Returning SEC_E_INCOMPLETE_MESSAGE\n");
1090 return SEC_E_INCOMPLETE_MESSAGE;
1093 data_size = buffer->cbBuffer;
1094 data = HeapAlloc(GetProcessHeap(), 0, data_size);
1096 transport.ctx = ctx;
1097 init_schan_buffers(&transport.in, message, schan_decrypt_message_get_next_buffer);
1098 transport.in.limit = expected_size;
1099 init_schan_buffers(&transport.out, NULL, NULL);
1100 schan_imp_set_session_transport(ctx->session, &transport);
1102 while (received < data_size)
1104 SIZE_T length = data_size - received;
1105 SECURITY_STATUS status = schan_imp_recv(ctx->session, data + received, &length);
1106 if (status == SEC_I_CONTINUE_NEEDED)
1108 if (!received)
1110 HeapFree(GetProcessHeap(), 0, data);
1111 TRACE("Returning SEC_E_INCOMPLETE_MESSAGE\n");
1112 return SEC_E_INCOMPLETE_MESSAGE;
1114 break;
1116 else if (status != SEC_E_OK)
1118 HeapFree(GetProcessHeap(), 0, data);
1119 ERR("Returning %d\n", status);
1120 return status;
1122 else if (!length)
1123 break;
1125 received += length;
1128 TRACE("Received %ld bytes\n", received);
1130 memcpy(buf_ptr + 5, data, received);
1131 HeapFree(GetProcessHeap(), 0, data);
1133 schan_decrypt_fill_buffer(message, SECBUFFER_DATA,
1134 buf_ptr + 5, received);
1136 schan_decrypt_fill_buffer(message, SECBUFFER_STREAM_TRAILER,
1137 buf_ptr + 5 + received, buffer->cbBuffer - 5 - received);
1139 if(buffer->cbBuffer > expected_size)
1140 schan_decrypt_fill_buffer(message, SECBUFFER_EXTRA,
1141 buf_ptr + expected_size, buffer->cbBuffer - expected_size);
1143 buffer->BufferType = SECBUFFER_STREAM_HEADER;
1144 buffer->cbBuffer = 5;
1146 return SEC_E_OK;
1149 static SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context_handle)
1151 struct schan_context *ctx;
1153 TRACE("context_handle %p\n", context_handle);
1155 if (!context_handle) return SEC_E_INVALID_HANDLE;
1157 ctx = schan_free_handle(context_handle->dwLower, SCHAN_HANDLE_CTX);
1158 if (!ctx) return SEC_E_INVALID_HANDLE;
1160 schan_imp_dispose_session(ctx->session);
1161 HeapFree(GetProcessHeap(), 0, ctx);
1163 return SEC_E_OK;
1166 static const SecurityFunctionTableA schanTableA = {
1168 NULL, /* EnumerateSecurityPackagesA */
1169 schan_QueryCredentialsAttributesA,
1170 schan_AcquireCredentialsHandleA,
1171 schan_FreeCredentialsHandle,
1172 NULL, /* Reserved2 */
1173 schan_InitializeSecurityContextA,
1174 NULL, /* AcceptSecurityContext */
1175 NULL, /* CompleteAuthToken */
1176 schan_DeleteSecurityContext,
1177 NULL, /* ApplyControlToken */
1178 schan_QueryContextAttributesA,
1179 NULL, /* ImpersonateSecurityContext */
1180 NULL, /* RevertSecurityContext */
1181 NULL, /* MakeSignature */
1182 NULL, /* VerifySignature */
1183 FreeContextBuffer,
1184 NULL, /* QuerySecurityPackageInfoA */
1185 NULL, /* Reserved3 */
1186 NULL, /* Reserved4 */
1187 NULL, /* ExportSecurityContext */
1188 NULL, /* ImportSecurityContextA */
1189 NULL, /* AddCredentialsA */
1190 NULL, /* Reserved8 */
1191 NULL, /* QuerySecurityContextToken */
1192 schan_EncryptMessage,
1193 schan_DecryptMessage,
1194 NULL, /* SetContextAttributesA */
1197 static const SecurityFunctionTableW schanTableW = {
1199 NULL, /* EnumerateSecurityPackagesW */
1200 schan_QueryCredentialsAttributesW,
1201 schan_AcquireCredentialsHandleW,
1202 schan_FreeCredentialsHandle,
1203 NULL, /* Reserved2 */
1204 schan_InitializeSecurityContextW,
1205 NULL, /* AcceptSecurityContext */
1206 NULL, /* CompleteAuthToken */
1207 schan_DeleteSecurityContext,
1208 NULL, /* ApplyControlToken */
1209 schan_QueryContextAttributesW,
1210 NULL, /* ImpersonateSecurityContext */
1211 NULL, /* RevertSecurityContext */
1212 NULL, /* MakeSignature */
1213 NULL, /* VerifySignature */
1214 FreeContextBuffer,
1215 NULL, /* QuerySecurityPackageInfoW */
1216 NULL, /* Reserved3 */
1217 NULL, /* Reserved4 */
1218 NULL, /* ExportSecurityContext */
1219 NULL, /* ImportSecurityContextW */
1220 NULL, /* AddCredentialsW */
1221 NULL, /* Reserved8 */
1222 NULL, /* QuerySecurityContextToken */
1223 schan_EncryptMessage,
1224 schan_DecryptMessage,
1225 NULL, /* SetContextAttributesW */
1228 static const WCHAR schannelComment[] = { 'S','c','h','a','n','n','e','l',' ',
1229 'S','e','c','u','r','i','t','y',' ','P','a','c','k','a','g','e',0 };
1230 static const WCHAR schannelDllName[] = { 's','c','h','a','n','n','e','l','.','d','l','l',0 };
1232 void SECUR32_initSchannelSP(void)
1234 /* This is what Windows reports. This shouldn't break any applications
1235 * even though the functions are missing, because the wrapper will
1236 * return SEC_E_UNSUPPORTED_FUNCTION if our function is NULL.
1238 static const LONG caps =
1239 SECPKG_FLAG_INTEGRITY |
1240 SECPKG_FLAG_PRIVACY |
1241 SECPKG_FLAG_CONNECTION |
1242 SECPKG_FLAG_MULTI_REQUIRED |
1243 SECPKG_FLAG_EXTENDED_ERROR |
1244 SECPKG_FLAG_IMPERSONATION |
1245 SECPKG_FLAG_ACCEPT_WIN32_NAME |
1246 SECPKG_FLAG_STREAM;
1247 static const short version = 1;
1248 static const LONG maxToken = 16384;
1249 SEC_WCHAR *uniSPName = (SEC_WCHAR *)UNISP_NAME_W,
1250 *schannel = (SEC_WCHAR *)SCHANNEL_NAME_W;
1251 const SecPkgInfoW info[] = {
1252 { caps, version, UNISP_RPC_ID, maxToken, uniSPName, uniSPName },
1253 { caps, version, UNISP_RPC_ID, maxToken, schannel,
1254 (SEC_WCHAR *)schannelComment },
1256 SecureProvider *provider;
1258 if (!schan_imp_init())
1259 return;
1261 schan_handle_table = HeapAlloc(GetProcessHeap(), 0, 64 * sizeof(*schan_handle_table));
1262 if (!schan_handle_table)
1264 ERR("Failed to allocate schannel handle table.\n");
1265 goto fail;
1267 schan_handle_table_size = 64;
1269 provider = SECUR32_addProvider(&schanTableA, &schanTableW, schannelDllName);
1270 if (!provider)
1272 ERR("Failed to add schannel provider.\n");
1273 goto fail;
1276 SECUR32_addPackages(provider, sizeof(info) / sizeof(info[0]), NULL, info);
1278 return;
1280 fail:
1281 HeapFree(GetProcessHeap(), 0, schan_handle_table);
1282 schan_handle_table = NULL;
1283 schan_imp_deinit();
1284 return;
1287 void SECUR32_deinitSchannelSP(void)
1289 SIZE_T i = schan_handle_count;
1291 if (!schan_handle_table) return;
1293 /* deinitialized sessions first because a pointer to the credentials
1294 * may be stored for the session. */
1295 while (i--)
1297 if (schan_handle_table[i].type == SCHAN_HANDLE_CTX)
1299 struct schan_context *ctx = schan_free_handle(i, SCHAN_HANDLE_CTX);
1300 schan_imp_dispose_session(ctx->session);
1301 HeapFree(GetProcessHeap(), 0, ctx);
1304 i = schan_handle_count;
1305 while (i--)
1307 if (schan_handle_table[i].type != SCHAN_HANDLE_FREE)
1309 struct schan_credentials *cred;
1310 cred = schan_free_handle(i, SCHAN_HANDLE_CRED);
1311 schan_imp_free_certificate_credentials(cred->credentials);
1312 HeapFree(GetProcessHeap(), 0, cred);
1315 HeapFree(GetProcessHeap(), 0, schan_handle_table);
1316 schan_imp_deinit();
1319 #else /* SONAME_LIBGNUTLS || HAVE_SECURITY_SECURITY_H */
1321 void SECUR32_initSchannelSP(void)
1323 ERR("TLS library not found, SSL connections will fail\n");
1326 void SECUR32_deinitSchannelSP(void) {}
1328 #endif /* SONAME_LIBGNUTLS || HAVE_SECURITY_SECURITY_H */