working SSL using gnutls - a bit of a painful process
authorTero Marttila <terom@fixme.fi>
Sun, 22 Feb 2009 05:27:29 +0200
changeset 2 a834f0559939
parent 1 cf0e1bb6bcab
child 3 cc94ae754e2a
working SSL using gnutls - a bit of a painful process
Makefile
src/nexus.c
src/sock.c
src/sock.h
src/sock_gnutls.c
src/sock_gnutls.h
src/sock_tcp.c
src/sock_tcp.h
--- 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))
--- 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 {
--- 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)
 {
--- 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);
--- /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 <stdlib.h>
+#include <err.h>
+
+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);
+}
+
--- /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 <gnutls/gnutls.h>
+
+/*
+ * 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 */
--- 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);
+}
--- 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 */