--- a/src/error.c Thu Apr 16 01:20:09 2009 +0300
+++ b/src/error.c Sun Apr 19 04:04:42 2009 +0300
@@ -45,6 +45,10 @@
{ ERR_GNUTLS_RECORD_SEND, "gnutls_record_send", ERR_EXTRA_GNUTLS },
{ ERR_GNUTLS_RECORD_RECV, "gnutls_record_recv", ERR_EXTRA_GNUTLS },
{ ERR_GNUTLS_RECORD_GET_DIRECTION, "gnutls_record_get_direction", ERR_EXTRA_GNUTLS },
+ { ERR_GNUTLS_CERT_VERIFY_PEERS2, "gnutls_certificate_verify_peers2", ERR_EXTRA_GNUTLS },
+ { ERR_GNUTLS_CERT_VERIFY, "X.509 Certificate verification failed", ERR_EXTRA_STR },
+ { ERR_GNUTLS_CERT_SET_X509_TRUST_FILE,"gnutls_certificate_set_x509_trust_file", ERR_EXTRA_GNUTLS },
+ { ERR_GNUTLS_CERT_SET_X509_KEY_FILE, "gnutls_certificate_set_x509_key_file", ERR_EXTRA_GNUTLS },
{ _ERR_INVALID, NULL, 0 }
}, _irc_error_desc[] = {
--- a/src/irc_client.c Thu Apr 16 01:20:09 2009 +0300
+++ b/src/irc_client.c Sun Apr 19 04:04:42 2009 +0300
@@ -54,7 +54,7 @@
{
struct irc_net *net;
struct irc_net_info net_info = {
- .use_ssl = _net_info->use_ssl,
+ .ssl_cred = _net_info->ssl_cred,
};
// combine _net_info and defaults to get net_info
@@ -68,7 +68,7 @@
RETURN_SET_ERROR_STR(err, ERR_IRC_NET_INFO, "hostname");
if ((net_info.service = (_net_info->service ? _net_info->service : (
- _net_info->use_ssl ? client->defaults.service_ssl : client->defaults.service
+ _net_info->ssl_cred ? client->defaults.service_ssl : client->defaults.service
))) == NULL)
RETURN_SET_ERROR_STR(err, ERR_IRC_NET_INFO, "service");
--- a/src/irc_net.c Thu Apr 16 01:20:09 2009 +0300
+++ b/src/irc_net.c Sun Apr 19 04:04:42 2009 +0300
@@ -290,24 +290,30 @@
return SET_ERROR(err, ERR_CALLOC);
// initialize
+ // XXX: info shouldn't be copied directly
net->info = *info;
TAILQ_INIT(&net->channels);
LIST_INIT(&net->users);
if (info->raw_sock) {
- // direct sock_stream connection
log_debug("connected using raw socket: %p", info->raw_sock);
+ // direct sock_stream connection
sock = info->raw_sock;
// then create the conn right away
if (irc_net_connected(net, sock, err))
goto error;
- } else if (info->use_ssl) {
+ } else if (info->ssl_cred) {
+ // aquire a ref
+ // NOTE: before any error handling
+ sock_ssl_client_cred_get(net->info.ssl_cred);
+
log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
- if (sock_ssl_connect_async(&sock, info->hostname, info->service, &irc_net_on_connect, net, err))
+ // connect
+ if (sock_ssl_connect_async(&sock, info->hostname, info->service, net->info.ssl_cred, &irc_net_on_connect, net, err))
goto error;
} else {
@@ -337,21 +343,31 @@
void irc_net_destroy (struct irc_net *net)
{
- struct irc_chan *next = TAILQ_FIRST(&net->channels), *chan;
+ struct irc_chan *chan_next = TAILQ_FIRST(&net->channels), *chan;
+ struct irc_user *user_next = LIST_FIRST(&net->users), *user;
// our conn
if (net->conn)
irc_conn_destroy(net->conn);
// our channels
- while (next) {
- chan = next;
- next = TAILQ_NEXT(chan, net_channels);
+ while ((chan = chan_next)) {
+ chan_next = TAILQ_NEXT(chan, net_channels);
irc_chan_destroy(chan);
}
- // XXX: our users
+ // our users
+ // XXX: this disregards external refs
+ while ((user = user_next)) {
+ user_next = LIST_NEXT(user, net_users);
+
+ irc_user_destroy(user);
+ }
+
+ // our info
+ if (net->info.ssl_cred)
+ sock_ssl_client_cred_put(net->info.ssl_cred);
// ourselves
free(net);
--- a/src/irc_net.h Thu Apr 16 01:20:09 2009 +0300
+++ b/src/irc_net.h Sun Apr 19 04:04:42 2009 +0300
@@ -13,6 +13,7 @@
#include "error.h"
#include "irc_conn.h"
#include "irc_chan.h"
+#include "sock_ssl.h"
#include <sys/queue.h>
/**
@@ -28,8 +29,8 @@
/** Service name (port) */
const char *service;
- /** SSL? */
- bool use_ssl;
+ /** Use SSL if given as non-NULL, a reference will be held by the irc_net */
+ struct sock_ssl_client_cred *ssl_cred;
/** Protocol registration info (nickname etc) */
struct irc_conn_register_info register_info;
--- a/src/nexus.c Thu Apr 16 01:20:09 2009 +0300
+++ b/src/nexus.c Sun Apr 19 04:04:42 2009 +0300
@@ -60,7 +60,7 @@
printf("\n");
printf(" --help / -h display this message\n");
printf(" --defaults / -D set the IRC client default info using '<nickname>:<username>:<realname>'\n");
- printf(" --network / -n add an IRC network using '<name>:<hostname>[:<port>[:ssl]]' format\n");
+ printf(" --network / -n add an IRC network using '<name>:<hostname>[:<port>[:ssl][:ssl_cafile=<path>][:ssl_verify][:ssl_cert=<path>][:ssl_pkey=<path>]]' format\n");
printf(" --channel add an IRC channel using '<network>:<channel>' format\n");
printf(" --module / -m add a module using '<name>:<path>' format\n");
printf(" --config / -C add a module configuration option using '<mod_name>:<name>[:<value>]' format\n");
@@ -135,16 +135,18 @@
static err_t apply_network (struct nexus *nexus, char *opt, struct error_info *err)
{
struct irc_net_info info;
+ const char *ssl_cafile = NULL, *ssl_cert = NULL, *ssl_pkey = NULL;
+ bool use_ssl = false, ssl_verify = false;
// init to zero
memset(&info, 0, sizeof(info));
// parse the required fields
if ((info.network = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <name> field for --network");
+ JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <name> field for --network");
if ((info.hostname = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <hostname> field for --network");
+ JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <hostname> field for --network");
// parse the optional fields
if (opt)
@@ -152,23 +154,64 @@
// parse any remaining flags
while (opt) {
- char *flag = strsep(&opt, ":");
+ char *flag_token = strsep(&opt, ":");
+ char *flag = strsep(&flag_token, "=");
+ char *value = flag_token;
- if (strcmp(flag, "ssl") == 0)
- info.use_ssl = true;
+ if (strcmp(flag, "ssl") == 0) {
+ use_ssl = true;
- else
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unrecognized flag for --network");
+ } else if (strcmp(flag, "ssl_cafile") == 0) {
+ if (!value)
+ JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing value for :ssl_cafile");
+
+ ssl_cafile = value;
+
+ } else if (strcmp(flag, "ssl_verify") == 0) {
+ ssl_verify = true;
+
+ } else if (strcmp(flag, "ssl_cert") == 0) {
+ if (!value)
+ JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing value for :ssl_cert");
+
+ ssl_cert = value;
+
+ } else if (strcmp(flag, "ssl_pkey") == 0) {
+ if (!value)
+ JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing value for :ssl_pkey");
+
+ ssl_pkey = value;
+
+ } else
+ JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "unrecognized flag for --network");
+ }
+
+ // SSL?
+ if (use_ssl || ssl_cafile || ssl_verify || ssl_cert || ssl_pkey) {
+ // verify
+ if ((ssl_cert || ssl_pkey) && !(ssl_cert && ssl_pkey))
+ 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))
+ goto error;
}
// create the net
log_info("add network '%s' at '%s:%s'", info.network, info.hostname, info.service);
if (irc_client_add_net(nexus->client, NULL, &info, err))
- return ERROR_CODE(err);
+ goto error;
// ok
- return SET_ERROR(err, SUCCESS);
+ return SUCCESS;
+
+error:
+ // cleanup
+ if (info.ssl_cred)
+ sock_ssl_client_cred_put(info.ssl_cred);
+
+ return ERROR_CODE(err);
}
/**
--- a/src/sock.h Thu Apr 16 01:20:09 2009 +0300
+++ b/src/sock.h Sun Apr 19 04:04:42 2009 +0300
@@ -91,21 +91,6 @@
sock_stream_connect_cb cb_func, void *cb_arg, struct error_info *err);
/**
- * 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.
- *
- * @param sock_ptr the new sock_stream
- * @param host the hostname to connect to
- * @param service the TCP service name (i.e. port) to connect to
- * @param err returned error info
- *
- * XXX: doesn't do any certificate verification.
- */
-err_t sock_ssl_connect_async (struct sock_stream **sock_ptr, const char *host, const char *service,
- sock_stream_connect_cb cb_func, void *cb_arg, struct error_info *err);
-
-/**
* A read-only "socket" based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
*/
err_t fifo_open_read (struct sock_stream **stream_ptr, const char *path, struct error_info *err);
--- a/src/sock_gnutls.c Thu Apr 16 01:20:09 2009 +0300
+++ b/src/sock_gnutls.c Sun Apr 19 04:04:42 2009 +0300
@@ -4,8 +4,13 @@
// XXX: remove
#include "log.h"
+#include <gnutls/x509.h>
+
#include <stdlib.h>
-#include <err.h>
+#include <string.h>
+#include <time.h>
+
+#include <assert.h>
/**
* Register for events based on the session's gnutls_record_get_direction().
@@ -37,10 +42,100 @@
}
/**
+ * 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, struct error_info *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, struct error_info *err)
@@ -56,6 +151,16 @@
// 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;
@@ -92,8 +197,8 @@
// wait for the next handshake step
} else if (SOCK_GNUTLS_BASE(sock)->conn_cb_func) {
- // the async connect process has now completed, either succesfully or not
- // invoke the user connect callback directly
+ // the async connect process has now completed, either succesfully or with an error
+ // invoke the user connect callback directly with appropriate error
sock_stream_invoke_conn_cb(SOCK_GNUTLS_BASE(sock), ERROR_CODE(&err) ? &err : NULL, true);
} else {
@@ -125,6 +230,7 @@
ret = gnutls_record_recv(sock->session, buf, *len);
// errors
+ // XXX: E_INTERRUPTED, E_REHANDSHAKE?
if (ret < 0 && ret != GNUTLS_E_AGAIN)
RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret);
@@ -250,24 +356,11 @@
};
/*
- * XXX: global shared sock_gnutls_ctx
- */
-struct sock_gnutls_client_ctx _sock_gnutls_client_ctx;
-
-/*
- * Configure the given gnutls socket context to use simple anonymous client credentials
+ * Global shared anonymous client credentials
*/
-static err_t sock_gnutls_client_ctx_anon (struct sock_gnutls_client_ctx *ctx, struct error_info *err)
-{
- // init to use anonymous x509 cert
- if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&ctx->xcred)) < 0)
- return SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED);
+static struct sock_ssl_client_cred sock_gnutls_client_cred_anon = { .x509 = NULL, .verify = false, .refcount = 0 };
- // done
- return SUCCESS;
-}
-
-// XXX: log func
+// XXX: GnuTLS log func
void _log (int level, const char *msg)
{
printf("gnutls: %d: %s", level, msg);
@@ -279,9 +372,9 @@
if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0)
return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT);
- // init _sock_gnutls_ctx
- if (sock_gnutls_client_ctx_anon(&_sock_gnutls_client_ctx, err))
- return ERROR_CODE(err);
+ // 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);
@@ -291,11 +384,83 @@
return SUCCESS;
}
-err_t sock_ssl_connect_async (struct sock_stream **sock_ptr, const char *host, const char *service,
- sock_stream_connect_cb cb_func, void *cb_arg, struct error_info *err)
+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,
+ struct error_info *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_async (struct sock_stream **sock_ptr,
+ const char *hostname, const char *service,
+ struct sock_ssl_client_cred *cred,
+ sock_stream_connect_cb cb_func, void *cb_arg,
+ struct error_info *err
+ )
{
struct sock_gnutls *sock = NULL;
- struct sock_gnutls_client_ctx *ctx = &_sock_gnutls_client_ctx;
// alloc
if ((sock = calloc(1, sizeof(*sock))) == NULL)
@@ -304,6 +469,24 @@
// initialize base
sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type, cb_func, cb_arg);
+ 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 client session
if ((ERROR_EXTRA(err) = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0)
JUMP_SET_ERROR(err, ERR_GNUTLS_INIT);
@@ -312,12 +495,15 @@
if ((ERROR_EXTRA(err) = gnutls_set_default_priority(sock->session)))
JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY);
- // bind anon credentials
- if ((ERROR_EXTRA(err) = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, ctx->xcred)))
+ // 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_begin(SOCK_GNUTLS_TCP(sock), host, service, err))
+ if (sock_tcp_connect_async_begin(SOCK_GNUTLS_TCP(sock), hostname, service, err))
goto error;
// done, wait for the connect to complete
@@ -338,10 +524,14 @@
sock_fd_close(SOCK_GNUTLS_FD(sock));
// close the session rudely
- // XXX: does this actually do everything we need it to? Don't want to call gnutls_bye here, since we're void...
gnutls_deinit(sock->session);
+ if (sock->cred)
+ // drop the cred ref
+ sock_ssl_client_cred_put(sock->cred);
+
// free
+ free(sock->hostname);
free(sock);
}
--- a/src/sock_gnutls.h Thu Apr 16 01:20:09 2009 +0300
+++ b/src/sock_gnutls.h Sun Apr 19 04:04:42 2009 +0300
@@ -27,16 +27,24 @@
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,
};
/**
- * Additional gnutls configuration for client sockets.
- *
- * XXX: currently, we just have one global instance, set up by sock_gnutls_init, used for all sockets
+ * GnuTLS credentials for client sockets.
*/
-struct sock_gnutls_client_ctx {
+struct sock_ssl_client_cred {
/** Our client certificate */
- gnutls_certificate_credentials_t xcred;
+ gnutls_certificate_credentials_t x509;
+
+ /** Should we verify? */
+ bool verify;
+
+ /** Refcount from sock_gnutls */
+ int refcount;
};
/**
@@ -45,16 +53,22 @@
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;
- /** Additional SSL info XXX: do we need to keep a ref to this? */
- struct sock_gnutls_ctx *ctx;
-
/** 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;
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sock_ssl.h Sun Apr 19 04:04:42 2009 +0300
@@ -0,0 +1,78 @@
+#ifndef SOCK_SSL_H
+#define SOCK_SSL_H
+
+/**
+ * @file
+ *
+ * SSL-specific functionality as related to sock.h
+ */
+#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,
+ struct error_info *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 sock_ptr the new sock_stream
+ * @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 cb_func the callback for connection/handshake completion
+ * @param cb_arg the callback context argument
+ * @param err returned error info
+ */
+err_t sock_ssl_connect_async (struct sock_stream **sock_ptr,
+ const char *hostname, const char *service,
+ struct sock_ssl_client_cred *cred,
+ sock_stream_connect_cb cb_func, void *cb_arg,
+ struct error_info *err
+ );
+
+
+
+#endif /* SOCK_SSL_H */