secur32: Add DTLS support.
Signed-off-by: Hans Leidekker <hans@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
37b29862b3
commit
f93284dfa4
|
@ -60,6 +60,7 @@ struct schan_context
|
|||
struct schan_transport transport;
|
||||
ULONG req_ctx_attr;
|
||||
const CERT_CONTEXT *cert;
|
||||
SIZE_T header_size;
|
||||
};
|
||||
|
||||
static struct schan_handle *schan_handle_table;
|
||||
|
@ -184,7 +185,9 @@ static void read_config(void)
|
|||
{{'S','S','L',' ','3','.','0',0}, SP_PROT_SSL3_CLIENT, TRUE, FALSE},
|
||||
{{'T','L','S',' ','1','.','0',0}, SP_PROT_TLS1_0_CLIENT, TRUE, FALSE},
|
||||
{{'T','L','S',' ','1','.','1',0}, SP_PROT_TLS1_1_CLIENT, TRUE, FALSE /* NOTE: not enabled by default on Windows */ },
|
||||
{{'T','L','S',' ','1','.','2',0}, SP_PROT_TLS1_2_CLIENT, TRUE, FALSE /* NOTE: not enabled by default on Windows */ }
|
||||
{{'T','L','S',' ','1','.','2',0}, SP_PROT_TLS1_2_CLIENT, TRUE, FALSE /* NOTE: not enabled by default on Windows */ },
|
||||
{{'D','T','L','S',' ','1','.','0',0}, SP_PROT_DTLS1_0_CLIENT, TRUE, TRUE },
|
||||
{{'D','T','L','S',' ','1','.','2',0}, SP_PROT_DTLS1_2_CLIENT, TRUE, TRUE },
|
||||
};
|
||||
|
||||
/* No need for thread safety */
|
||||
|
@ -399,10 +402,17 @@ static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schan
|
|||
|
||||
if (schanCred)
|
||||
{
|
||||
const unsigned dtls_protocols = SP_PROT_DTLS_CLIENT | SP_PROT_DTLS1_2_CLIENT;
|
||||
const unsigned tls_protocols = SP_PROT_TLS1_CLIENT | SP_PROT_TLS1_0_CLIENT | SP_PROT_TLS1_1_CLIENT |
|
||||
SP_PROT_TLS1_2_CLIENT | SP_PROT_TLS1_3_CLIENT;
|
||||
|
||||
status = get_cert(schanCred, &cert);
|
||||
if (status != SEC_E_OK && status != SEC_E_NO_CREDENTIALS)
|
||||
return status;
|
||||
|
||||
if ((schanCred->grbitEnabledProtocols & tls_protocols) &&
|
||||
(schanCred->grbitEnabledProtocols & dtls_protocols)) return SEC_E_ALGORITHM_MISMATCH;
|
||||
|
||||
status = SEC_E_OK;
|
||||
}
|
||||
|
||||
|
@ -773,12 +783,18 @@ static void dump_buffer_desc(SecBufferDesc *desc)
|
|||
}
|
||||
|
||||
#define HEADER_SIZE_TLS 5
|
||||
#define HEADER_SIZE_DTLS 13
|
||||
|
||||
static inline SIZE_T read_record_size(const BYTE *buf, SIZE_T header_size)
|
||||
{
|
||||
return (buf[header_size - 2] << 8) | buf[header_size - 1];
|
||||
}
|
||||
|
||||
static inline BOOL is_dtls_context(const struct schan_context *ctx)
|
||||
{
|
||||
return (ctx->header_size == HEADER_SIZE_DTLS);
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* InitializeSecurityContextW
|
||||
*/
|
||||
|
@ -836,6 +852,11 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
|
|||
return SEC_E_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
if (cred->enabled_protocols & (SP_PROT_DTLS1_0_CLIENT | SP_PROT_DTLS1_2_CLIENT))
|
||||
ctx->header_size = HEADER_SIZE_DTLS;
|
||||
else
|
||||
ctx->header_size = HEADER_SIZE_TLS;
|
||||
|
||||
ctx->transport.ctx = ctx;
|
||||
schan_imp_set_session_transport(ctx->session, &ctx->transport);
|
||||
|
||||
|
@ -866,38 +887,38 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
|
|||
SIZE_T record_size = 0;
|
||||
unsigned char *ptr;
|
||||
|
||||
if (!pInput)
|
||||
return SEC_E_INCOMPLETE_MESSAGE;
|
||||
|
||||
idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN);
|
||||
if (idx == -1)
|
||||
return SEC_E_INCOMPLETE_MESSAGE;
|
||||
|
||||
buffer = &pInput->pBuffers[idx];
|
||||
ptr = buffer->pvBuffer;
|
||||
expected_size = 0;
|
||||
|
||||
while (buffer->cbBuffer > expected_size + HEADER_SIZE_TLS)
|
||||
ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX);
|
||||
if (pInput)
|
||||
{
|
||||
record_size = HEADER_SIZE_TLS + read_record_size(ptr, HEADER_SIZE_TLS);
|
||||
idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN);
|
||||
if (idx == -1)
|
||||
return SEC_E_INCOMPLETE_MESSAGE;
|
||||
|
||||
if (buffer->cbBuffer < expected_size + record_size)
|
||||
break;
|
||||
buffer = &pInput->pBuffers[idx];
|
||||
ptr = buffer->pvBuffer;
|
||||
expected_size = 0;
|
||||
|
||||
expected_size += record_size;
|
||||
ptr += record_size;
|
||||
}
|
||||
|
||||
if (!expected_size)
|
||||
{
|
||||
TRACE("Expected at least %lu bytes, but buffer only contains %u bytes.\n",
|
||||
max(6, record_size), buffer->cbBuffer);
|
||||
return SEC_E_INCOMPLETE_MESSAGE;
|
||||
while (buffer->cbBuffer > expected_size + ctx->header_size)
|
||||
{
|
||||
record_size = ctx->header_size + read_record_size(ptr, ctx->header_size);
|
||||
|
||||
if (buffer->cbBuffer < expected_size + record_size)
|
||||
break;
|
||||
|
||||
expected_size += record_size;
|
||||
ptr += record_size;
|
||||
}
|
||||
|
||||
if (!expected_size)
|
||||
{
|
||||
TRACE("Expected at least %lu bytes, but buffer only contains %u bytes.\n",
|
||||
max(6, record_size), buffer->cbBuffer);
|
||||
return SEC_E_INCOMPLETE_MESSAGE;
|
||||
}
|
||||
}
|
||||
else if (!is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE;
|
||||
|
||||
TRACE("Using expected_size %lu.\n", expected_size);
|
||||
|
||||
ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX);
|
||||
}
|
||||
|
||||
ctx->req_ctx_attr = fContextReq;
|
||||
|
@ -1038,11 +1059,11 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
|
|||
unsigned int block_size = schan_imp_get_session_cipher_block_size(ctx->session);
|
||||
unsigned int message_size = schan_imp_get_max_message_size(ctx->session);
|
||||
|
||||
TRACE("Using %lu mac bytes, message size %u, block size %u\n",
|
||||
mac_size, message_size, block_size);
|
||||
TRACE("Using header size %lu mac bytes %lu, message size %u, block size %u\n",
|
||||
ctx->header_size, mac_size, message_size, block_size);
|
||||
|
||||
/* These are defined by the TLS RFC */
|
||||
stream_sizes->cbHeader = HEADER_SIZE_TLS;
|
||||
stream_sizes->cbHeader = ctx->header_size;
|
||||
stream_sizes->cbTrailer = mac_size + 256; /* Max 255 bytes padding + 1 for padding size */
|
||||
stream_sizes->cbMaximumMessage = message_size;
|
||||
stream_sizes->cbBuffers = 4;
|
||||
|
@ -1367,7 +1388,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
|
|||
buffer = &message->pBuffers[idx];
|
||||
buf_ptr = buffer->pvBuffer;
|
||||
|
||||
expected_size = HEADER_SIZE_TLS + read_record_size(buf_ptr, HEADER_SIZE_TLS);
|
||||
expected_size = ctx->header_size + read_record_size(buf_ptr, ctx->header_size);
|
||||
if(buffer->cbBuffer < expected_size)
|
||||
{
|
||||
TRACE("Expected %u bytes, but buffer only contains %u bytes\n", expected_size, buffer->cbBuffer);
|
||||
|
@ -1384,7 +1405,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
|
|||
return SEC_E_INCOMPLETE_MESSAGE;
|
||||
}
|
||||
|
||||
data_size = expected_size - HEADER_SIZE_TLS;
|
||||
data_size = expected_size - ctx->header_size;
|
||||
data = heap_alloc(data_size);
|
||||
|
||||
init_schan_buffers(&ctx->transport.in, message, schan_decrypt_message_get_next_buffer);
|
||||
|
@ -1419,21 +1440,21 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
|
|||
|
||||
TRACE("Received %ld bytes\n", received);
|
||||
|
||||
memcpy(buf_ptr + HEADER_SIZE_TLS, data, received);
|
||||
memcpy(buf_ptr + ctx->header_size, data, received);
|
||||
heap_free(data);
|
||||
|
||||
schan_decrypt_fill_buffer(message, SECBUFFER_DATA,
|
||||
buf_ptr + HEADER_SIZE_TLS, received);
|
||||
buf_ptr + ctx->header_size, received);
|
||||
|
||||
schan_decrypt_fill_buffer(message, SECBUFFER_STREAM_TRAILER,
|
||||
buf_ptr + HEADER_SIZE_TLS + received, buffer->cbBuffer - HEADER_SIZE_TLS - received);
|
||||
buf_ptr + ctx->header_size + received, buffer->cbBuffer - ctx->header_size - received);
|
||||
|
||||
if(buffer->cbBuffer > expected_size)
|
||||
schan_decrypt_fill_buffer(message, SECBUFFER_EXTRA,
|
||||
buf_ptr + expected_size, buffer->cbBuffer - expected_size);
|
||||
|
||||
buffer->BufferType = SECBUFFER_STREAM_HEADER;
|
||||
buffer->cbBuffer = HEADER_SIZE_TLS;
|
||||
buffer->cbBuffer = ctx->header_size;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
|
|||
/* Not present in gnutls version < 2.9.10. */
|
||||
static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t);
|
||||
|
||||
/* Not present in gnutls version < 3.0. */
|
||||
static void (*pgnutls_transport_set_pull_timeout_function)(gnutls_session_t,
|
||||
int (*)(gnutls_transport_ptr_t, unsigned int));
|
||||
|
||||
/* Not present in gnutls version < 3.2.0. */
|
||||
static int (*pgnutls_alpn_get_selected_protocol)(gnutls_session_t, gnutls_datum_t *);
|
||||
static int (*pgnutls_alpn_set_protocols)(gnutls_session_t, const gnutls_datum_t *,
|
||||
|
@ -147,6 +151,12 @@ static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
|
|||
}
|
||||
}
|
||||
|
||||
static void compat_gnutls_transport_set_pull_timeout_function(gnutls_session_t session,
|
||||
int (*func)(gnutls_transport_ptr_t, unsigned int))
|
||||
{
|
||||
FIXME("\n");
|
||||
}
|
||||
|
||||
static int compat_gnutls_privkey_export_x509(gnutls_privkey_t privkey, gnutls_x509_privkey_t *key)
|
||||
{
|
||||
FIXME("\n");
|
||||
|
@ -212,6 +222,8 @@ static const struct {
|
|||
DWORD enable_flag;
|
||||
const char *gnutls_flag;
|
||||
} protocol_priority_flags[] = {
|
||||
{SP_PROT_DTLS1_2_CLIENT, "VERS-DTLS1.2"},
|
||||
{SP_PROT_DTLS1_0_CLIENT, "VERS-DTLS1.0"},
|
||||
{SP_PROT_TLS1_3_CLIENT, "VERS-TLS1.3"},
|
||||
{SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"},
|
||||
{SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"},
|
||||
|
@ -257,14 +269,29 @@ DWORD schan_imp_enabled_protocols(void)
|
|||
return supported_protocols;
|
||||
}
|
||||
|
||||
static int schan_pull_timeout(gnutls_transport_ptr_t transport, unsigned int timeout)
|
||||
{
|
||||
struct schan_transport *t = (struct schan_transport *)transport;
|
||||
SIZE_T count = 0;
|
||||
|
||||
if (schan_get_buffer(t, &t->in, &count)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
|
||||
{
|
||||
gnutls_session_t *s = (gnutls_session_t*)session;
|
||||
char priority[128] = "NORMAL:%LATEST_RECORD_VERSION", *p;
|
||||
BOOL using_vers_all = FALSE, disabled;
|
||||
unsigned i;
|
||||
unsigned int i, flags = (cred->credential_use == SECPKG_CRED_INBOUND) ? GNUTLS_SERVER : GNUTLS_CLIENT;
|
||||
int err;
|
||||
|
||||
int err = pgnutls_init(s, cred->credential_use == SECPKG_CRED_INBOUND ? GNUTLS_SERVER : GNUTLS_CLIENT);
|
||||
if (cred->enabled_protocols & (SP_PROT_DTLS1_0_CLIENT | SP_PROT_DTLS1_2_CLIENT))
|
||||
{
|
||||
flags |= GNUTLS_DATAGRAM | GNUTLS_NONBLOCK;
|
||||
}
|
||||
|
||||
err = pgnutls_init(s, flags);
|
||||
if (err != GNUTLS_E_SUCCESS)
|
||||
{
|
||||
pgnutls_perror(err);
|
||||
|
@ -315,6 +342,7 @@ BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cre
|
|||
}
|
||||
|
||||
pgnutls_transport_set_pull_function(*s, schan_pull_adapter);
|
||||
if (flags & GNUTLS_DATAGRAM) pgnutls_transport_set_pull_timeout_function(*s, schan_pull_timeout);
|
||||
pgnutls_transport_set_push_function(*s, schan_push_adapter);
|
||||
|
||||
return TRUE;
|
||||
|
@ -400,6 +428,8 @@ static DWORD schannel_get_protocol(gnutls_protocol_t proto)
|
|||
case GNUTLS_TLS1_0: return SP_PROT_TLS1_0_CLIENT;
|
||||
case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT;
|
||||
case GNUTLS_TLS1_2: return SP_PROT_TLS1_2_CLIENT;
|
||||
case GNUTLS_DTLS1_0: return SP_PROT_DTLS1_0_CLIENT;
|
||||
case GNUTLS_DTLS1_2: return SP_PROT_DTLS1_2_CLIENT;
|
||||
default:
|
||||
FIXME("unknown protocol %d\n", proto);
|
||||
return 0;
|
||||
|
@ -1085,6 +1115,11 @@ BOOL schan_imp_init(void)
|
|||
WARN("gnutls_cipher_get_block_size not found\n");
|
||||
pgnutls_cipher_get_block_size = compat_cipher_get_block_size;
|
||||
}
|
||||
if (!(pgnutls_transport_set_pull_timeout_function = dlsym(libgnutls_handle, "gnutls_transport_set_pull_timeout_function")))
|
||||
{
|
||||
WARN("gnutls_transport_set_pull_timeout_function not found\n");
|
||||
pgnutls_transport_set_pull_timeout_function = compat_gnutls_transport_set_pull_timeout_function;
|
||||
}
|
||||
if (!(pgnutls_alpn_set_protocols = dlsym(libgnutls_handle, "gnutls_alpn_set_protocols")))
|
||||
{
|
||||
WARN("gnutls_alpn_set_protocols not found\n");
|
||||
|
|
Loading…
Reference in New Issue