From 78fd5267a0533ca82c2c7733f24ed62f13023f9c Mon Sep 17 00:00:00 2001 From: Thadeu Lima de Souza Cascardo Date: Thu, 2 Jul 2009 19:07:09 -0300 Subject: [PATCH] Added SSL client layer. This layer should be easily migrated into a server layer too. It should make things easier when communicating with SSL peers, since it's pretty transparent to the peer proxy connection. --- hcconn_ssl.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 hcconn_ssl.c diff --git a/hcconn_ssl.c b/hcconn_ssl.c new file mode 100644 index 0000000..5956acc --- /dev/null +++ b/hcconn_ssl.c @@ -0,0 +1,186 @@ +/* +** Copyright (C) 2006 Thadeu Lima de Souza Cascardo +** Copyright (C) 2009 Thadeu Lima de Souza Cascardo +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +** +*/ + +#include +#include +#include +#include +#include +#include "hcconn_internal.h" + +struct ssl_data +{ + gnutls_session_t session; + GString *buffer; + gboolean handshaking; + gpointer lowconn; +}; + +static struct ssl_data * +ssl_data_new (void) +{ + struct ssl_data *ssl; + int kx_prio[] = {GNUTLS_KX_RSA, 0}; + gnutls_certificate_credentials cred; + gnutls_certificate_allocate_credentials (&cred); + ssl = g_slice_new (struct ssl_data); + gnutls_init (&ssl->session, GNUTLS_CLIENT); + gnutls_set_default_priority (ssl->session); + gnutls_kx_set_priority (ssl->session, kx_prio); + gnutls_credentials_set (ssl->session, GNUTLS_CRD_CERTIFICATE, cred); + ssl->buffer = g_string_sized_new (4096); + ssl->handshaking = FALSE; + return ssl; +} + +static void +ssl_data_destroy (struct ssl_data *ssl) +{ + gnutls_deinit (ssl->session); + g_string_free (ssl->buffer, TRUE); + g_slice_free (struct ssl_data, ssl); +} + +static ssize_t +ssl_push (gnutls_transport_ptr_t ptr, const void *buffer, size_t len) +{ + HCConn *conn = ptr; + struct ssl_data *ssl = conn->layer; + hc_conn_write (ssl->lowconn, (void *) buffer, len); + return len; +} + +static ssize_t +ssl_pull (gnutls_transport_ptr_t ptr, void *buffer, size_t len) +{ + HCConn *conn = ptr; + struct ssl_data *ssl = conn->layer; + int r; + if (ssl->handshaking == TRUE) + { + r = hc_conn_read (ssl->lowconn, buffer, len); + return r; + } + if (len > ssl->buffer->len) + { + r = ssl->buffer->len; + memcpy (buffer, ssl->buffer->str, r); + g_string_truncate (ssl->buffer, 0); + } + else + { + r = len; + memcpy (buffer, ssl->buffer->str, r); + g_string_erase (ssl->buffer, 0, r); + } + if (r == 0) + { + gnutls_transport_set_errno (ssl->session, EAGAIN); + return -1; + } + return r; +} + +static void +ssl_server_handshake (struct ssl_data *ssl) +{ + int error; + if ((error = gnutls_handshake (ssl->session)) < 0) + { + if (gnutls_error_is_fatal (error)) + g_critical ("Fatal error while doing TLS handshaking: %s\n", + gnutls_strerror (error)); + } + else + { + ssl->handshaking = FALSE; + } +} + +static void +ssl_server_connect (HCConn *conn) +{ + struct ssl_data *ssl = conn->layer; + gnutls_transport_set_ptr (ssl->session, (gnutls_transport_ptr_t) conn); + gnutls_transport_set_push_function (ssl->session, ssl_push); + gnutls_transport_set_pull_function (ssl->session, ssl_pull); + ssl->handshaking = TRUE; + ssl_server_handshake (ssl); +} + +static void +hc_conn_ssl_close (gpointer data) +{ + struct ssl_data *ssl = data; + if (ssl != NULL) + { + gnutls_bye (ssl->session, GNUTLS_SHUT_RDWR); + hc_conn_close (ssl->lowconn); + ssl_data_destroy (ssl); + } +} + +static ssize_t +hc_conn_ssl_read (gpointer data, gchar *buffer, size_t len) +{ + struct ssl_data *ssl = data; + return gnutls_record_recv (ssl->session, buffer, len); +} + +static ssize_t +hc_conn_ssl_write (gpointer data, gchar *buffer, size_t len) +{ + struct ssl_data *ssl = data; + return gnutls_record_send (ssl->session, buffer, len); +} + +void +hc_conn_ssl_watch (HCConn *conn, HCEvent event, gpointer data) +{ + char buffer[4096]; + HCConn *ssl_conn = data; + struct ssl_data *ssl = ssl_conn->layer; + int r; + if (event != HC_EVENT_READ) + return; + if (ssl->handshaking) + { + ssl_server_handshake (ssl); + return; + } + while ((r = hc_conn_read (ssl->lowconn, buffer, sizeof (buffer))) > 0) + g_string_append_len (ssl->buffer, buffer, r); + if (ssl_conn->func && !ssl->handshaking) + ssl_conn->func (ssl_conn, event, ssl_conn->data); + return; +} + +void +hc_conn_set_driver_ssl (HCConn *conn, HCConn *lowconn) +{ + struct ssl_data *ssl = ssl_data_new (); + ssl->lowconn = lowconn; + conn->layer = ssl; + conn->read = hc_conn_ssl_read; + conn->write = hc_conn_ssl_write; + conn->close = hc_conn_ssl_close; + hc_conn_set_callback (lowconn, hc_conn_ssl_watch, conn); + ssl_server_connect (conn); +} -- 2.20.1