# HG changeset patch # User Tero Marttila # Date 1240103082 -10800 # Node ID aa390e52eda86fada36f8e7a386139aec69b6d93 # Parent 55b9dcc2b73ac701eea5d8a92384a1f75dadca0b implement ssl_cafile/verify/cert/pkey for x509 credentials diff -r 55b9dcc2b73a -r aa390e52eda8 src/error.c --- 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[] = { diff -r 55b9dcc2b73a -r aa390e52eda8 src/irc_client.c --- 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"); diff -r 55b9dcc2b73a -r aa390e52eda8 src/irc_net.c --- 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); diff -r 55b9dcc2b73a -r aa390e52eda8 src/irc_net.h --- 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 /** @@ -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; diff -r 55b9dcc2b73a -r aa390e52eda8 src/nexus.c --- 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 '::'\n"); - printf(" --network / -n add an IRC network using ':[:[:ssl]]' format\n"); + printf(" --network / -n add an IRC network using ':[:[:ssl][:ssl_cafile=][:ssl_verify][:ssl_cert=][:ssl_pkey=]]' format\n"); printf(" --channel add an IRC channel using ':' format\n"); printf(" --module / -m add a module using ':' format\n"); printf(" --config / -C add a module configuration option using ':[:]' 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 field for --network"); + JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --network"); if ((info.hostname = strsep(&opt, ":")) == NULL) - RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing field for --network"); + JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing 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); } /** diff -r 55b9dcc2b73a -r aa390e52eda8 src/sock.h --- 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); diff -r 55b9dcc2b73a -r aa390e52eda8 src/sock_gnutls.c --- 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 + #include -#include +#include +#include + +#include /** * 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); } diff -r 55b9dcc2b73a -r aa390e52eda8 src/sock_gnutls.h --- 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; }; diff -r 55b9dcc2b73a -r aa390e52eda8 src/sock_ssl.h --- /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 */