--- a/src/CMakeLists.txt Thu May 07 02:17:55 2009 +0300
+++ b/src/CMakeLists.txt Thu May 07 02:46:44 2009 +0300
@@ -11,7 +11,7 @@
# define our source code modules
set (CORE_SOURCES error.c log.c str.c object.c)
-set (IO_SOURCES transport.c service.c transport_fd.c sock.c resolve.c tcp.c tcp_transport.c tcp_client.c tcp_server.c fifo.c line_proto.c)
+set (IO_SOURCES transport.c service.c transport_fd.c sock.c resolve.c tcp.c tcp_transport.c tcp_client.c tcp_server.c ssl.c ssl_client.c fifo.c line_proto.c)
set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c irc_net_connect.c)
set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c lua_func.c lua_type.c)
set (CONSOLE_SOURCES console.c lua_console.c)
--- a/src/error.c Thu May 07 02:17:55 2009 +0300
+++ b/src/error.c Thu May 07 02:46:44 2009 +0300
@@ -3,7 +3,7 @@
// for the error_desc tables
#include "sock.h"
-#include "sock_gnutls.h"
+#include "ssl_internal.h"
#include "module.h"
#include <string.h>
--- a/src/irc_net.c Thu May 07 02:17:55 2009 +0300
+++ b/src/irc_net.c Thu May 07 02:46:44 2009 +0300
@@ -310,7 +310,7 @@
// our info
if (net->info.ssl_cred)
- sock_ssl_client_cred_put(net->info.ssl_cred);
+ ssl_client_cred_put(net->info.ssl_cred);
// misc
irc_net_connect_destroy(net);
--- a/src/irc_net.h Thu May 07 02:17:55 2009 +0300
+++ b/src/irc_net.h Thu May 07 02:46:44 2009 +0300
@@ -13,7 +13,7 @@
#include "error.h"
#include "irc_conn.h"
#include "irc_chan.h"
-#include "sock_ssl.h"
+#include "ssl.h"
#include <sys/queue.h>
/**
@@ -30,7 +30,7 @@
const char *service;
/** Use SSL if given as non-NULL, a reference will be held by the irc_net */
- struct sock_ssl_client_cred *ssl_cred;
+ struct ssl_client_cred *ssl_cred;
/** Protocol registration info (nickname etc) */
struct irc_conn_register_info register_info;
--- a/src/irc_net_connect.c Thu May 07 02:17:55 2009 +0300
+++ b/src/irc_net_connect.c Thu May 07 02:46:44 2009 +0300
@@ -1,7 +1,7 @@
#include "irc_net_internal.h"
#include "tcp.h"
-#include "sock_ssl.h"
+#include "ssl.h"
#include "log.h"
#include <time.h>
@@ -134,12 +134,12 @@
} else if (info->ssl_cred) {
// aquire a ref
// NOTE: before any error handling
- sock_ssl_client_cred_get(info->ssl_cred);
+ ssl_client_cred_get(info->ssl_cred);
log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
// connect
- if (sock_ssl_connect(&transport_info, &transport, info->hostname, info->service, info->ssl_cred, err))
+ if (ssl_connect(&transport_info, &transport, info->hostname, info->service, info->ssl_cred, err))
goto error;
net->connecting = true;
--- a/src/lua_irc.c Thu May 07 02:17:55 2009 +0300
+++ b/src/lua_irc.c Thu May 07 02:46:44 2009 +0300
@@ -259,7 +259,7 @@
return luaL_error(L, "must give both ssl_cert and ssl_pkey");
// create
- if (sock_ssl_client_cred_create(&net_info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, &err))
+ if (ssl_client_cred_create(&net_info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, &err))
return luaL_error(L, "sock_ssl_client_cred_create(ssl_cafile=%s, ssl_verify=%b, ssl_cert=%s, ssl_pkey=%s): %s",
ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, error_msg(&err)
);
--- a/src/module.c Thu May 07 02:17:55 2009 +0300
+++ b/src/module.c Thu May 07 02:46:44 2009 +0300
@@ -382,7 +382,7 @@
// unload the dl handle
if (module->handle && dlclose(module->handle))
- log_error("dlclose(%s): %s", module->info.name, dlerror());
+ log_warn("dlclose(%s): %s", module->info.name, dlerror());
// release the path buf, if any
free(module->path_buf);
--- a/src/nexus.c Thu May 07 02:17:55 2009 +0300
+++ b/src/nexus.c Thu May 07 02:46:44 2009 +0300
@@ -1,5 +1,6 @@
#include "nexus.h"
#include "lua_config.h"
+#include "sock.h"
#include "log.h"
#include <stdlib.h>
@@ -193,7 +194,7 @@
JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "must give both :ssl_cert/:ssl_pkey");
// create
- if (sock_ssl_client_cred_create(&info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, err))
+ if (ssl_client_cred_create(&info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, err))
goto error;
}
@@ -209,7 +210,7 @@
error:
// cleanup
if (info.ssl_cred)
- sock_ssl_client_cred_put(info.ssl_cred);
+ ssl_client_cred_put(info.ssl_cred);
return ERROR_CODE(err);
}
--- a/src/sock.c Thu May 07 02:17:55 2009 +0300
+++ b/src/sock.c Thu May 07 02:46:44 2009 +0300
@@ -1,6 +1,6 @@
#include "sock_internal.h"
-#include "sock_gnutls.h"
+#include "ssl_internal.h"
#include <assert.h>
@@ -13,7 +13,7 @@
_sock_stream_ctx.ev_base = ev_base;
// XXX: just call these all directly for now
- if (sock_gnutls_global_init(err))
+ if (ssl_global_init(err))
return ERROR_CODE(err);
// done
--- a/src/sock.h Thu May 07 02:17:55 2009 +0300
+++ b/src/sock.h Thu May 07 02:46:44 2009 +0300
@@ -4,7 +4,7 @@
/**
* @file
*
- * General sock_* interface.
+ * Legacy sock_* interface for global state
*/
#include "error.h"
#include <sys/types.h>
--- a/src/sock_gnutls.c Thu May 07 02:17:55 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,522 +0,0 @@
-
-#include "sock_gnutls.h"
-
-// XXX: remove
-#include "log.h"
-
-#include <gnutls/x509.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include <assert.h>
-
-/**
- * Enable the TCP events based on the session's gnutls_record_get_direction().
- */
-static err_t sock_gnutls_ev_enable (struct sock_gnutls *sock, error_t *err)
-{
- int ret;
- short mask;
-
- // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
- // operation, so we can use that to determine what events to register
- switch ((ret = gnutls_record_get_direction(sock->session))) {
- case 0:
- // read more data
- mask = TRANSPORT_READ;
- break;
-
- case 1:
- // write buffer full
- mask = TRANSPORT_WRITE;
- break;
-
- default:
- // random error
- RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
- }
-
- // do the enabling
- if ((ERROR_CODE(err) = transport_fd_enable(SOCK_GNUTLS_FD(sock), mask)))
- return ERROR_CODE(err);
-
-
- return SUCCESS;
-}
-
-/**
- * Translate a set of gnutls_certificate_status_t values to a constant error message
- */
-static const char* sock_gnutls_verify_error (unsigned int status)
-{
- if (status & GNUTLS_CERT_REVOKED)
- return "certificate was revoked";
-
- else if (status & GNUTLS_CERT_INVALID) {
- if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
- return "certificate signer was not found";
-
- else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
- return "certificate signer is not a Certificate Authority";
-
- else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
- return "certificate signed using an insecure algorithm";
-
- else
- return "certificate could not be verified";
-
- } else
- return "unknown error";
-
-}
-
-/**
- * Perform the certificate validation procedure on the socket.
- *
- * Based on the GnuTLS examples/ex-rfc2818.c
- */
-static err_t sock_gnutls_verify (struct sock_gnutls *sock, error_t *err)
-{
- unsigned int status;
- const gnutls_datum_t *cert_list;
- unsigned int cert_list_size;
- gnutls_x509_crt_t cert = NULL;
- time_t t, now;
-
- // init
- RESET_ERROR(err);
- now = time(NULL);
-
- // inspect the peer's cert chain using the installed trusted CAs
- if ((ERROR_EXTRA(err) = gnutls_certificate_verify_peers2(sock->session, &status)))
- JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_VERIFY_PEERS2);
-
- // verify errors?
- if (status)
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, sock_gnutls_verify_error(status));
-
- // import the main cert
- assert(gnutls_certificate_type_get(sock->session) == GNUTLS_CRT_X509);
-
- if ((ERROR_EXTRA(err) = gnutls_x509_crt_init(&cert)))
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_init");
-
- if ((cert_list = gnutls_certificate_get_peers(sock->session, &cert_list_size)) == NULL)
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_certificate_get_peers");
-
- if (!cert_list_size)
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "cert_list_size");
-
- if ((ERROR_EXTRA(err) = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)))
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_import");
-
- // check expire/activate... not sure if we need to do this
- if ((t = gnutls_x509_crt_get_expiration_time(cert)) == ((time_t) -1) || t < now)
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_expiration_time");
-
- if ((t = gnutls_x509_crt_get_activation_time(cert)) == ((time_t) -1) || t > now)
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_activation_time");
-
- // check hostname
- if (!gnutls_x509_crt_check_hostname(cert, sock->hostname))
- JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_check_hostname");
-
-error:
- // cleanup
- if (cert)
- gnutls_x509_crt_deinit(cert);
-
- // should be SUCCESS
- return ERROR_CODE(err);
-}
-
-
-/**
- * Our handshake driver. This will execute the next gnutls_handshake step, handling E_AGAIN.
- *
- * This updates the sock_gnutls::handshake state internally, as used by sock_gnutls_event_handler.
- *
- * If the sock is marked as verify, this will perform the verification, returning on any errors, and then unset the
- * verify flag - this ensures that the peer cert is only verified once per connection...
- *
- * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
- */
-static int sock_gnutls_handshake (struct sock_gnutls *sock, error_t *err)
-{
- int ret;
-
- // perform the handshake
- if ((ret = gnutls_handshake(sock->session)) < 0 && ret != GNUTLS_E_AGAIN)
- JUMP_SET_ERROR_EXTRA(err, ERR_GNUTLS_HANDSHAKE, ret);
-
- // complete?
- if (ret == 0) {
- // update state
- sock->handshake = false;
-
- // verify?
- if (sock->verify) {
- // perform the validation
- if (sock_gnutls_verify(sock, err))
- goto error;
-
- // unmark
- sock->verify = false;
- }
-
- // handshake done
- return 1;
-
- } else {
- // set state, isn't really needed every time, but easier this way
- sock->handshake = true;
-
- // re-enable the event for the next iteration
- return sock_gnutls_ev_enable(sock, err);
- }
-
-error:
- return -ERROR_CODE(err);
-}
-
-/**
- * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
- */
-static void sock_gnutls_on_event (struct transport_fd *fd, short what, void *arg)
-{
- struct sock_gnutls *sock = arg;
- error_t err;
-
- (void) fd;
-
- // XXX: timeouts
- (void) what;
-
- // are we in the handshake cycle?
- if (sock->handshake) {
- RESET_ERROR(&err);
-
- // perform the next handshake step
- if (sock_gnutls_handshake(sock, &err) == 0) {
- // handshake continues
-
- // XXX: this state flag is completely wrong
- } else if (SOCK_GNUTLS_TRANSPORT(sock)->connected) {
- // the async connect process has now completed, either succesfully or with an error
- // invoke the user connect callback directly with appropriate error
- transport_connected(SOCK_GNUTLS_TRANSPORT(sock), ERROR_CODE(&err) ? &err : NULL, true);
-
- } else {
- if (ERROR_CODE(&err))
- // the re-handshake failed, so this transport is dead
- transport_error(SOCK_GNUTLS_TRANSPORT(sock), &err);
-
- else
- // re-handshake completed, so continue with the transport_callbacks
- transport_invoke(SOCK_GNUTLS_TRANSPORT(sock), what);
- }
-
- } else {
- // normal sock_stream operation
- // gnutls might be able to proceed now, so invoke user callbacks
- transport_invoke(SOCK_GNUTLS_TRANSPORT(sock), what);
- }
-}
-
-static err_t sock_gnutls_read (transport_t *transport, void *buf, size_t *len, error_t *err)
-{
- struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
- int ret;
-
- // read gnutls record
- do {
- ret = gnutls_record_recv(sock->session, buf, *len);
-
- } while (ret == GNUTLS_E_INTERRUPTED);
-
- // errors
- // XXX: E_REHANDSHAKE?
- if (ret < 0 && ret != GNUTLS_E_AGAIN)
- RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
-
- else if (ret == 0)
- return SET_ERROR(err, ERR_EOF);
-
-
- // EAGAIN?
- if (ret < 0) {
- *len = 0;
-
- } else {
- // updated length
- *len = ret;
-
- }
-
- return SUCCESS;
-}
-
-static err_t sock_gnutls_write (transport_t *transport, const void *buf, size_t *len, error_t *err)
-{
- struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
- int ret;
-
- // read gnutls record
- do {
- ret = gnutls_record_send(sock->session, buf, *len);
-
- } while (ret == GNUTLS_E_INTERRUPTED);
-
- // errors
- if (ret < 0 && ret != GNUTLS_E_AGAIN)
- RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_SEND, ret);
-
- else if (ret == 0)
- return SET_ERROR(err, ERR_WRITE_EOF);
-
-
- // eagain?
- if (ret < 0) {
- *len = 0;
-
- } else {
- // updated length
- *len = ret;
- }
-
- return SUCCESS;
-}
-
-static void _sock_gnutls_destroy (transport_t *transport)
-{
- struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
-
- // die
- sock_gnutls_destroy(sock);
-}
-
-/**
- * Our sock_tcp-invoked connect handler
- */
-static void sock_gnutls__connected (transport_t *transport, const error_t *tcp_err)
-{
- struct sock_gnutls *sock = transport_check(transport, &sock_gnutls_type);
- error_t err;
-
- // trap errors to let the user handle them directly
- if (tcp_err)
- JUMP_SET_ERROR_INFO(&err, tcp_err);
-
- // bind default transport functions (recv/send) to use the TCP fd
- gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd);
-
- // add ourselves as the event handler
- if ((ERROR_CODE(&err) = transport_fd_setup(SOCK_GNUTLS_FD(sock), sock_gnutls_on_event, sock)))
- goto error;
-
- // start handshake
- if (sock_gnutls_handshake(sock, &err))
- // this should complete with SUCCESS if it returns >0
- goto error;
-
- // ok, so we wait...
- return;
-
-error:
- // tell the user
- transport_connected(transport, &err, true);
-}
-
-struct transport_type sock_gnutls_type = {
- .parent = &sock_tcp_type,
- .methods = {
- .read = sock_gnutls_read,
- .write = sock_gnutls_write,
- .destroy = _sock_gnutls_destroy,
- ._connected = sock_gnutls__connected,
- },
-};
-
-/*
- * Global shared anonymous client credentials
- */
-static struct sock_ssl_client_cred sock_gnutls_client_cred_anon = { .x509 = NULL, .verify = false, .refcount = 0 };
-
-// XXX: GnuTLS log func
-void _log (int level, const char *msg)
-{
- printf("gnutls: %d: %s", level, msg);
-}
-
-err_t sock_gnutls_global_init (error_t *err)
-{
- // global init
- if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
- return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);
-
- // initialize the anon client credentials
- if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&sock_gnutls_client_cred_anon.x509)) < 0)
- return SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
-
- // XXX: debug
-// gnutls_global_set_log_function(&_log);
-// gnutls_global_set_log_level(11);
-
- // done
- return SUCCESS;
-}
-
-static void sock_ssl_client_cred_destroy (struct sock_ssl_client_cred *cred)
-{
- // simple
- gnutls_certificate_free_credentials(cred->x509);
-
- free(cred);
-}
-
-err_t sock_ssl_client_cred_create (struct sock_ssl_client_cred **ctx_cred,
- const char *cafile_path, bool verify,
- const char *cert_path, const char *pkey_path,
- error_t *err
-) {
- struct sock_ssl_client_cred *cred;
-
- // alloc it
- if ((cred = calloc(1, sizeof(*cred))) == NULL)
- return SET_ERROR(err, ERR_CALLOC);
-
- // create the cert
- if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&cred->x509)) < 0)
- JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
-
- // load the trusted ca certs?
- if (cafile_path) {
- // load them
- if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_trust_file(cred->x509, cafile_path, GNUTLS_X509_FMT_PEM)) < 0)
- JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_TRUST_FILE);
-
- }
-
- // set the verify flags?
- cred->verify = verify;
- gnutls_certificate_set_verify_flags(cred->x509, 0);
-
- // load the client cert?
- if (cert_path || pkey_path) {
- // need both...
- assert(cert_path && pkey_path);
-
- // load
- if ((ERROR_EXTRA(err) = gnutls_certificate_set_x509_key_file(cred->x509, cert_path, pkey_path, GNUTLS_X509_FMT_PEM)))
- JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_SET_X509_KEY_FILE);
- }
-
- // ok
- cred->refcount = 1;
- *ctx_cred = cred;
-
- return SUCCESS;
-
-error:
- // release
- sock_ssl_client_cred_destroy(cred);
-
- return ERROR_CODE(err);
-}
-
-void sock_ssl_client_cred_get (struct sock_ssl_client_cred *cred)
-{
- cred->refcount++;
-}
-
-void sock_ssl_client_cred_put (struct sock_ssl_client_cred *cred)
-{
- if (--cred->refcount == 0)
- sock_ssl_client_cred_destroy(cred);
-}
-
-err_t sock_ssl_connect (const struct transport_info *info, transport_t **transport_ptr,
- const char *hostname, const char *service,
- struct sock_ssl_client_cred *cred,
- error_t *err
- )
-{
- struct sock_gnutls *sock = NULL;
-
- // alloc
- if ((sock = calloc(1, sizeof(*sock))) == NULL)
- return SET_ERROR(err, ERR_CALLOC);
-
- // initialize base
- transport_init(SOCK_GNUTLS_TRANSPORT(sock), &sock_gnutls_type, info);
-
- if (!cred) {
- // default credentials
- cred = &sock_gnutls_client_cred_anon;
-
- } else {
- // take a ref
- sock->cred = cred;
- cred->refcount++;
- };
-
- // do verify?
- if (cred->verify)
- sock->verify = true;
-
- // init
- if ((sock->hostname = strdup(hostname)) == NULL)
- JUMP_SET_ERROR(err, ERR_STRDUP);
-
- // initialize TCP
- sock_tcp_init(SOCK_GNUTLS_TCP(sock));
-
- // initialize client session
- if ((ERROR_EXTRA(err) = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0)
- JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
-
- // ...default priority stuff
- if ((ERROR_EXTRA(err) = gnutls_set_default_priority(sock->session)))
- JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY);
-
- // XXX: silly hack for OpenSSL interop
- gnutls_dh_set_prime_bits(sock->session, 512);
-
- // bind credentials
- if ((ERROR_EXTRA(err) = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
- JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
-
- // TCP connect
- if (sock_tcp_connect_async(SOCK_GNUTLS_TCP(sock), hostname, service, err))
- goto error;
-
- // done, wait for the connect to complete
- *transport_ptr = SOCK_GNUTLS_TRANSPORT(sock);
-
- return SUCCESS;
-
-error:
- // cleanup
- sock_gnutls_destroy(sock);
-
- return ERROR_CODE(err);
-}
-
-void sock_gnutls_destroy (struct sock_gnutls *sock)
-{
- // close the session rudely
- gnutls_deinit(sock->session);
-
- // terminate the TCP transport
- sock_tcp_destroy(SOCK_GNUTLS_TCP(sock));
-
- if (sock->cred)
- // drop the cred ref
- sock_ssl_client_cred_put(sock->cred);
-
- // free
- free(sock->hostname);
-}
-
--- a/src/sock_gnutls.h Thu May 07 02:17:55 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-#ifndef SOCK_GNUTLS_H
-#define SOCK_GNUTLS_H
-
-/**
- * @file
- *
- * A sock_stream implementation using GnuTLS for SSL
- */
-
-#include "sock_tcp_internal.h"
-
-#include <gnutls/gnutls.h>
-
-/**
- * GnuTLS library error codes
- */
-enum sock_gnutls_error_code {
- _ERR_GNUTLS_BEGIN = _ERR_GNUTLS,
-
- ERR_GNUTLS_CERT_ALLOC_CRED,
- ERR_GNUTLS_GLOBAL_INIT,
- ERR_GNUTLS_INIT,
- ERR_GNUTLS_SET_DEFAULT_PRIORITY,
- ERR_GNUTLS_CRED_SET,
- ERR_GNUTLS_HANDSHAKE,
- ERR_GNUTLS_RECORD_SEND,
- ERR_GNUTLS_RECORD_RECV,
- ERR_GNUTLS_RECORD_GET_DIRECTION,
- ERR_GNUTLS_CERT_VERIFY_PEERS2,
- ERR_GNUTLS_CERT_VERIFY,
- ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,
- ERR_GNUTLS_CERT_SET_X509_KEY_FILE,
-};
-
-/*
- * Our transport_type
- */
-extern struct transport_type sock_gnutls_type;
-
-/**
- * GnuTLS credentials for client sockets.
- */
-struct sock_ssl_client_cred {
- /** Our client certificate */
- gnutls_certificate_credentials_t x509;
-
- /** Should we verify? */
- bool verify;
-
- /** Refcount from sock_gnutls */
- int refcount;
-};
-
-/**
- * An SSL-encrypted TCP connection, using libgnutls
- */
-struct sock_gnutls {
- /** The underlying TCP connection */
- struct sock_tcp base_tcp;
-
- /** The hostname we connected to, for verification */
- char *hostname;
-
- /** The credentials we are using, unless anon */
- struct sock_ssl_client_cred *cred;
-
- /** The GnuTLS session for this connection */
- gnutls_session_t session;
-
- /** The current event_enable mask */
- int ev_mask;
-
- /** Should we verify the peer cert? */
- bool verify;
-
- /** Are we running a handshake? */
- bool handshake;
-};
-
-/**
- * Cast a sock_gnutls to a sock_tcp.
- */
-#define SOCK_GNUTLS_TCP(sock_ptr) (&(sock_ptr)->base_tcp)
-
-/**
- * Cast a sock_gnutls to a sock_fd.
- */
-#define SOCK_GNUTLS_FD(sock_ptr) SOCK_TCP_FD(SOCK_GNUTLS_TCP(sock_ptr))
-
-/**
- * Cast a sock_gnutls to a sock_stream.
- */
-#define SOCK_GNUTLS_TRANSPORT(sock_ptr) SOCK_TCP_TRANSPORT(SOCK_GNUTLS_TCP(sock_ptr))
-
-/**
- * Initialize the global gnutls state
- */
-err_t sock_gnutls_global_init (struct error_info *err);
-
-/**
- * Release the connection destructively, i.e. do not close it cleanly, just abort.
- */
-void sock_gnutls_destroy (struct sock_gnutls *sock);
-
-#endif /* SOCK_GNUTLS_H */
--- a/src/sock_ssl.h Thu May 07 02:17:55 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-#ifndef SOCK_SSL_H
-#define SOCK_SSL_H
-
-/**
- * @file
- *
- * SSL transport implementation.
- */
-#include "sock.h"
-
-/**
- * SSL client credentials for use with sock_ssl_client_credentials/sock_ssl_connect
- */
-struct sock_ssl_client_cred;
-
-/**
- * Set up SSL client credentials for use with sock_ssl_connect. This includes information both required to identify
- * ourselves to the server, as well as to verify the server.
- *
- * To verify the server's certificate, pass in a path to a file containing the CA certificate(s) that should be used to
- * verify the server's certificate, and then either give `verify` as true to force verification, or false to simply
- * warn. XXX: not entirely true
- *
- * To supply a client certificate to the server, pass in the paths to the cert/pkey files. If given as NULL, an
- * anonymous client certificate will be used. Both must be supplied if given.
- *
- * The newly created SSL client credential will initially have a refcount of one, and can then be used with sock_ssl_connect.
- *
- * @param ctx_cred the newly created client credentials are returned via this
- * @param cafile_path given as non-NULL to load trusted certs for verification from the given path
- * @param verify force verification of the peer cert
- * @param cert_path path to the client certificate file, or NULL
- * @param pkey_path path to the client private key, or NULL
- * @param err returned error info
- */
-err_t sock_ssl_client_cred_create (struct sock_ssl_client_cred **ctx_cred,
- const char *cafile_path, bool verify,
- const char *cert_path, const char *pkey_path,
- error_t *err
-);
-
-/**
- * Aquire a referenec for the given cred.
- */
-void sock_ssl_client_cred_get (struct sock_ssl_client_cred *cred);
-
-/**
- * Release a reference allocated for the given cred.
- */
-void sock_ssl_client_cred_put (struct sock_ssl_client_cred *cred);
-
-/**
- * Start a non-blocking SSL connect/handshake to the given host/service. The socket will not yet be connected when the
- * function returns, but rather, the eventual redyness/failure of the connect/handshake will be indicated later using
- * the given \a cb_func.
- *
- * The given sock_ssl_client_cred should either be NULL to use an anonymous client cert and not verify the server cert,
- * or a sock_ssl_client_cred allocated using sock_ssl_client_cred_create(). The contexts are reference-counted, so once
- * a cred context has been released, it will be destroyed once the last connection using it is destroyed.
- *
- * @param info the transport setup info
- * @param transport_ptr returned transport
- * @param hostname the hostname to connect to
- * @param service the TCP service name (i.e. port) to connect to
- * @param cred the SSL client credentials to use, if not NULL
- * @param err returned error info
- */
-err_t sock_ssl_connect (const struct transport_info *info, transport_t **transport_ptr,
- const char *hostname, const char *service,
- struct sock_ssl_client_cred *cred,
- error_t *err
- );
-
-
-
-#endif /* SOCK_SSL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl.h Thu May 07 02:46:44 2009 +0300
@@ -0,0 +1,77 @@
+#ifndef SSL_H
+#define SSL_H
+
+/**
+ * @file
+ *
+ * SSL transport implementation.
+ */
+#include "transport.h"
+#include <stdbool.h>
+
+/**
+ * SSL client credentials for use with ssl_client_credentials/sock_ssl_connect
+ */
+struct ssl_client_cred;
+
+/**
+ * Set up SSL client credentials for use with sock_ssl_connect. This includes information both required to identify
+ * ourselves to the server, as well as to verify the server.
+ *
+ * To verify the server's certificate, pass in a path to a file containing the CA certificate(s) that should be used to
+ * verify the server's certificate, and then either give `verify` as true to force verification, or false to simply
+ * warn. XXX: not entirely true
+ *
+ * To supply a client certificate to the server, pass in the paths to the cert/pkey files. If given as NULL, an
+ * anonymous client certificate will be used. Both must be supplied if given.
+ *
+ * The newly created SSL client credential will initially have a refcount of one, and can then be used with sock_ssl_connect.
+ *
+ * @param ctx_cred the newly created client credentials are returned via this
+ * @param cafile_path given as non-NULL to load trusted certs for verification from the given path
+ * @param verify force verification of the peer cert
+ * @param cert_path path to the client certificate file, or NULL
+ * @param pkey_path path to the client private key, or NULL
+ * @param err returned error info
+ */
+err_t ssl_client_cred_create (struct ssl_client_cred **ctx_cred,
+ const char *cafile_path, bool verify,
+ const char *cert_path, const char *pkey_path,
+ error_t *err
+);
+
+/**
+ * Aquire a referenec for the given cred.
+ */
+void ssl_client_cred_get (struct ssl_client_cred *cred);
+
+/**
+ * Release a reference allocated for the given cred.
+ */
+void ssl_client_cred_put (struct ssl_client_cred *cred);
+
+/**
+ * Start a non-blocking SSL connect/handshake to the given host/service. The socket will not yet be connected when the
+ * function returns, but rather, the eventual redyness/failure of the connect/handshake will be indicated later using
+ * the given \a cb_func.
+ *
+ * The given ssl_client_cred should either be NULL to use an anonymous client cert and not verify the server cert,
+ * or a ssl_client_cred allocated using ssl_client_cred_create(). The contexts are reference-counted, so once
+ * a cred context has been released, it will be destroyed once the last connection using it is destroyed.
+ *
+ * @param info the transport setup info
+ * @param transport_ptr returned transport
+ * @param hostname the hostname to connect to
+ * @param service the TCP service name (i.e. port) to connect to
+ * @param cred the SSL client credentials to use, if not NULL
+ * @param err returned error info
+ */
+err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr,
+ const char *hostname, const char *service,
+ struct ssl_client_cred *cred,
+ error_t *err
+ );
+
+
+
+#endif /* SSL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl_client.c Thu May 07 02:46:44 2009 +0300
@@ -0,0 +1,453 @@
+#include "ssl_internal.h"
+
+#include <gnutls/x509.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+// XXX: remove
+#include "log.h"
+#include <assert.h>
+
+
+/**
+ * Cast a ssl_client to a sock_fd.
+ */
+#define SSL_CLIENT_FD(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd)
+
+/**
+ * Cast a ssl_client to a sock_stream.
+ */
+#define SSL_CLIENT_TRANSPORT(client_ptr) (&(client_ptr)->base_tcp.base_trans.base_fd.base)
+
+
+
+/**
+ * Enable the TCP events based on the session's gnutls_record_get_direction().
+ */
+static err_t ssl_client_ev_enable (struct ssl_client *client, error_t *err)
+{
+ int ret;
+ short mask;
+
+ // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last
+ // operation, so we can use that to determine what events to register
+ switch ((ret = gnutls_record_get_direction(client->session))) {
+ case 0:
+ // read more data
+ mask = TRANSPORT_READ;
+ break;
+
+ case 1:
+ // write buffer full
+ mask = TRANSPORT_WRITE;
+ break;
+
+ default:
+ // random error
+ RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_GET_DIRECTION, ret);
+ }
+
+ // do the enabling
+ if ((ERROR_CODE(err) = transport_fd_enable(SSL_CLIENT_FD(client), mask)))
+ return ERROR_CODE(err);
+
+
+ return SUCCESS;
+}
+
+/**
+ * Translate a set of gnutls_certificate_status_t values to a constant error message
+ */
+static const char* ssl_client_verify_error (unsigned int status)
+{
+ if (status & GNUTLS_CERT_REVOKED)
+ return "certificate was revoked";
+
+ else if (status & GNUTLS_CERT_INVALID) {
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ return "certificate signer was not found";
+
+ else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
+ return "certificate signer is not a Certificate Authority";
+
+ else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+ return "certificate signed using an insecure algorithm";
+
+ else
+ return "certificate could not be verified";
+
+ } else
+ return "unknown error";
+
+}
+
+/**
+ * Perform the certificate validation procedure on the peer cert.
+ *
+ * Based on the GnuTLS examples/ex-rfc2818.c
+ */
+static err_t ssl_client_verify (struct ssl_client *client, error_t *err)
+{
+ unsigned int status;
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size;
+ gnutls_x509_crt_t cert = NULL;
+ time_t t, now;
+
+ // init
+ RESET_ERROR(err);
+ now = time(NULL);
+
+ // inspect the peer's cert chain using the installed trusted CAs
+ if ((ERROR_EXTRA(err) = gnutls_certificate_verify_peers2(client->session, &status)))
+ JUMP_SET_ERROR(err, ERR_GNUTLS_CERT_VERIFY_PEERS2);
+
+ // verify errors?
+ if (status)
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, ssl_client_verify_error(status));
+
+ // import the main cert
+ assert(gnutls_certificate_type_get(client->session) == GNUTLS_CRT_X509);
+
+ if ((ERROR_EXTRA(err) = gnutls_x509_crt_init(&cert)))
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_init");
+
+ if ((cert_list = gnutls_certificate_get_peers(client->session, &cert_list_size)) == NULL)
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_certificate_get_peers");
+
+ if (!cert_list_size)
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "cert_list_size");
+
+ if ((ERROR_EXTRA(err) = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)))
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_import");
+
+ // check expire/activate... not sure if we need to do this
+ if ((t = gnutls_x509_crt_get_expiration_time(cert)) == ((time_t) -1) || t < now)
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_expiration_time");
+
+ if ((t = gnutls_x509_crt_get_activation_time(cert)) == ((time_t) -1) || t > now)
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_get_activation_time");
+
+ // check hostname
+ if (!gnutls_x509_crt_check_hostname(cert, client->hostname))
+ JUMP_SET_ERROR_STR(err, ERR_GNUTLS_CERT_VERIFY, "gnutls_x509_crt_check_hostname");
+
+error:
+ // cleanup
+ if (cert)
+ gnutls_x509_crt_deinit(cert);
+
+ // should be SUCCESS
+ return ERROR_CODE(err);
+}
+
+
+/**
+ * Our handshake driver. This will execute the next gnutls_handshake step, handling E_AGAIN.
+ *
+ * This updates the ssl_client::handshake state internally, as used by ssl_client_event_handler.
+ *
+ * If the client is marked as verify, this will perform the verification, returning on any errors, and then unset the
+ * verify flag - this ensures that the peer cert is only verified once per connection...
+ *
+ * @return >0 for finished handshake, 0 for handshake-in-progress, -err_t for errors.
+ */
+static int ssl_client_handshake (struct ssl_client *client, error_t *err)
+{
+ int ret;
+
+ // perform the handshake
+ if ((ret = gnutls_handshake(client->session)) < 0 && ret != GNUTLS_E_AGAIN)
+ JUMP_SET_ERROR_EXTRA(err, ERR_GNUTLS_HANDSHAKE, ret);
+
+ // complete?
+ if (ret == 0) {
+ // update state
+ client->handshake = false;
+
+ // verify?
+ if (client->verify) {
+ // perform the validation
+ if (ssl_client_verify(client, err))
+ goto error;
+
+ // unmark
+ client->verify = false;
+ }
+
+ // handshake done
+ return 1;
+
+ } else {
+ // set state, isn't really needed every time, but easier this way
+ client->handshake = true;
+
+ // re-enable the event for the next iteration
+ return ssl_client_ev_enable(client, err);
+ }
+
+error:
+ return -ERROR_CODE(err);
+}
+
+/**
+ * Our transport_fd event handler. Drive the handshake if that's current, otherwise, invoke user callbacks.
+ */
+static void ssl_client_on_event (struct transport_fd *fd, short what, void *arg)
+{
+ struct ssl_client *client = arg;
+ error_t err;
+
+ (void) fd;
+
+ // XXX: timeouts
+ (void) what;
+
+ // are we in the handshake cycle?
+ if (client->handshake) {
+ RESET_ERROR(&err);
+
+ // perform the next handshake step
+ if (ssl_client_handshake(client, &err) == 0) {
+ // handshake continues
+
+ // XXX: this state flag is completely wrong
+ } else if (SSL_CLIENT_TRANSPORT(client)->connected) {
+ // the async connect process has now completed, either succesfully or with an error
+ // invoke the user connect callback directly with appropriate error
+ transport_connected(SSL_CLIENT_TRANSPORT(client), ERROR_CODE(&err) ? &err : NULL, true);
+
+ } else {
+ if (ERROR_CODE(&err))
+ // the re-handshake failed, so this transport is dead
+ transport_error(SSL_CLIENT_TRANSPORT(client), &err);
+
+ else
+ // re-handshake completed, so continue with the transport_callbacks
+ transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
+ }
+
+ } else {
+ // normal transport operation
+ // gnutls might be able to proceed now, so invoke user callbacks
+ transport_invoke(SSL_CLIENT_TRANSPORT(client), what);
+ }
+}
+
+static err_t ssl_client__read (transport_t *transport, void *buf, size_t *len, error_t *err)
+{
+ struct ssl_client *client = transport_check(transport, &ssl_client_type);
+ int ret;
+
+ // read gnutls record
+ do {
+ ret = gnutls_record_recv(client->session, buf, *len);
+
+ } while (ret == GNUTLS_E_INTERRUPTED);
+
+ // errors
+ // XXX: E_REHANDSHAKE?
+ if (ret < 0 && ret != GNUTLS_E_AGAIN)
+ RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
+
+ else if (ret == 0)
+ return SET_ERROR(err, ERR_EOF);
+
+
+ // EAGAIN?
+ if (ret < 0) {
+ *len = 0;
+
+ } else {
+ // updated length
+ *len = ret;
+
+ }
+
+ return SUCCESS;
+}
+
+static err_t ssl_client__write (transport_t *transport, const void *buf, size_t *len, error_t *err)
+{
+ struct ssl_client *client = transport_check(transport, &ssl_client_type);
+ int ret;
+
+ // read gnutls record
+ do {
+ ret = gnutls_record_send(client->session, buf, *len);
+
+ } while (ret == GNUTLS_E_INTERRUPTED);
+
+ // errors
+ if (ret < 0 && ret != GNUTLS_E_AGAIN)
+ RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_SEND, ret);
+
+ else if (ret == 0)
+ return SET_ERROR(err, ERR_WRITE_EOF);
+
+
+ // eagain?
+ if (ret < 0) {
+ *len = 0;
+
+ } else {
+ // updated length
+ *len = ret;
+ }
+
+ return SUCCESS;
+}
+
+void ssl_client_deinit (struct ssl_client *client)
+{
+ // close the session rudely
+ gnutls_deinit(client->session);
+ client->session = NULL;
+
+ // terminate the TCP transport
+ tcp_client_deinit(&client->base_tcp);
+
+ if (client->cred) {
+ // drop the cred ref
+ ssl_client_cred_put(client->cred);
+
+ client->cred = NULL;
+ }
+
+ // free
+ free(client->hostname);
+ client->hostname = NULL;
+}
+
+
+static void ssl_client__deinit (transport_t *transport)
+{
+ struct ssl_client *client = transport_check(transport, &ssl_client_type);
+
+ // die
+ ssl_client_deinit(client);
+}
+
+/**
+ * Our tcp_client-invoked connect handler
+ */
+static void ssl_client__connected (transport_t *transport, const error_t *tcp_err)
+{
+ struct ssl_client *client = transport_check(transport, &ssl_client_type);
+ error_t err;
+
+ // trap errors to let the user handle them directly
+ if (tcp_err)
+ JUMP_SET_ERROR_INFO(&err, tcp_err);
+
+ // bind default transport functions (recv/send) to use the TCP fd
+ gnutls_transport_set_ptr(client->session, (gnutls_transport_ptr_t) (long int) SSL_CLIENT_FD(client)->fd);
+
+ // add ourselves as the event handler
+ if ((ERROR_CODE(&err) = transport_fd_setup(SSL_CLIENT_FD(client), ssl_client_on_event, client)))
+ goto error;
+
+ // start handshake
+ if (ssl_client_handshake(client, &err))
+ // this should complete with SUCCESS if it returns >0
+ goto error;
+
+ // ok, so we wait...
+ return;
+
+error:
+ // tell the user
+ transport_connected(transport, &err, true);
+}
+
+struct transport_type ssl_client_type = {
+ .base_type = {
+ .parent = &tcp_client_type.base_type,
+ },
+ .methods = {
+ .read = ssl_client__read,
+ .write = ssl_client__write,
+ .deinit = ssl_client__deinit,
+ ._connected = ssl_client__connected,
+ },
+};
+
+
+
+static void ssl_client_destroy (struct ssl_client *client)
+{
+ ssl_client_deinit(client);
+
+ free(client);
+}
+
+err_t ssl_connect (const struct transport_info *info, transport_t **transport_ptr,
+ const char *hostname, const char *service,
+ struct ssl_client_cred *cred,
+ error_t *err
+ )
+{
+ struct ssl_client *client = NULL;
+
+ // alloc
+ if ((client = calloc(1, sizeof(*client))) == NULL)
+ return SET_ERROR(err, ERR_CALLOC);
+
+ // initialize base
+ transport_init(SSL_CLIENT_TRANSPORT(client), &ssl_client_type, info);
+
+ if (!cred) {
+ // default credentials
+ cred = &ssl_client_cred_anon;
+
+ } else {
+ // take a ref
+ client->cred = cred;
+ cred->refcount++;
+ };
+
+ // do verify?
+ if (cred->verify)
+ client->verify = true;
+
+ // init
+ if ((client->hostname = strdup(hostname)) == NULL)
+ JUMP_SET_ERROR(err, ERR_STRDUP);
+
+ // initialize TCP
+ tcp_client_init(&client->base_tcp);
+
+ // initialize client session
+ if ((ERROR_EXTRA(err) = gnutls_init(&client->session, GNUTLS_CLIENT)) < 0)
+ JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
+
+ // ...default priority stuff
+ if ((ERROR_EXTRA(err) = gnutls_set_default_priority(client->session)))
+ JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY);
+
+ // XXX: silly hack for OpenSSL interop
+ gnutls_dh_set_prime_bits(client->session, 512);
+
+ // bind credentials
+ if ((ERROR_EXTRA(err) = gnutls_credentials_set(client->session, GNUTLS_CRD_CERTIFICATE, cred->x509)))
+ JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET);
+
+ // TCP connect
+ if (tcp_client_connect_async(&client->base_tcp, hostname, service, err))
+ goto error;
+
+ // done, wait for the connect to complete
+ *transport_ptr = SSL_CLIENT_TRANSPORT(client);
+
+ return SUCCESS;
+
+error:
+ // cleanup
+ ssl_client_destroy(client);
+
+ return ERROR_CODE(err);
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssl_internal.h Thu May 07 02:46:44 2009 +0300
@@ -0,0 +1,87 @@
+#ifndef SSL_INTERNAL_H
+#define SSL_INTERNAL_H
+
+/**
+ * @file
+ *
+ * A sock_stream implementation using GnuTLS for SSL
+ */
+#include "ssl.h"
+#include "tcp_internal.h"
+
+#include <gnutls/gnutls.h>
+
+/**
+ * GnuTLS library error codes
+ */
+enum ssl_error_code {
+ _ERR_SSL_BEGIN = _ERR_GNUTLS,
+
+ ERR_GNUTLS_CERT_ALLOC_CRED,
+ ERR_GNUTLS_GLOBAL_INIT,
+ ERR_GNUTLS_INIT,
+ ERR_GNUTLS_SET_DEFAULT_PRIORITY,
+ ERR_GNUTLS_CRED_SET,
+ ERR_GNUTLS_HANDSHAKE,
+ ERR_GNUTLS_RECORD_SEND,
+ ERR_GNUTLS_RECORD_RECV,
+ ERR_GNUTLS_RECORD_GET_DIRECTION,
+ ERR_GNUTLS_CERT_VERIFY_PEERS2,
+ ERR_GNUTLS_CERT_VERIFY,
+ ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,
+ ERR_GNUTLS_CERT_SET_X509_KEY_FILE,
+};
+
+/**
+ * GnuTLS credentials for client sockets.
+ */
+struct ssl_client_cred {
+ /** Our client certificate */
+ gnutls_certificate_credentials_t x509;
+
+ /** Should we verify? */
+ bool verify;
+
+ /** Refcount from ssl_client */
+ int refcount;
+};
+
+/**
+ * Global anonymous x509 credentials
+ */
+extern struct ssl_client_cred ssl_client_cred_anon;
+
+/*
+ * Our transport_type
+ */
+extern struct transport_type ssl_client_type;
+
+/**
+ * An SSL-encrypted TCP connection, using libgnutls
+ */
+struct ssl_client {
+ /** The underlying TCP connection */
+ struct tcp_client base_tcp;
+
+ /** The hostname we connected to, for verification */
+ char *hostname;
+
+ /** The credentials we are using, unless anon */
+ struct ssl_client_cred *cred;
+
+ /** The GnuTLS session for this connection */
+ gnutls_session_t session;
+
+ /** Should we verify the peer cert? */
+ bool verify;
+
+ /** Are we running a handshake? */
+ bool handshake;
+};
+
+/**
+ * Initialize the global gnutls state
+ */
+err_t ssl_global_init (error_t *err);
+
+#endif /* SSL_INTERNAL_H */