]> Pileus Git - ~andy/lamechat/commitdiff
Support GnuTLS
authorAndy Spencer <andy753421@gmail.com>
Sun, 29 Oct 2017 13:03:16 +0000 (13:03 +0000)
committerAndy Spencer <andy753421@gmail.com>
Sun, 29 Oct 2017 13:03:16 +0000 (13:03 +0000)
irc.c
makefile
net.c
net.h
xmpp.c

diff --git a/irc.c b/irc.c
index d2be0c322bdcf6621f7db1baa7f65fb029211b43..f99994ed8a54eb061e8ba9a44125ab490e70b7ac 100644 (file)
--- a/irc.c
+++ b/irc.c
@@ -72,6 +72,7 @@ typedef struct {
        server_t      server;
 
        int           connect;
+       int           noverify;
        const char   *host;
        int           port;
        int           tls;
@@ -341,7 +342,7 @@ static void irc_run(irc_server_t *srv, const char *line)
 
        /* Encryption */
        if (srv->state == IRC_ENCRYPT) {
-               net_encrypt(&srv->net);
+               net_encrypt(&srv->net, srv->noverify ? NET_NOVERIFY : 0);
                srv->state = IRC_SEND_SASL;
        }
 
@@ -505,6 +506,8 @@ void irc_config(server_t *server, channel_t *channel,
                }
                else if (match(key, "connect"))
                        srv->connect = get_bool(value);
+               else if (match(key, "noverify"))
+                       srv->noverify = get_bool(value);
                else if (match(key, "host"))
                        srv->host = get_string(value);
                else if (match(key, "port"))
index 04f6f993378b4d60063565122daae7368b1fe35b..0535f81c3e57dd9ee68dd5b12d6a804168a2eaf3 100644 (file)
--- a/makefile
+++ b/makefile
@@ -8,15 +8,32 @@ VERSION   ?= 0.1-rc1
 PREFIX    ?= /usr/local
 MANPREFIX ?= $(PREFIX)/share/man
 
+# Features
+CRYPTO    ?= GNUTLS
+
 # Compiler
 GCC       ?= gcc
 CFLAGS    ?= -Wall --std=c99
-LDFLAGS   ?= -lncursesw -lexpat -lcrypto -lssl
+LDFLAGS   ?= -lncursesw -lexpat
 
 # Sources
 PROG      ?= lamechat
 SOURCES   ?= main util args conf view chat net irc xmpp 
 
+# OpenSSL
+ifeq ($(CRYPTO),OPENSSL)
+net.o: \
+CFLAGS    += -DUSE_OPENSSL
+LDFLAGS   += -lcrypto -lssl
+endif
+
+# GnuTLS
+ifeq ($(CRYPTO),GNUTLS)
+net.o: \
+CFLAGS    += -DUSE_GNUTLS
+LDFLAGS   += -lgnutls
+endif
+
 # Targets
 all: $(PROG)
 
@@ -40,12 +57,11 @@ memcheck: $(PROG)
                 --track-origins=yes     \
                 --leak-check=full       \
                 --leak-resolution=high  \
-                --show-leak-kinds=all   \
                 ./$(PROG)
 
 # Rules
 $(PROG): $(SOURCES:%=%.o)
-       $(GCC) $(CFLAGS) -o $@ $+ $(LDFLAGS)
+       $(GCC) -o $@ $+ $(LDFLAGS)
 
 %.o: %.c $(wildcard *.h makefile config.mk)
        $(GCC) $(CFLAGS) -c -o $@ $<
diff --git a/net.c b/net.c
index d7937e4ddc396bd20a2fc8d86d896f9bdbcc8b66..a1c11c8446e90e4ec36a0839a70acd65d75fa061 100644 (file)
--- a/net.c
+++ b/net.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 
+#ifdef USE_OPENSSL
 #include <openssl/bio.h>
 #include <openssl/ssl.h>
+#endif
+#ifdef USE_GNUTLS
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#endif
 
 #include "util.h"
 #include "net.h"
 
+/* Crypto types */
+struct crypto_t {
+#ifdef USE_OPENSSL
+       SSL_CTX *ctx;
+       SSL     *ssl;
+       BIO     *in;
+       BIO     *out;
+#endif
+#ifdef USE_GNUTLS
+       gnutls_session_t tls;
+#endif
+};
+
+/* Local data */
+#ifdef USE_GNUTLS
+static gnutls_certificate_credentials_t xcred;
+#endif
+
 /* Local functions */
 static int flush(net_t *net)
 {
+#ifdef USE_OPENSSL
        static char buf[NET_BUFFER];
+#endif
        int len;
 
        /* State machine */
@@ -56,9 +83,10 @@ static int flush(net_t *net)
                }
        }
        if (net->state == NET_ENCRYPTED) {
+#ifdef USE_OPENSSL
                 if (net->out_len > 0) {
                        debug("net: flush crypto");
-                       len = SSL_write(net->ssl,
+                       len = SSL_write(net->crypto->ssl,
                                        &net->out_buf[net->out_pos],
                                        net->out_len-net->out_pos);
                        if (len > 0)
@@ -71,6 +99,20 @@ static int flush(net_t *net)
 
                while ((len = BIO_read(net->out, buf, sizeof(buf))) > 0)
                        send(net->poll.fd, buf, len, 0);
+#endif
+#ifdef USE_GNUTLS
+                if (net->out_len > 0) {
+                       len = gnutls_record_send(net->crypto->tls,
+                                          &net->out_buf[net->out_pos],
+                                          net->out_len-net->out_pos);
+                       if (len > 0)
+                               net->out_pos += len;
+                       if (net->out_pos == net->out_len) {
+                               net->out_pos = 0;
+                               net->out_len = 0;
+                       }
+               }
+#endif
        }
 
        return 1;
@@ -93,6 +135,9 @@ static void on_poll(void *_net)
                }
 
                /* State machine */
+               if (net->state == NET_CLOSED) {
+                       done = 1;
+               }
                if (net->state == NET_CONNECT) {
                        debug("net: connect");
                        net->state = NET_READY;
@@ -113,6 +158,7 @@ static void on_poll(void *_net)
                }
                if (net->state == NET_HANDSHAKE) {
                        debug("net: handshake");
+#ifdef USE_OPENSSL
                        len = recv(net->poll.fd, buf, sizeof(buf), 0);
                        if (len < 0)
                                done = 1;
@@ -121,27 +167,59 @@ static void on_poll(void *_net)
                        if (len > 0)
                                BIO_write(net->in, buf, len);
 
-                       SSL_do_handshake(net->ssl);
+                       SSL_do_handshake(net->crypto->ssl);
 
                        while ((len = BIO_read(net->out, buf, sizeof(buf))) > 0)
                                send(net->poll.fd, buf, len, 0);
 
-                       if (SSL_is_init_finished(net->ssl))
+                       if (SSL_is_init_finished(net->crypto->ssl))
+                               net->state = NET_ENCRYPTED;
+#endif
+#ifdef USE_GNUTLS
+                       err = gnutls_handshake(net->crypto->tls);
+                       if (err == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR) {
+                               gnutls_datum_t out;
+                               int type = gnutls_certificate_type_get(net->crypto->tls);
+                               unsigned status = gnutls_session_get_verify_cert_status(net->crypto->tls);
+                               gnutls_certificate_verification_status_print(status, type, &out, 0);
+                               debug("Cert verify failed: %s", out.data);
+                               gnutls_free(out.data);
+                               net_close(net);
+                       } else if (err < 0) {
+                               debug("Handshake failed: %s", gnutls_strerror(err));
+                               net_close(net);
+                       } else {
+                               char *desc = gnutls_session_get_desc(net->crypto->tls);
+                               debug("Session info: %s", desc);
+                               gnutls_free(desc);
                                net->state = NET_ENCRYPTED;
+                       }
+#endif
                        flush(net);
                }
                if (net->state == NET_ENCRYPTED) {
                        debug("net: encrypted");
+#ifdef USE_OPENSSL
                        len = recv(net->poll.fd, buf, sizeof(buf), 0);
                        if (len < 0)
                                done = 1;
                        if (len == 0)
                                net_close(net);
                        if (len > 0)
-                               BIO_write(net->in, buf, len);
+                               BIO_write(net->crypto->in, buf, len);
 
-                       while ((len = SSL_read(net->ssl, buf, sizeof(buf))) > 0)
+                       while ((len = SSL_read(net->crypto->ssl, buf, sizeof(buf))) > 0)
+                               net->recv(net->data, buf, len);
+#endif
+#ifdef USE_GNUTLS
+                       len = gnutls_record_recv(net->crypto->tls, buf, sizeof(buf));
+                       if (len < 0)
+                               done = 1;
+                       if (len == 0)
+                               net_close(net);
+                       if (len > 0)
                                net->recv(net->data, buf, len);
+#endif
                }
        }
 }
@@ -159,10 +237,19 @@ const char *get_hostname(void)
 
 void net_init(void)
 {
+#ifdef USE_OPENSSL
        SSL_library_init();
        SSL_load_error_strings();
        ERR_load_BIO_strings();
        OpenSSL_add_all_algorithms();
+#endif
+#ifdef USE_GNUTLS
+       gnutls_global_init();
+       gnutls_certificate_allocate_credentials(&xcred);
+       gnutls_certificate_set_x509_trust_file(xcred,
+                       "/etc/ssl/certs/ca-certificates.crt",
+                       GNUTLS_X509_FMT_PEM);
+#endif
 }
 
 void net_open(net_t *net, const char *host, int port)
@@ -218,21 +305,37 @@ void net_open(net_t *net, const char *host, int port)
        poll_add(&net->poll, sock, on_poll, net);
 }
 
-void net_encrypt(net_t *net)
+void net_encrypt(net_t *net, int flags)
 {
        debug("net: encrypt");
 
-       net->ctx = SSL_CTX_new(TLSv1_2_client_method());
-       net->ssl = SSL_new(net->ctx);
-
-       net->in  = BIO_new(BIO_s_mem());
-       net->out = BIO_new(BIO_s_mem());
-
-       BIO_set_mem_eof_return(net->in,  -1);
-       BIO_set_mem_eof_return(net->out, -1);
-
-       SSL_set_bio(net->ssl, net->in, net->out);
-       SSL_set_connect_state(net->ssl);
+#if defined(USE_OPENSSL)
+       net->crypto = new0(crypto_t);
+       net->crypto->ctx = SSL_CTX_new(TLSv1_2_client_method());
+       net->crypto->ssl = SSL_new(net->ctx);
+
+       net->crypto->in  = BIO_new(BIO_s_mem());
+       net->crypto->out = BIO_new(BIO_s_mem());
+
+       BIO_set_mem_eof_return(net->crypto->in,  -1);
+       BIO_set_mem_eof_return(net->crypto->out, -1);
+
+       SSL_set_bio(net->crypto->ssl, net->crypto->in, net->crypto->out);
+       SSL_set_connect_state(net->crypto->ssl);
+#elif defined(USE_GNUTLS)
+       net->crypto = new0(crypto_t);
+       gnutls_init(&net->crypto->tls, GNUTLS_CLIENT);
+       gnutls_set_default_priority(net->crypto->tls);
+       gnutls_server_name_set(net->crypto->tls, GNUTLS_NAME_DNS,
+                              net->host, strlen(net->host));
+       gnutls_credentials_set(net->crypto->tls, GNUTLS_CRD_CERTIFICATE, xcred);
+        gnutls_handshake_set_timeout(net->crypto->tls, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+        gnutls_transport_set_int(net->crypto->tls, net->poll.fd);
+        if (!(flags & NET_NOVERIFY))
+               gnutls_session_set_verify_cert(net->crypto->tls, net->host, 0);
+#else
+       error("Encryption is not supported");
+#endif
 
        net->state = NET_ENCRYPT;
 }
diff --git a/net.h b/net.h
index a2d802070a3fede49882ba4f629e70876b9e4cae..a0b55610315e3386ed83bed5191cfe096fad21a2 100644 (file)
--- a/net.h
+++ b/net.h
 /* Networking Constants */
 #define NET_BUFFER 1024
 
-/* OpenSSL Types */
-typedef struct ssl_ctx_st SSL_CTX;
-typedef struct ssl_st     SSL;
-typedef struct bio_st     BIO;
-
 /* Networking Callbacks */
 typedef void (*send_t)(void *data);
 typedef void (*recv_t)(void *data, char *buf, int len);
 typedef void (*err_t)(void *data, int errno);
 
 /* Networking Types */
+typedef struct crypto_t crypto_t;
+
+typedef enum {
+       NET_NOVERIFY = 0x01,
+} encrypt_t;
+
 typedef enum {
        NET_CLOSED,
        NET_CONNECT,
@@ -39,24 +40,20 @@ typedef enum {
 } nstate_t;
 
 typedef struct {
-       char    *host;
-       int      port;
-       send_t   send;
-       recv_t   recv;
-       err_t    err;
-       void    *data;
-
-       SSL_CTX *ctx;
-       SSL     *ssl;
-       BIO     *in;
-       BIO     *out;
+       char     *host;
+       int       port;
+       send_t    send;
+       recv_t    recv;
+       err_t     err;
+       void     *data;
 
-       poll_t   poll;
-       nstate_t state;
+       poll_t    poll;
+       nstate_t  state;
+       crypto_t *crypto;
 
-       char     out_buf[NET_BUFFER];
-       int      out_pos;
-       int      out_len;
+       char      out_buf[NET_BUFFER];
+       int       out_pos;
+       int       out_len;
 } net_t;
 
 /* Networking functions */
@@ -65,7 +62,7 @@ const char *get_hostname(void);
 /* Connection functions */
 void net_init(void);
 void net_open(net_t *net, const char *host, int port);
-void net_encrypt(net_t *net);
+void net_encrypt(net_t *net, int flags);
 int  net_send(net_t *net, const char *buf, int len);
 int  net_print(net_t *net, const char *fmt, ...);
 void net_close(net_t *net);
diff --git a/xmpp.c b/xmpp.c
index 76fee43260e445f1caa554b5f77a0e172cf13f81..553456112d8e2c088bc1a78c11eda2cc969f71e3 100644 (file)
--- a/xmpp.c
+++ b/xmpp.c
@@ -81,6 +81,7 @@ typedef struct {
 
        int             connect;
        int             timeout;
+       int             noverify;
        const char     *host;
        int             port;
        const char     *srv;
@@ -336,7 +337,7 @@ static void xmpp_run(xmpp_server_t *srv, int idle,
                srv->state = XMPP_SEND_STREAM;
        }
        if (srv->state == XMPP_ENCRYPT && !start && !end) {
-               net_encrypt(&srv->net);
+               net_encrypt(&srv->net, srv->noverify ? NET_NOVERIFY : 0);
 
                if (!(XML_ParserReset(srv->expat, NULL)))
                        error("Error resetting XML parser");
@@ -728,6 +729,8 @@ void xmpp_config(server_t *server, channel_t *channel,
                        srv->connect = get_bool(value);
                else if (match(key, "timeout"))
                        srv->timeout = get_number(value);
+               else if (match(key, "noverify"))
+                       srv->noverify = get_bool(value);
                else if (match(key, "host"))
                        srv->host = get_string(value);
                else if (match(key, "port"))