secur32: Manage gnutls transport data in unixlib.

Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
This commit is contained in:
Nikolay Sivov 2022-05-30 15:53:27 +03:00 committed by Alexandre Julliard
parent 3f2cdb6820
commit c1d9d6e06e
3 changed files with 97 additions and 76 deletions

View file

@ -61,7 +61,7 @@ struct schan_handle
struct schan_context
{
struct schan_transport transport;
schan_session session;
ULONG req_ctx_attr;
const CERT_CONTEXT *cert;
SIZE_T header_size;
@ -758,14 +758,15 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
{
const ULONG extra_size = 0x10000;
struct schan_context *ctx;
struct schan_buffers *out_buffers;
struct schan_credentials *cred;
SIZE_T expected_size = 0;
SECURITY_STATUS ret;
SecBuffer *buffer;
SecBuffer alloc_buffer = { 0 };
struct handshake_params params;
struct handshake_params params = { 0 };
int output_buffer_idx = -1;
int idx, i;
ULONG input_offset = 0, output_offset = 0;
TRACE("%p %p %s 0x%08lx %ld %ld %p %ld %p %p %p %p\n", phCredential, phContext,
debugstr_w(pszTargetName), fContextReq, Reserved1, TargetDataRep, pInput,
@ -815,9 +816,8 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
return SEC_E_INTERNAL_ERROR;
}
create_params.transport = &ctx->transport;
create_params.cred = cred;
create_params.session = &ctx->transport.session;
create_params.session = &ctx->session;
if (GNUTLS_CALL( create_session, &create_params ))
{
schan_free_handle(handle, SCHAN_HANDLE_CTX);
@ -837,7 +837,7 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
if (target)
{
struct set_session_target_params params = { ctx->transport.session, target };
struct set_session_target_params params = { ctx->session, target };
WideCharToMultiByte( CP_UNIXCP, 0, pszTargetName, -1, target, len, NULL, NULL );
GNUTLS_CALL( set_session_target, &params );
free( target );
@ -846,8 +846,8 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
if (pInput && (idx = schan_find_sec_buffer_idx(pInput, 0, SECBUFFER_APPLICATION_PROTOCOLS)) != -1)
{
struct set_application_protocols_params params = { ctx->transport.session,
pInput->pBuffers[idx].pvBuffer, pInput->pBuffers[idx].cbBuffer };
struct set_application_protocols_params params = { ctx->session, pInput->pBuffers[idx].pvBuffer,
pInput->pBuffers[idx].cbBuffer };
GNUTLS_CALL( set_application_protocols, &params );
}
@ -856,7 +856,7 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
buffer = &pInput->pBuffers[idx];
if (buffer->cbBuffer >= sizeof(WORD))
{
struct set_dtls_mtu_params params = { ctx->transport.session, *(WORD *)buffer->pvBuffer };
struct set_dtls_mtu_params params = { ctx->session, *(WORD *)buffer->pvBuffer };
GNUTLS_CALL( set_dtls_mtu, &params );
}
else WARN("invalid buffer size %lu\n", buffer->cbBuffer);
@ -864,7 +864,7 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
if (is_dtls_context(ctx))
{
struct set_dtls_timeouts_params params = { ctx->transport.session, 0, 60000 };
struct set_dtls_timeouts_params params = { ctx->session, 0, 60000 };
GNUTLS_CALL( set_dtls_timeouts, &params );
}
@ -917,18 +917,20 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
alloc_buffer.BufferType = SECBUFFER_TOKEN;
alloc_buffer.pvBuffer = RtlAllocateHeap( GetProcessHeap(), 0, extra_size );
}
params.session = ctx->transport.session;
params.session = ctx->session;
params.input = pInput;
params.input_size = expected_size;
params.output = pOutput;
params.alloc_buffer = &alloc_buffer;
params.input_offset = &input_offset;
params.output_buffer_idx = &output_buffer_idx;
params.output_offset = &output_offset;
ret = GNUTLS_CALL( handshake, &params );
out_buffers = &ctx->transport.out;
if (out_buffers->current_buffer_idx != -1)
if (output_buffer_idx != -1)
{
SecBuffer *buffer = &out_buffers->desc->pBuffers[out_buffers->current_buffer_idx];
buffer->cbBuffer = out_buffers->offset;
SecBuffer *buffer = &pOutput->pBuffers[output_buffer_idx];
buffer->cbBuffer = output_offset;
if (buffer->pvBuffer == alloc_buffer.pvBuffer)
{
RtlReAllocateHeap( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY,
@ -936,19 +938,19 @@ static SECURITY_STATUS SEC_ENTRY schan_InitializeSecurityContextW(
alloc_buffer.pvBuffer = NULL;
}
}
else if (out_buffers->desc && out_buffers->desc->cBuffers > 0)
else if (pOutput && pOutput->cBuffers)
{
SecBuffer *buffer = &out_buffers->desc->pBuffers[0];
buffer->cbBuffer = 0;
pOutput->pBuffers[0].cbBuffer = 0;
}
RtlFreeHeap( GetProcessHeap(), 0, alloc_buffer.pvBuffer );
if(ctx->transport.in.offset && ctx->transport.in.offset != pInput->pBuffers[0].cbBuffer) {
if (input_offset && input_offset != pInput->pBuffers[0].cbBuffer)
{
if(pInput->cBuffers<2 || pInput->pBuffers[1].BufferType!=SECBUFFER_EMPTY)
return SEC_E_INVALID_TOKEN;
pInput->pBuffers[1].BufferType = SECBUFFER_EXTRA;
pInput->pBuffers[1].cbBuffer = pInput->pBuffers[0].cbBuffer-ctx->transport.in.offset;
pInput->pBuffers[1].cbBuffer = pInput->pBuffers[0].cbBuffer - input_offset;
}
for (i = 0; i < pOutput->cBuffers; i++)
@ -1031,7 +1033,7 @@ static SECURITY_STATUS ensure_remote_cert(struct schan_context *ctx)
PCCERT_CONTEXT cert = NULL;
SECURITY_STATUS status;
ULONG count, size = 0;
struct get_session_peer_certificate_params params = { ctx->transport.session, NULL, &size, &count };
struct get_session_peer_certificate_params params = { ctx->session, NULL, &size, &count };
if (ctx->cert) return SEC_E_OK;
if (!(store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL)))
@ -1089,11 +1091,11 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
case SECPKG_ATTR_STREAM_SIZES:
{
SecPkgContext_ConnectionInfo info;
struct get_connection_info_params params = { ctx->transport.session, &info };
struct get_connection_info_params params = { ctx->session, &info };
status = GNUTLS_CALL( get_connection_info, &params );
if (status == SEC_E_OK)
{
struct session_params params = { ctx->transport.session };
struct session_params params = { ctx->session };
SecPkgContext_StreamSizes *stream_sizes = buffer;
SIZE_T mac_size = info.dwHashStrength;
unsigned int block_size = GNUTLS_CALL( get_session_cipher_block_size, &params );
@ -1115,11 +1117,11 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
case SECPKG_ATTR_KEY_INFO:
{
SecPkgContext_ConnectionInfo conn_info;
struct get_connection_info_params params = { ctx->transport.session, &conn_info };
struct get_connection_info_params params = { ctx->session, &conn_info };
status = GNUTLS_CALL( get_connection_info, &params );
if (status == SEC_E_OK)
{
struct session_params params = { ctx->transport.session };
struct session_params params = { ctx->session };
SecPkgContext_KeyInfoW *info = buffer;
info->KeySize = conn_info.dwCipherStrength;
info->SignatureAlgorithm = GNUTLS_CALL( get_key_signature_algorithm, &params );
@ -1143,7 +1145,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
case SECPKG_ATTR_CONNECTION_INFO:
{
SecPkgContext_ConnectionInfo *info = buffer;
struct get_connection_info_params params = { ctx->transport.session, info };
struct get_connection_info_params params = { ctx->session, info };
return GNUTLS_CALL( get_connection_info, &params );
}
case SECPKG_ATTR_ENDPOINT_BINDINGS:
@ -1193,7 +1195,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
SecPkgContext_Bindings *bindings = buffer;
ULONG size;
char *p;
struct get_unique_channel_binding_params params = { ctx->transport.session, NULL, &size };
struct get_unique_channel_binding_params params = { ctx->session, NULL, &size };
if (GNUTLS_CALL( get_unique_channel_binding, &params ) != SEC_E_BUFFER_TOO_SMALL)
return SEC_E_INTERNAL_ERROR;
@ -1216,7 +1218,7 @@ static SECURITY_STATUS SEC_ENTRY schan_QueryContextAttributesW(
case SECPKG_ATTR_APPLICATION_PROTOCOL:
{
SecPkgContext_ApplicationProtocol *protocol = buffer;
struct get_application_protocol_params params = { ctx->transport.session, protocol };
struct get_application_protocol_params params = { ctx->session, protocol };
return GNUTLS_CALL( get_application_protocol, &params );
}
@ -1297,7 +1299,7 @@ static SECURITY_STATUS SEC_ENTRY schan_EncryptMessage(PCtxtHandle context_handle
memcpy(data, buffer->pvBuffer, data_size);
length = data_size;
params.session = ctx->transport.session;
params.session = ctx->session;
params.output = message;
params.buffer = data;
params.length = &length;
@ -1421,7 +1423,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DecryptMessage(PCtxtHandle context_handle
received = data_size;
params.session = ctx->transport.session;
params.session = ctx->session;
params.input = message;
params.input_size = expected_size;
params.buffer = data;
@ -1469,7 +1471,7 @@ static SECURITY_STATUS SEC_ENTRY schan_DeleteSecurityContext(PCtxtHandle context
if (!ctx) return SEC_E_INVALID_HANDLE;
if (ctx->cert) CertFreeCertificateContext(ctx->cert);
params.session = ctx->transport.session;
params.session = ctx->session;
GNUTLS_CALL( dispose_session, &params );
free(ctx);
return SEC_E_OK;
@ -1610,7 +1612,7 @@ void SECUR32_deinitSchannelSP(void)
if (schan_handle_table[i].type == SCHAN_HANDLE_CTX)
{
struct schan_context *ctx = schan_free_handle(i, SCHAN_HANDLE_CTX);
struct session_params params = { ctx->transport.session };
struct session_params params = { ctx->session };
GNUTLS_CALL( dispose_session, &params );
free(ctx);
}

View file

@ -141,6 +141,23 @@ static inline gnutls_session_t session_from_handle(UINT64 handle)
return (gnutls_session_t)(ULONG_PTR)handle;
}
struct schan_buffers
{
SIZE_T offset;
SIZE_T limit;
const SecBufferDesc *desc;
SecBuffer *alloc_buffer;
int current_buffer_idx;
int (*get_next_buffer)(struct schan_buffers *);
};
struct schan_transport
{
gnutls_session_t session;
struct schan_buffers in;
struct schan_buffers out;
};
static int compat_cipher_get_block_size(gnutls_cipher_algorithm_t cipher)
{
switch(cipher) {
@ -374,7 +391,6 @@ static char *get_buffer(struct schan_buffers *s, SIZE_T *count)
static ssize_t pull_adapter(gnutls_transport_ptr_t transport, void *buff, size_t buff_len)
{
struct schan_transport *t = (struct schan_transport*)transport;
gnutls_session_t s = session_from_handle(t->session);
SIZE_T len = buff_len;
char *b;
@ -383,7 +399,7 @@ static ssize_t pull_adapter(gnutls_transport_ptr_t transport, void *buff, size_t
b = get_buffer(&t->in, &len);
if (!b)
{
pgnutls_transport_set_errno(s, EAGAIN);
pgnutls_transport_set_errno(t->session, EAGAIN);
return -1;
}
memcpy(buff, b, len);
@ -395,7 +411,6 @@ static ssize_t pull_adapter(gnutls_transport_ptr_t transport, void *buff, size_t
static ssize_t push_adapter(gnutls_transport_ptr_t transport, const void *buff, size_t buff_len)
{
struct schan_transport *t = (struct schan_transport*)transport;
gnutls_session_t s = session_from_handle(t->session);
SIZE_T len = buff_len;
char *b;
@ -404,7 +419,7 @@ static ssize_t push_adapter(gnutls_transport_ptr_t transport, const void *buff,
b = get_buffer(&t->out, &len);
if (!b)
{
pgnutls_transport_set_errno(s, EAGAIN);
pgnutls_transport_set_errno(t->session, EAGAIN);
return -1;
}
memcpy(b, buff, len);
@ -483,6 +498,7 @@ static NTSTATUS schan_create_session( void *args )
char priority[128] = "NORMAL:%LATEST_RECORD_VERSION", *p;
BOOL using_vers_all = FALSE, disabled;
unsigned int i, flags = (cred->credential_use == SECPKG_CRED_INBOUND) ? GNUTLS_SERVER : GNUTLS_CLIENT;
struct schan_transport *transport;
gnutls_session_t s;
int err;
@ -500,6 +516,13 @@ static NTSTATUS schan_create_session( void *args )
return STATUS_INTERNAL_ERROR;
}
if (!(transport = calloc(1, sizeof(*transport))))
{
pgnutls_deinit(s);
return STATUS_INTERNAL_ERROR;
}
transport->session = s;
p = priority + strlen(priority);
/* VERS-ALL is nice to use for forward compatibility. It was introduced before support for TLS1.3,
@ -531,6 +554,7 @@ static NTSTATUS schan_create_session( void *args )
{
pgnutls_perror(err);
pgnutls_deinit(s);
free(transport);
return STATUS_INTERNAL_ERROR;
}
@ -540,13 +564,14 @@ static NTSTATUS schan_create_session( void *args )
{
pgnutls_perror(err);
pgnutls_deinit(s);
free(transport);
return STATUS_INTERNAL_ERROR;
}
pgnutls_transport_set_pull_function(s, pull_adapter);
if (flags & GNUTLS_DATAGRAM) pgnutls_transport_set_pull_timeout_function(s, pull_timeout);
pgnutls_transport_set_push_function(s, push_adapter);
pgnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)params->transport);
pgnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)transport);
*params->session = (ULONG_PTR)s;
return STATUS_SUCCESS;
@ -556,7 +581,10 @@ static NTSTATUS schan_dispose_session( void *args )
{
const struct session_params *params = args;
gnutls_session_t s = session_from_handle(params->session);
struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
pgnutls_transport_set_ptr(s, NULL);
pgnutls_deinit(s);
free(t);
return STATUS_SUCCESS;
}
@ -573,6 +601,7 @@ static NTSTATUS schan_handshake( void *args )
const struct handshake_params *params = args;
gnutls_session_t s = session_from_handle(params->session);
struct schan_transport *t = (struct schan_transport *)pgnutls_transport_get_ptr(s);
NTSTATUS status;
int err;
init_schan_buffers(&t->in, params->input, handshake_get_next_buffer);
@ -580,47 +609,52 @@ static NTSTATUS schan_handshake( void *args )
init_schan_buffers(&t->out, params->output, handshake_get_next_buffer_alloc );
t->out.alloc_buffer = params->alloc_buffer;
while(1) {
while (1)
{
err = pgnutls_handshake(s);
switch(err) {
case GNUTLS_E_SUCCESS:
if (err == GNUTLS_E_SUCCESS)
{
TRACE("Handshake completed\n");
return SEC_E_OK;
case GNUTLS_E_AGAIN:
status = SEC_E_OK;
}
else if (err == GNUTLS_E_AGAIN)
{
TRACE("Continue...\n");
return SEC_I_CONTINUE_NEEDED;
case GNUTLS_E_WARNING_ALERT_RECEIVED:
status = SEC_I_CONTINUE_NEEDED;
}
else if (err == GNUTLS_E_WARNING_ALERT_RECEIVED)
{
gnutls_alert_description_t alert = pgnutls_alert_get(s);
WARN("WARNING ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
switch(alert) {
case GNUTLS_A_UNRECOGNIZED_NAME:
if (alert == GNUTLS_A_UNRECOGNIZED_NAME)
{
TRACE("Ignoring\n");
continue;
default:
return SEC_E_INTERNAL_ERROR;
}
else
status = SEC_E_INTERNAL_ERROR;
}
case GNUTLS_E_FATAL_ALERT_RECEIVED:
else if (err == GNUTLS_E_FATAL_ALERT_RECEIVED)
{
gnutls_alert_description_t alert = pgnutls_alert_get(s);
WARN("FATAL ALERT: %d %s\n", alert, pgnutls_alert_get_name(alert));
return SEC_E_INTERNAL_ERROR;
status = SEC_E_INTERNAL_ERROR;
}
default:
else
{
pgnutls_perror(err);
return SEC_E_INTERNAL_ERROR;
status = SEC_E_INTERNAL_ERROR;
}
break;
}
/* Never reached */
return SEC_E_OK;
*params->input_offset = t->in.offset;
*params->output_buffer_idx = t->out.current_buffer_idx;
*params->output_offset = t->out.offset;
return status;
}
static DWORD get_protocol(gnutls_protocol_t proto)

View file

@ -88,23 +88,6 @@ typedef struct schan_credentials
DWORD enabled_protocols;
} schan_credentials;
struct schan_buffers
{
SIZE_T offset;
SIZE_T limit;
const SecBufferDesc *desc;
SecBuffer *alloc_buffer;
int current_buffer_idx;
int (*get_next_buffer)(struct schan_buffers *);
};
struct schan_transport
{
schan_session session;
struct schan_buffers in;
struct schan_buffers out;
};
struct session_params
{
schan_session session;
@ -122,7 +105,6 @@ struct allocate_certificate_credentials_params
struct create_session_params
{
struct schan_transport *transport;
schan_credentials *cred;
schan_session *session;
};
@ -166,6 +148,9 @@ struct handshake_params
SIZE_T input_size;
SecBufferDesc *output;
SecBuffer *alloc_buffer;
ULONG *input_offset;
int *output_buffer_idx;
ULONG *output_offset;
};
struct recv_params