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.
21 #include "wine/port.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
50 enum schan_handle_type type
;
53 struct schan_credentials
56 schan_imp_certificate_credentials credentials
;
61 schan_imp_session session
;
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
;
107 if (!(schan_handle_count
< schan_handle_table_size
))
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
));
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
;
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
;
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
);
142 object
= handle
->object
;
143 handle
->object
= schan_free_handles
;
144 handle
->type
= SCHAN_HANDLE_FREE
;
145 schan_free_handles
= handle
;
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
);
163 return handle
->object
;
166 static SECURITY_STATUS
schan_QueryCredentialsAttributes(
167 PCredHandle phCredential
, ULONG ulAttribute
, VOID
*pBuffer
)
173 case SECPKG_ATTR_SUPPORTED_ALGS
:
176 /* FIXME: get from CryptoAPI */
177 FIXME("SECPKG_ATTR_SUPPORTED_ALGS: stub\n");
178 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
181 ret
= SEC_E_INTERNAL_ERROR
;
183 case SECPKG_ATTR_CIPHER_STRENGTHS
:
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;
195 ret
= SEC_E_INTERNAL_ERROR
;
197 case SECPKG_ATTR_SUPPORTED_PROTOCOLS
:
200 /* FIXME: get from OpenSSL? */
201 FIXME("SECPKG_ATTR_SUPPORTED_PROTOCOLS: stub\n");
202 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
205 ret
= SEC_E_INTERNAL_ERROR
;
208 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
213 static SECURITY_STATUS SEC_ENTRY
schan_QueryCredentialsAttributesA(
214 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
218 TRACE("(%p, %d, %p)\n", phCredential
, ulAttribute
, pBuffer
);
222 case SECPKG_CRED_ATTR_NAMES
:
223 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
224 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
227 ret
= schan_QueryCredentialsAttributes(phCredential
, ulAttribute
,
233 static SECURITY_STATUS SEC_ENTRY
schan_QueryCredentialsAttributesW(
234 PCredHandle phCredential
, ULONG ulAttribute
, PVOID pBuffer
)
238 TRACE("(%p, %d, %p)\n", phCredential
, ulAttribute
, pBuffer
);
242 case SECPKG_CRED_ATTR_NAMES
:
243 FIXME("SECPKG_CRED_ATTR_NAMES: stub\n");
244 ret
= SEC_E_UNSUPPORTED_FUNCTION
;
247 ret
= schan_QueryCredentialsAttributes(phCredential
, ulAttribute
,
253 static SECURITY_STATUS
schan_CheckCreds(const SCHANNEL_CRED
*schanCred
)
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
)
275 case SCHANNEL_CRED_VERSION
:
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
;
291 ret
= CryptAcquireCertificatePrivateKey(schanCred
->paCred
[0],
292 0, /* FIXME: what flags to use? */ NULL
,
293 &csp
, &keySpec
, &freeCSP
);
298 CryptReleaseContext(csp
, 0);
301 st
= SEC_E_UNKNOWN_CREDENTIALS
;
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
);
316 st
= schan_CheckCreds(schanCred
);
317 if (st
== SEC_E_NO_CREDENTIALS
)
321 /* For now, the only thing I'm interested in is the direction of the
322 * connection, so just store it.
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
);
341 phCredential
->dwLower
= handle
;
342 phCredential
->dwUpper
= 0;
344 /* Outbound credentials have no expiry */
347 ptsExpiry
->LowPart
= 0;
348 ptsExpiry
->HighPart
= 0;
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
)
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
);
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 */
392 static SECURITY_STATUS
schan_AcquireCredentialsHandle(ULONG fCredentialUse
,
393 const SCHANNEL_CRED
*schanCred
, PCredHandle phCredential
, PTimeStamp ptsExpiry
)
397 if (fCredentialUse
== SECPKG_CRED_OUTBOUND
)
398 ret
= schan_AcquireClientCredentials(schanCred
, phCredential
,
401 ret
= schan_AcquireServerCredentials(schanCred
, phCredential
,
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
);
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
*))
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
)
465 for (i
= start_idx
; i
< desc
->cBuffers
; ++i
)
467 buffer
= &desc
->pBuffers
[i
];
468 if (buffer
->BufferType
== buffer_type
) return i
;
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;
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;
485 new_data
= HeapReAlloc(GetProcessHeap(), 0, b
->pvBuffer
, new_size
);
487 new_data
= HeapAlloc(GetProcessHeap(), 0, new_size
);
491 TRACE("Failed to resize %p from %d to %ld\n", b
->pvBuffer
, b
->cbBuffer
, new_size
);
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
)
510 if (s
->current_buffer_idx
== -1)
513 int buffer_idx
= s
->get_next_buffer(t
, s
);
514 if (buffer_idx
== -1)
516 TRACE("No next buffer\n");
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
;
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");
538 s
->current_buffer_idx
= buffer_idx
;
540 return schan_get_buffer(t
, s
, count
);
543 if (*count
> max_count
) *count
= max_count
;
544 return (char *)buffer
->pvBuffer
+ s
->offset
;
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.
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
)
569 size_t local_len
= *buff_len
;
571 TRACE("Pull %zu bytes\n", local_len
);
575 b
= schan_get_buffer(t
, &t
->in
, &local_len
);
579 if (t
->in
.limit
!= 0 && t
->in
.offset
+ local_len
>= t
->in
.limit
)
581 local_len
= t
->in
.limit
- t
->in
.offset
;
586 memcpy(buff
, b
, local_len
);
587 t
->in
.offset
+= local_len
;
589 TRACE("Read %zu bytes\n", local_len
);
591 *buff_len
= local_len
;
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.
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
)
615 size_t local_len
= *buff_len
;
617 TRACE("Push %zu bytes\n", local_len
);
621 b
= schan_get_buffer(t
, &t
->out
, &local_len
);
625 memcpy(b
, buff
, local_len
);
626 t
->out
.offset
+= local_len
;
628 TRACE("Wrote %zu bytes\n", local_len
);
630 *buff_len
= local_len
;
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
)
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
;
663 static void dump_buffer_desc(SecBufferDesc
*desc
)
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
;
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
);
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;
735 ctx
= schan_get_object(phContext
->dwLower
, SCHAN_HANDLE_CTX
);
738 ctx
->req_ctx_attr
= fContextReq
;
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
;
764 if (ctx
->req_ctx_attr
& ISC_REQ_ALLOCATE_MEMORY
)
765 *pfContextAttr
|= ISC_RET_ALLOCATED_MEMORY
;
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
)
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
);
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
);
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
);
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 %zu 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
;
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
);
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
);
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
);
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
)
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
);
893 static int schan_encrypt_message_get_next_buffer_token(const struct schan_transport
*t
, struct schan_buffers
*s
)
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
);
913 idx
= schan_find_sec_buffer_idx(s
->desc
, idx
+ 1, SECBUFFER_TOKEN
);
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
;
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
);
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
);
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
);
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
)
966 else if (status
!= SEC_E_OK
)
968 HeapFree(GetProcessHeap(), 0, data
);
969 ERR("Returning %d\n", status
);
976 TRACE("Sent %zd bytes\n", sent
);
979 b
->desc
->pBuffers
[b
->current_buffer_idx
].cbBuffer
= b
->offset
;
980 HeapFree(GetProcessHeap(), 0, data
);
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
);
993 static int schan_validate_decrypt_buffer_desc(PSecBufferDesc message
)
996 unsigned int empty_count
= 0;
999 if (message
->cBuffers
< 4)
1001 WARN("Less than four buffers passed\n");
1005 for (i
= 0; i
< message
->cBuffers
; ++i
)
1007 SecBuffer
*b
= &message
->pBuffers
[i
];
1008 if (b
->BufferType
== SECBUFFER_DATA
)
1012 WARN("More than one data buffer passed\n");
1017 else if (b
->BufferType
== SECBUFFER_EMPTY
)
1023 WARN("No data buffer passed\n");
1027 if (empty_count
< 3)
1029 WARN("Less than three empty buffers passed\n");
1036 static void schan_decrypt_fill_buffer(PSecBufferDesc message
, ULONG buffer_type
, void *data
, ULONG size
)
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
;
1057 unsigned expected_size
;
1058 ssize_t received
= 0;
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
);
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
)
1110 HeapFree(GetProcessHeap(), 0, data
);
1111 TRACE("Returning SEC_E_INCOMPLETE_MESSAGE\n");
1112 return SEC_E_INCOMPLETE_MESSAGE
;
1116 else if (status
!= SEC_E_OK
)
1118 HeapFree(GetProcessHeap(), 0, data
);
1119 ERR("Returning %d\n", status
);
1128 TRACE("Received %zd 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;
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
);
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 */
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 */
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
|
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())
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");
1267 schan_handle_table_size
= 64;
1269 provider
= SECUR32_addProvider(&schanTableA
, &schanTableW
, schannelDllName
);
1272 ERR("Failed to add schannel provider.\n");
1276 SECUR32_addPackages(provider
, sizeof(info
) / sizeof(info
[0]), NULL
, info
);
1281 HeapFree(GetProcessHeap(), 0, schan_handle_table
);
1282 schan_handle_table
= NULL
;
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. */
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
;
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
);
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 */