From fc4fc052359dc3a2520cbbec4d9c280da5063fe5 Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Mon, 14 Nov 2011 21:24:10 +0200 Subject: [PATCH] tls: internalize state handling sip-sec-tls-dsk.c only drives the state machine and copies out the result. --- src/core/sip-sec-tls-dsk.c | 82 ++++++++++-------------------- src/core/sipe-tls.c | 124 +++++++++++++++++++++++++++++++++++++++------ src/core/sipe-tls.h | 46 ++++++++++++++--- 3 files changed, 177 insertions(+), 75 deletions(-) diff --git a/src/core/sip-sec-tls-dsk.c b/src/core/sip-sec-tls-dsk.c index 83b58cd8..f9b6b3ad 100644 --- a/src/core/sip-sec-tls-dsk.c +++ b/src/core/sip-sec-tls-dsk.c @@ -39,24 +39,11 @@ #include "sipe-utils.h" /* Security context for TLS-DSK */ -enum tls_dsk_state { - TLS_DSK_STATE_START, - TLS_DSK_STATE_SERVER_HELLO, - TLS_DSK_STATE_FINISHED, - TLS_DSK_STATE_COMPLETED, - TLS_DSK_STATE_FAILED -}; - typedef struct _context_tls_dsk { struct sip_sec_context common; - gpointer certificate; - enum tls_dsk_state state; + struct sipe_tls_state *state; } *context_tls_dsk; -/* TLS-DSK implementation */ - -/* TBD.... */ - /* sip-sec-mech.h API implementation for TLS-DSK */ static sip_uint32 @@ -67,17 +54,15 @@ sip_sec_acquire_cred__tls_dsk(SipSecContext context, { context_tls_dsk ctx = (context_tls_dsk)context; - /* TLS-DSK requires a certificate. Everything else is ignored */ - if (!password) - return SIP_SEC_E_INTERNAL_ERROR; - - ctx->certificate = (gpointer) password; - ctx->state = TLS_DSK_STATE_START; - - /* Authentication not yet completed */ - ctx->common.is_ready = FALSE; + ctx->state = sipe_tls_start((gpointer) password); + if (ctx->state) { + /* Authentication not yet completed */ + ctx->common.is_ready = FALSE; - return SIP_SEC_E_OK; + return SIP_SEC_E_OK; + } else { + return SIP_SEC_E_INTERNAL_ERROR; + } } static sip_uint32 @@ -87,44 +72,32 @@ sip_sec_init_sec_context__tls_dsk(SipSecContext context, SIPE_UNUSED_PARAMETER const char *service_name) { context_tls_dsk ctx = (context_tls_dsk) context; + struct sipe_tls_state *state = ctx->state; - out_buff->value = NULL; + state->in_buffer = in_buff.value; + state->in_length = in_buff.length; - switch (ctx->state) { - case TLS_DSK_STATE_START: - ctx->state = TLS_DSK_STATE_SERVER_HELLO; - - out_buff->value = sipe_tls_client_hello(&out_buff->length); - break; - - case TLS_DSK_STATE_SERVER_HELLO: - ctx->state = TLS_DSK_STATE_FINISHED; - - out_buff->value = sipe_tls_server_hello(in_buff.value, - in_buff.length, - &out_buff->length); - break; - - case TLS_DSK_STATE_FINISHED: - if (sipe_tls_finished(in_buff.value, - in_buff.length)) { + if (sipe_tls_next(state)) { + if (state->session_key) { /* Authentication is completed */ - ctx->state = TLS_DSK_STATE_COMPLETED; ctx->common.is_ready = TRUE; + + /* TBD... copy session key */ + + sipe_tls_free(state); + ctx->state = NULL; } else { - ctx->state = TLS_DSK_STATE_FAILED; + out_buff->value = state->out_buffer; + out_buff->length = state->out_length; + /* we take ownership of the buffer */ + state->out_buffer = NULL; } - break; - - case TLS_DSK_STATE_COMPLETED: - case TLS_DSK_STATE_FAILED: - /* This should not happen */ - ctx->state = TLS_DSK_STATE_FAILED; - break; + } else { + sipe_tls_free(state); + ctx->state = NULL; } - return(((ctx->state == TLS_DSK_STATE_COMPLETED) || out_buff->value) ? - SIP_SEC_E_OK : SIP_SEC_E_INTERNAL_ERROR); + return((ctx->common.is_ready || ctx->state) ? SIP_SEC_E_OK : SIP_SEC_E_INTERNAL_ERROR); } static sip_uint32 @@ -162,6 +135,7 @@ sip_sec_destroy_sec_context__tls_dsk(SipSecContext context) { context_tls_dsk ctx = (context_tls_dsk) context; + sipe_tls_free(ctx->state); g_free(ctx); } diff --git a/src/core/sipe-tls.c b/src/core/sipe-tls.c index 6b3d7d7c..a1e80876 100644 --- a/src/core/sipe-tls.c +++ b/src/core/sipe-tls.c @@ -45,6 +45,23 @@ #include "sipe-tls.h" /* + * Private part of TLS state tracking + */ +enum tls_handshake_state { + TLS_HANDSHAKE_STATE_START, + TLS_HANDSHAKE_STATE_SERVER_HELLO, + TLS_HANDSHAKE_STATE_FINISHED, + TLS_HANDSHAKE_STATE_COMPLETED, + TLS_HANDSHAKE_STATE_FAILED +}; + +struct tls_internal_state { + struct sipe_tls_state common; + gpointer certificate; + enum tls_handshake_state state; +}; + +/* * TLS message debugging */ static void debug_hex(GString *str, @@ -401,7 +418,7 @@ static const guchar const client_hello[] = { #endif }; -guchar *sipe_tls_client_hello(gsize *out_length) +static gboolean tls_client_hello(struct tls_internal_state *state) { guchar *msg = g_memdup(client_hello, sizeof(client_hello)); guint32 now = time(NULL); @@ -413,41 +430,118 @@ guchar *sipe_tls_client_hello(gsize *out_length) for (p = msg + RANDOM_OFFSET, i = 0; i < 2; i++) *p++ = rand() & 0xFF; - *out_length = sizeof(client_hello); + state->common.out_buffer = msg; + state->common.out_length = sizeof(client_hello); + state->state = TLS_HANDSHAKE_STATE_SERVER_HELLO; + tls_record_parse(msg, sizeof(client_hello), FALSE); - return(msg); + + return(TRUE); } -guchar *sipe_tls_server_hello(const guchar *incoming, - gsize in_length, - gsize *out_length) +static gboolean tls_server_hello(struct tls_internal_state *state) { - guchar *outgoing = NULL; - gpointer parsed_data = tls_record_parse(incoming, in_length, TRUE); + gpointer parsed_data = tls_record_parse(state->common.in_buffer, + state->common.in_length, + TRUE); if (!parsed_data) - return(NULL); + return(FALSE); /* temporary */ - *out_length = 0; + state->common.out_buffer = NULL; + state->common.out_length = 0; + state->state = TLS_HANDSHAKE_STATE_FINISHED; + + tls_record_parse(state->common.out_buffer, + state->common.out_length, + FALSE); - tls_record_parse(outgoing, *out_length, FALSE); - return(outgoing); + return(state->common.out_buffer != NULL); } -gboolean sipe_tls_finished(const guchar *incoming, - gsize in_length) +static gboolean tls_finished(struct tls_internal_state *state) { - gpointer parsed_data = tls_record_parse(incoming, in_length, TRUE); + gpointer parsed_data = tls_record_parse(state->common.in_buffer, + state->common.in_length, + TRUE); if (!parsed_data) return(FALSE); /* TBD: data is really not needed? */ free_parsed_data(parsed_data); + + state->common.out_buffer = NULL; + state->common.out_length = 0; + state->state = TLS_HANDSHAKE_STATE_COMPLETED; + + /* temporary */ return(TRUE); } +/* Public API */ + +struct sipe_tls_state *sipe_tls_start(gpointer certificate) +{ + struct tls_internal_state *state; + + if (!certificate) + return(NULL); + + state = g_new0(struct tls_internal_state, 1); + state->certificate = certificate; + state->state = TLS_HANDSHAKE_STATE_START; + + return((struct sipe_tls_state *) state); +} + +gboolean sipe_tls_next(struct sipe_tls_state *state) +{ + struct tls_internal_state *internal = (struct tls_internal_state *) state; + gboolean success = FALSE; + + if (!state) + return(FALSE); + + state->out_buffer = NULL; + + switch (internal->state) { + case TLS_HANDSHAKE_STATE_START: + success = tls_client_hello(internal); + break; + + case TLS_HANDSHAKE_STATE_SERVER_HELLO: + success = tls_server_hello(internal); + break; + + case TLS_HANDSHAKE_STATE_FINISHED: + success = tls_finished(internal); + break; + + case TLS_HANDSHAKE_STATE_COMPLETED: + case TLS_HANDSHAKE_STATE_FAILED: + /* This should not happen */ + SIPE_DEBUG_ERROR_NOFORMAT("sipe_tls_next: called in incorrect state!"); + break; + } + + if (!success) { + internal->state = TLS_HANDSHAKE_STATE_FAILED; + } + + return(success); +} + +void sipe_tls_free(struct sipe_tls_state *state) +{ + if (state) { + g_free(state->session_key); + g_free(state->out_buffer); + g_free(state); + } +} + /* Local Variables: mode: c diff --git a/src/core/sipe-tls.h b/src/core/sipe-tls.h index 47e06a50..e74d4fe7 100644 --- a/src/core/sipe-tls.h +++ b/src/core/sipe-tls.h @@ -27,9 +27,43 @@ * */ -guchar *sipe_tls_client_hello(gsize *length); -guchar *sipe_tls_server_hello(const guchar *incoming, - gsize in_length, - gsize *out_length); -gboolean sipe_tls_finished(const guchar *incoming, - gsize in_length); +/** + * Public part of TLS state tracking + * + * If @c session_key != @c NULL then handshake is complete + */ +struct sipe_tls_state { + const guchar *in_buffer; + guchar *out_buffer; + gsize in_length; + gsize out_length; + guchar *session_key; + gsize key_length; +}; + +/** + * Initialize TLS state + * + * @param certificate opaque pointer to the user certificate + * + * @return TLS state structure + */ +struct sipe_tls_state *sipe_tls_start(gpointer certificate); + +/** + * Proceed to next TLS state + * + * @param state pointer to TLS state structure + * @param incoming pointer to incoming message (NULL for initial transition) + * @param in_length length of incoming message + * + * @return TLS state structure + */ +gboolean sipe_tls_next(struct sipe_tls_state *state); + +/** + * Free TLS state + * + * @param state pointer to TLS state structure + */ +void sipe_tls_free(struct sipe_tls_state *state); -- 2.11.4.GIT