# HG changeset patch # User Tero Marttila # Date 1235273249 -7200 # Node ID a834f055993996369155a71ba47215f2c731ac60 # Parent cf0e1bb6bcab8b2eac918de9efe2cdf0c19a1f1c working SSL using gnutls - a bit of a painful process diff -r cf0e1bb6bcab -r a834f0559939 Makefile --- a/Makefile Sun Feb 22 03:57:44 2009 +0200 +++ b/Makefile Sun Feb 22 05:27:29 2009 +0200 @@ -1,8 +1,3 @@ -#LIBEVENT_PATH = ~/opt -#LIBRARY_PATHS = -L${LIBEVENT_PATH}/lib -#INCLUDE_PATHS = -I${LIBEVENT_PATH}/include -LDLIBS = -levent - # default is test ifndef MODE MODE = test @@ -20,6 +15,15 @@ FIXED_CFLAGS = -Wall -std=gnu99 +# libevent +LIBEVENT_CFLAGS = +LIBEVENT_LDFLAGS = -levent + +# GnuTLS stuff +GNUTLS_NAME = "gnutls" +GNUTLS_CFLAGS = $(shell pkg-config ${GNUTLS_NAME} --cflags) +GNUTLS_LDFLAGS = $(shell pkg-config ${GNUTLS_NAME} --libs) + BIN_NAMES = nexus BIN_PATHS = $(addprefix bin/,$(BIN_NAMES)) @@ -27,6 +31,7 @@ module_objs = $(patsubst src/%.c,obj/%.o,$(wildcard src/$(1)/*.c)) SOCK_OBJS = obj/sock.o obj/sock_tcp.o +SOCK_GNUTLS_OBJS = obj/sock_gnutls.o # XXX: not yet there #CORE_OBJS = obj/lib/log.o obj/lib/signals.o @@ -35,13 +40,16 @@ all: ${BIN_PATHS} # binaries -bin/nexus: ${SOCK_OBJS} +bin/nexus: ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS} # computed -LDFLAGS = ${LIBRARY_PATHS} -CPPFLAGS = ${INCLUDE_PATHS} ${DEFINES} -CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} +CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} ${LIBEVENT_CFLAGS} ${GNUTLS_CFLAGS} +LDFLAGS = ${LIBEVENT_LDFLAGS} ${GNUTLS_LDFLAGS} +# XXX: is this valid? +CPPFLAGS = ${CFLAGS} + +# names for src/ SRC_PATHS = $(wildcard src/*.c) $(wildcard src/*/*.c) SRC_NAMES = $(patsubst src/%,%,$(SRC_PATHS)) SRC_DIRS = $(dir $(SRC_NAMES)) diff -r cf0e1bb6bcab -r a834f0559939 src/nexus.c --- a/src/nexus.c Sun Feb 22 03:57:44 2009 +0200 +++ b/src/nexus.c Sun Feb 22 05:27:29 2009 +0200 @@ -11,7 +11,7 @@ #include "sock.h" #define CONNECT_HOST "irc.fixme.fi" -#define CONNECT_SERV "6667" +#define CONNECT_SERV "6697" #define LINE_LENGTH 512 struct recvline_state { @@ -110,10 +110,11 @@ struct recvline_state recvline_ctx; // initialize + sock_init(); memset(&recvline_ctx, 0, sizeof(recvline_ctx)); // over-simplified connect - sock = sock_tcp_connect(CONNECT_HOST, CONNECT_SERV); + sock = sock_ssl_connect(CONNECT_HOST, CONNECT_SERV); // read lines and dump them out do { diff -r cf0e1bb6bcab -r a834f0559939 src/sock.c --- a/src/sock.c Sun Feb 22 03:57:44 2009 +0200 +++ b/src/sock.c Sun Feb 22 05:27:29 2009 +0200 @@ -1,5 +1,12 @@ #include "sock_internal.h" +#include "sock_gnutls.h" + +void sock_init (void) +{ + // XXX: just call directly for now + sock_gnutls_init(); +} int sock_stream_read (struct sock_stream *sock, void *buf, size_t len) { diff -r cf0e1bb6bcab -r a834f0559939 src/sock.h --- a/src/sock.h Sun Feb 22 03:57:44 2009 +0200 +++ b/src/sock.h Sun Feb 22 05:27:29 2009 +0200 @@ -33,6 +33,11 @@ struct sock_stream *sock_ssl_connect (const char *host, const char *service); /* + * Initialize the socket module's global state + */ +void sock_init (void); + +/* * The generic read/write API for stream sockets. */ int sock_stream_read (struct sock_stream *sock, void *buf, size_t len); diff -r cf0e1bb6bcab -r a834f0559939 src/sock_gnutls.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sock_gnutls.c Sun Feb 22 05:27:29 2009 +0200 @@ -0,0 +1,105 @@ + +#include "sock_gnutls.h" + +#include +#include + +static void _sock_gnutls_error (struct sock_gnutls *sock, const char *func, int _err) { + if (_err == GNUTLS_E_FATAL_ALERT_RECEIVED) + errx(1, "%s: %s: %s", func, gnutls_strerror(_err), gnutls_alert_get_name(gnutls_alert_get(sock->session))); + + else + errx(1, "%s: %s", func, gnutls_strerror(_err)); +} + +static int sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t len) +{ + struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); + + // just map to gnutls_record_recv + return gnutls_record_recv(sock->session, buf, len); +} + +static int sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t len) +{ + struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); + + // just map to gnutls_record_send + return gnutls_record_send(sock->session, buf, len); +} + +/* + * Our sock_stream_Type + */ +struct sock_stream_type sock_gnutls_type = { + .methods.read = &sock_gnutls_read, + .methods.write = &sock_gnutls_write, +}; + +/* + * 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 + * + * XXX: errors + */ +void sock_gnutls_client_ctx_anon (struct sock_gnutls_client_ctx *ctx) +{ + gnutls_certificate_allocate_credentials(&ctx->xcred); +} + +// XXX: errors +void sock_gnutls_init (void) +{ + int _err; + + // global init + if ((_err = gnutls_global_init()) < 0) + errx(1, "gnutls_global_init: %s", gnutls_strerror(_err)); + + // init _sock_gnutls_ctx + sock_gnutls_client_ctx_anon(&_sock_gnutls_client_ctx); +} + + +// XXX: errors +struct sock_stream *sock_ssl_connect (const char *host, const char *service) +{ + int _err; + struct sock_gnutls *sock; + struct sock_gnutls_client_ctx *ctx = &_sock_gnutls_client_ctx; + + // alloc + if ((sock = calloc(1, sizeof(*sock))) == NULL) + errx(1, "calloc"); + + // initialize + sock->base_tcp.base.type = &sock_gnutls_type; + + // initialize client session + if ((_err = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0) + errx(1, "gnutls_init: %s", gnutls_strerror(_err)); + + // ...default priority stuff + gnutls_set_default_priority(sock->session); + + // bind anon credentials + gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, ctx->xcred); + + // TCP connect + sock_tcp_init_connect(SOCK_GNUTLS_TCP(sock), host, service); + + // bind default transport functions (recv/send) to use the TCP fd + gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) sock->base_tcp.fd); + + // perform the handshake + if ((_err = gnutls_handshake(sock->session)) < 0) + _sock_gnutls_error(sock, "gnutls_handshake", _err); + + // done + return SOCK_GNUTLS_BASE(sock); +} + diff -r cf0e1bb6bcab -r a834f0559939 src/sock_gnutls.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sock_gnutls.h Sun Feb 22 05:27:29 2009 +0200 @@ -0,0 +1,44 @@ +#ifndef SOCK_GNUTLS_H +#define SOCK_GNUTLS_H + +/* + * A sock_stream implementation using GnuTLS + */ + +#include "sock_internal.h" +#include "sock_tcp.h" + +#include + +/* + * Additional gnutls configuration for client sockets. + * + * XXX: currently, we just have one global instance, set up by sock_gnutls_init, used for all sockets + */ +struct sock_gnutls_client_ctx { + gnutls_certificate_credentials_t xcred; +}; + +/* + * Per-sock state, this includes the sock_tcp connection + */ +struct sock_gnutls { + /* SSL connections use TCP connections */ + struct sock_tcp base_tcp; + + /* 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; +}; + +#define SOCK_GNUTLS_BASE(sock_ptr) (&(sock_ptr)->base_tcp.base) +#define SOCK_GNUTLS_TCP(sock_ptr) (&(sock_ptr)->base_tcp) + +/* + * Initialize the global gnutls state + */ +void sock_gnutls_init (void); + +#endif /* SOCK_GNUTLS_H */ diff -r cf0e1bb6bcab -r a834f0559939 src/sock_tcp.c --- a/src/sock_tcp.c Sun Feb 22 03:57:44 2009 +0200 +++ b/src/sock_tcp.c Sun Feb 22 05:27:29 2009 +0200 @@ -30,33 +30,43 @@ } /* - * Define our sock_stream_Type + * Our sock_stream_type + * + * XXX: move to sock_tcp.h */ struct sock_stream_type sock_tcp_type = { .methods.read = &sock_tcp_read, .methods.write = &sock_tcp_write, }; -struct sock_stream* sock_tcp_new (int fd) +struct sock_tcp* sock_tcp_alloc (void) { struct sock_tcp *sock; // alloc if ((sock = calloc(1, sizeof(*sock))) == NULL) errx(1, "calloc"); + + // initialize base + sock->base.type = &sock_tcp_type; + // done + return sock; +} + +int sock_tcp_init_fd (struct sock_tcp *sock, int fd) +{ // initialize - sock->base.type = &sock_tcp_type; sock->fd = fd; // done - return SOCK_TCP_BASE(sock); + return 0; } -struct sock_stream* sock_tcp_connect (const char *host, const char *service) +int sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service) { struct addrinfo hints, *res, *r; - int _err, fd = -1; + int _err; // hints memset(&hints, 0, sizeof(hints)); @@ -64,26 +74,40 @@ hints.ai_socktype = SOCK_STREAM; // resolve - if ((_err = getaddrinfo(host, service, &hints, &res))) + if ((_err = getaddrinfo(hostname, service, &hints, &res))) errx(1, "getaddrinfo: %s", gai_strerror(_err)); // use for (r = res; r; r = r->ai_next) { // XXX: wrong - if ((fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0) + if ((sock->fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0) err(1, "socket"); - if (connect(fd, r->ai_addr, r->ai_addrlen)) + if (connect(sock->fd, r->ai_addr, r->ai_addrlen)) err(1, "connect"); break; } // ensure we got some valid socket - if (fd < 0) + if (sock->fd < 0) errx(1, "no valid socket"); - // ok, return sock_stream - return sock_tcp_new(fd); + // ok, done + return 0; } +// XXX: error handling +struct sock_stream* sock_tcp_connect (const char *host, const char *service) +{ + struct sock_tcp *sock; + + // allocate + sock = sock_tcp_alloc(); + + // connect + sock_tcp_init_connect(sock, host, service); + + // done + return SOCK_TCP_BASE(sock); +} diff -r cf0e1bb6bcab -r a834f0559939 src/sock_tcp.h --- a/src/sock_tcp.h Sun Feb 22 03:57:44 2009 +0200 +++ b/src/sock_tcp.h Sun Feb 22 05:27:29 2009 +0200 @@ -20,8 +20,18 @@ #define SOCK_TCP_BASE(sock_ptr) (&(sock_ptr)->base) /* - * Create a new sock_tcp from the given fd, returning it as a sock_stream + * Allocate a new blank sock_tcp with a correctly initialized base */ -struct sock_stream* sock_tcp_new (int fd); +struct sock_tcp* sock_tcp_alloc (void); + +/* + * Initialize a blank sock_tcp with a given already-existing fd + */ +int sock_tcp_init_fd (struct sock_tcp *sock, int fd); + +/* + * Initialize a blank sock_tcp by connecting + */ +int sock_tcp_init_connect (struct sock_tcp *sock, const char *hostname, const char *service); #endif /* SOCK_TCP_H */