Commit f93284df authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

secur32: Add DTLS support.

parent 37b29862
...@@ -60,6 +60,7 @@ struct schan_context ...@@ -60,6 +60,7 @@ struct schan_context
struct schan_transport transport; struct schan_transport transport;
ULONG req_ctx_attr; ULONG req_ctx_attr;
const CERT_CONTEXT *cert; const CERT_CONTEXT *cert;
SIZE_T header_size;
}; };
static struct schan_handle *schan_handle_table; static struct schan_handle *schan_handle_table;
...@@ -184,7 +185,9 @@ static void read_config(void) ...@@ -184,7 +185,9 @@ static void read_config(void)
{{'S','S','L',' ','3','.','0',0}, SP_PROT_SSL3_CLIENT, TRUE, FALSE}, {{'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','.','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','.','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 */ /* No need for thread safety */
...@@ -399,10 +402,17 @@ static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schan ...@@ -399,10 +402,17 @@ static SECURITY_STATUS schan_AcquireClientCredentials(const SCHANNEL_CRED *schan
if (schanCred) 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); status = get_cert(schanCred, &cert);
if (status != SEC_E_OK && status != SEC_E_NO_CREDENTIALS) if (status != SEC_E_OK && status != SEC_E_NO_CREDENTIALS)
return status; return status;
if ((schanCred->grbitEnabledProtocols & tls_protocols) &&
(schanCred->grbitEnabledProtocols & dtls_protocols)) return SEC_E_ALGORITHM_MISMATCH;
status = SEC_E_OK; status = SEC_E_OK;
} }
...@@ -773,12 +783,18 @@ static void dump_buffer_desc(SecBufferDesc *desc) ...@@ -773,12 +783,18 @@ static void dump_buffer_desc(SecBufferDesc *desc)
} }
#define HEADER_SIZE_TLS 5 #define HEADER_SIZE_TLS 5
#define HEADER_SIZE_DTLS 13
static inline SIZE_T read_record_size(const BYTE *buf, SIZE_T header_size) static inline SIZE_T read_record_size(const BYTE *buf, SIZE_T header_size)
{ {
return (buf[header_size - 2] << 8) | buf[header_size - 1]; 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 * InitializeSecurityContextW
*/ */
...@@ -836,6 +852,11 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( ...@@ -836,6 +852,11 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
return SEC_E_INTERNAL_ERROR; 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; ctx->transport.ctx = ctx;
schan_imp_set_session_transport(ctx->session, &ctx->transport); schan_imp_set_session_transport(ctx->session, &ctx->transport);
...@@ -866,9 +887,9 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( ...@@ -866,9 +887,9 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
SIZE_T record_size = 0; SIZE_T record_size = 0;
unsigned char *ptr; unsigned char *ptr;
if (!pInput) ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX);
return SEC_E_INCOMPLETE_MESSAGE; if (pInput)
{
idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN); idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_TOKEN);
if (idx == -1) if (idx == -1)
return SEC_E_INCOMPLETE_MESSAGE; return SEC_E_INCOMPLETE_MESSAGE;
...@@ -877,9 +898,9 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( ...@@ -877,9 +898,9 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
ptr = buffer->pvBuffer; ptr = buffer->pvBuffer;
expected_size = 0; expected_size = 0;
while (buffer->cbBuffer > expected_size + HEADER_SIZE_TLS) while (buffer->cbBuffer > expected_size + ctx->header_size)
{ {
record_size = HEADER_SIZE_TLS + read_record_size(ptr, HEADER_SIZE_TLS); record_size = ctx->header_size + read_record_size(ptr, ctx->header_size);
if (buffer->cbBuffer < expected_size + record_size) if (buffer->cbBuffer < expected_size + record_size)
break; break;
...@@ -894,10 +915,10 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW( ...@@ -894,10 +915,10 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
max(6, record_size), buffer->cbBuffer); max(6, record_size), buffer->cbBuffer);
return SEC_E_INCOMPLETE_MESSAGE; return SEC_E_INCOMPLETE_MESSAGE;
} }
}
else if (!is_dtls_context(ctx)) return SEC_E_INCOMPLETE_MESSAGE;
TRACE("Using expected_size %lu.\n", expected_size); TRACE("Using expected_size %lu.\n", expected_size);
ctx = schan_get_object(phContext->dwLower, SCHAN_HANDLE_CTX);
} }
ctx->req_ctx_attr = fContextReq; ctx->req_ctx_attr = fContextReq;
...@@ -1038,11 +1059,11 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW( ...@@ -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 block_size = schan_imp_get_session_cipher_block_size(ctx->session);
unsigned int message_size = schan_imp_get_max_message_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", TRACE("Using header size %lu mac bytes %lu, message size %u, block size %u\n",
mac_size, message_size, block_size); ctx->header_size, mac_size, message_size, block_size);
/* These are defined by the TLS RFC */ /* 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->cbTrailer = mac_size + 256; /* Max 255 bytes padding + 1 for padding size */
stream_sizes->cbMaximumMessage = message_size; stream_sizes->cbMaximumMessage = message_size;
stream_sizes->cbBuffers = 4; stream_sizes->cbBuffers = 4;
...@@ -1367,7 +1388,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle ...@@ -1367,7 +1388,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
buffer = &message->pBuffers[idx]; buffer = &message->pBuffers[idx];
buf_ptr = buffer->pvBuffer; 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) if(buffer->cbBuffer < expected_size)
{ {
TRACE("Expected %u bytes, but buffer only contains %u bytes\n", expected_size, buffer->cbBuffer); 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 ...@@ -1384,7 +1405,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
return SEC_E_INCOMPLETE_MESSAGE; return SEC_E_INCOMPLETE_MESSAGE;
} }
data_size = expected_size - HEADER_SIZE_TLS; data_size = expected_size - ctx->header_size;
data = heap_alloc(data_size); data = heap_alloc(data_size);
init_schan_buffers(&ctx->transport.in, message, schan_decrypt_message_get_next_buffer); 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 ...@@ -1419,21 +1440,21 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
TRACE("Received %ld bytes\n", received); 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); heap_free(data);
schan_decrypt_fill_buffer(message, SECBUFFER_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, 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) if(buffer->cbBuffer > expected_size)
schan_decrypt_fill_buffer(message, SECBUFFER_EXTRA, schan_decrypt_fill_buffer(message, SECBUFFER_EXTRA,
buf_ptr + expected_size, buffer->cbBuffer - expected_size); buf_ptr + expected_size, buffer->cbBuffer - expected_size);
buffer->BufferType = SECBUFFER_STREAM_HEADER; buffer->BufferType = SECBUFFER_STREAM_HEADER;
buffer->cbBuffer = HEADER_SIZE_TLS; buffer->cbBuffer = ctx->header_size;
return status; return status;
} }
......
...@@ -50,6 +50,10 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag); ...@@ -50,6 +50,10 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
/* Not present in gnutls version < 2.9.10. */ /* Not present in gnutls version < 2.9.10. */
static int (*pgnutls_cipher_get_block_size)(gnutls_cipher_algorithm_t); 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. */ /* 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_get_selected_protocol)(gnutls_session_t, gnutls_datum_t *);
static int (*pgnutls_alpn_set_protocols)(gnutls_session_t, const 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) ...@@ -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) static int compat_gnutls_privkey_export_x509(gnutls_privkey_t privkey, gnutls_x509_privkey_t *key)
{ {
FIXME("\n"); FIXME("\n");
...@@ -212,6 +222,8 @@ static const struct { ...@@ -212,6 +222,8 @@ static const struct {
DWORD enable_flag; DWORD enable_flag;
const char *gnutls_flag; const char *gnutls_flag;
} protocol_priority_flags[] = { } 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_3_CLIENT, "VERS-TLS1.3"},
{SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"}, {SP_PROT_TLS1_2_CLIENT, "VERS-TLS1.2"},
{SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"}, {SP_PROT_TLS1_1_CLIENT, "VERS-TLS1.1"},
...@@ -257,14 +269,29 @@ DWORD schan_imp_enabled_protocols(void) ...@@ -257,14 +269,29 @@ DWORD schan_imp_enabled_protocols(void)
return supported_protocols; 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) BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cred)
{ {
gnutls_session_t *s = (gnutls_session_t*)session; gnutls_session_t *s = (gnutls_session_t*)session;
char priority[128] = "NORMAL:%LATEST_RECORD_VERSION", *p; char priority[128] = "NORMAL:%LATEST_RECORD_VERSION", *p;
BOOL using_vers_all = FALSE, disabled; 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) if (err != GNUTLS_E_SUCCESS)
{ {
pgnutls_perror(err); pgnutls_perror(err);
...@@ -315,6 +342,7 @@ BOOL schan_imp_create_session(schan_imp_session *session, schan_credentials *cre ...@@ -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); 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); pgnutls_transport_set_push_function(*s, schan_push_adapter);
return TRUE; return TRUE;
...@@ -400,6 +428,8 @@ static DWORD schannel_get_protocol(gnutls_protocol_t proto) ...@@ -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_0: return SP_PROT_TLS1_0_CLIENT;
case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT; case GNUTLS_TLS1_1: return SP_PROT_TLS1_1_CLIENT;
case GNUTLS_TLS1_2: return SP_PROT_TLS1_2_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: default:
FIXME("unknown protocol %d\n", proto); FIXME("unknown protocol %d\n", proto);
return 0; return 0;
...@@ -1085,6 +1115,11 @@ BOOL schan_imp_init(void) ...@@ -1085,6 +1115,11 @@ BOOL schan_imp_init(void)
WARN("gnutls_cipher_get_block_size not found\n"); WARN("gnutls_cipher_get_block_size not found\n");
pgnutls_cipher_get_block_size = compat_cipher_get_block_size; 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"))) if (!(pgnutls_alpn_set_protocols = dlsym(libgnutls_handle, "gnutls_alpn_set_protocols")))
{ {
WARN("gnutls_alpn_set_protocols not found\n"); WARN("gnutls_alpn_set_protocols not found\n");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment