]> jfr.im git - irc/irssi/irssi.git/commitdiff
Initial irssiproxy SSL support
authordequis <redacted>
Sat, 27 Jun 2015 14:33:55 +0000 (11:33 -0300)
committerdequis <redacted>
Tue, 6 Oct 2015 10:39:41 +0000 (07:39 -0300)
Patch by "Christian Sachs" from FS#645, rebased to current head.

http://bugs.irssi.org/index.php?do=details&task_id=645

src/irc/proxy/dump.c
src/irc/proxy/listen.c
src/irc/proxy/module.h
src/irc/proxy/proxy.c
src/irc/proxy/proxy.h

index e39c21a6de5da4489fb1fddc8cb4a8c57db6f152..0ca9ebb48b58c85c7c368fd02af94df38dec281b 100644 (file)
 #include "irc-channels.h"
 #include "irc-nicklist.h"
 #include "modes.h"
+#include "line-split.h"
+
+void proxy_send(CLIENT_REC *client, char *d, int l)
+{
+#ifdef HAVE_OPENSSL
+       if(client->listen->use_ssl) {
+               SSL_write(client->ssl, d, l);
+       } else 
+#endif
+               net_sendbuffer_send(client->handle, d, l);
+}
+
+int proxy_readline(CLIENT_REC *client, char **str)
+{
+#ifdef HAVE_OPENSSL
+       if(client->listen->use_ssl) {
+               char tmpbuf[2048];
+               int recvlen = 0;
+        
+               recvlen = SSL_read(client->ssl, tmpbuf, sizeof(tmpbuf));
+               if(recvlen > 0) {
+                       return line_split(tmpbuf, recvlen, str, &client->handle->readbuffer);
+               } else {
+                       int err;
+                       err = SSL_get_error(client->ssl, recvlen);
+                       /* READ/WRITE are not really errors, they just indicate that atm 
+                          OpenSSL is waiting for more data */
+                       if(err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+                               return line_split(tmpbuf, 0, str, &client->handle->readbuffer);
+                       }
+                       return recvlen; /* if any other error occurs, this will quit the connection */
+               }
+       } else 
+#endif
+               return net_sendbuffer_receive_line(client->handle, str, 1);
+}
 
 void proxy_outdata(CLIENT_REC *client, const char *data, ...)
 {
@@ -41,7 +77,7 @@ void proxy_outdata(CLIENT_REC *client, const char *data, ...)
        va_start(args, data);
 
        str = g_strdup_vprintf(data, args);
-       net_sendbuffer_send(client->handle, str, strlen(str));
+       proxy_send(client, str, strlen(str));
        g_free(str);
 
        va_end(args);
@@ -65,7 +101,7 @@ void proxy_outdata_all(IRC_SERVER_REC *server, const char *data, ...)
                CLIENT_REC *rec = tmp->data;
 
                if (rec->connected && rec->server == server)
-                       net_sendbuffer_send(rec->handle, str, len);
+                       proxy_send(rec, str, len);
        }
        g_free(str);
 
index dcc94e6b8d468f19c200f6a481fecec74b6a268d..55d66efbba762d250f30218027a41ee9a10f69c2 100644 (file)
@@ -50,6 +50,11 @@ static void remove_client(CLIENT_REC *rec)
        printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE,
                  "Proxy: Client %s:%d disconnected", rec->host, rec->port);
 
+#ifdef HAVE_OPENSSL
+       if(rec->listen->use_ssl) {
+               SSL_free(rec->ssl);     
+       }
+#endif
        g_free(rec->proxy_address);
        net_sendbuffer_destroy(rec->handle, TRUE);
        g_source_remove(rec->recv_tag);
@@ -133,6 +138,13 @@ static void handle_client_connect_cmd(CLIENT_REC *client,
                                  "Proxy: Client %s:%d connected",
                                  client->host, client->port);
                        client->connected = TRUE;
+#ifdef HAVE_OPENSSL
+       if(client->listen->use_ssl) {
+               printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+                         "Proxy: Client connected from %s using encryption %s and logged in!", client->host, SSL_get_cipher(client->ssl));
+       }
+#endif
+
                        proxy_dump_data(client);
                }
        }
@@ -310,7 +322,7 @@ static void sig_listen_client(CLIENT_REC *client)
        g_return_if_fail(client != NULL);
 
        while (g_slist_find(proxy_clients, client) != NULL) {
-               ret = net_sendbuffer_receive_line(client->handle, &str, 1);
+               ret = proxy_readline(client, &str);
                if (ret == -1) {
                        /* connection lost */
                        remove_client(client);
@@ -350,6 +362,26 @@ static void sig_listen(LISTEN_REC *listen)
        net_ip2host(&ip, host);
        sendbuf = net_sendbuffer_create(handle, 0);
        rec = g_new0(CLIENT_REC, 1);
+       
+#ifdef HAVE_OPENSSL
+       if(listen->use_ssl) {
+               rec->ssl = SSL_new(listen->ssl_ctx);
+               SSL_set_fd(rec->ssl, g_io_channel_unix_get_fd(handle));
+               int sslerror = SSL_accept(rec->ssl); /* handle error! */
+               if(sslerror <= 0) {
+                       /* The Handshake might take longer and the client might not be ready yet
+                          so if such an error occurs, we just ignore it, SSL_read and SSL_write
+                          should continue with the handshake. */
+                       if(SSL_get_error(rec->ssl, sslerror) != SSL_ERROR_WANT_READ) {
+                               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                                   "Proxy: An error occured while accepting SSL connection!");
+                               g_free(rec);
+                               return;  
+                       }
+               }
+       }
+#endif
+               
        rec->listen = listen;
        rec->handle = sendbuf;
         rec->host = g_strdup(host);
@@ -416,7 +448,7 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
                if (sscanf(signal+6, "%p", &client) == 1) {
                        /* send it to specific client only */
                        if (g_slist_find(proxy_clients, client) != NULL)
-                               net_sendbuffer_send(((CLIENT_REC *) client)->handle, next_line->str, next_line->len);
+                               proxy_send((CLIENT_REC *) client, next_line->str, next_line->len);
                        g_free(event);
                         signal_stop();
                        return;
@@ -433,7 +465,7 @@ static void sig_server_event(IRC_SERVER_REC *server, const char *line,
                        if (rec->want_ctcp == 1) {
                                /* only CTCP for the chatnet where client is connected to will be forwarded */
                                if (strstr(rec->proxy_address, server->connrec->chatnet) != NULL) {
-                                       net_sendbuffer_send(rec->handle,
+                                       proxy_send(rec,
                                                            next_line->str, next_line->len);
                                        signal_stop();
                                }
@@ -582,7 +614,7 @@ static LISTEN_REC *find_listen(const char *ircnet, int port)
        return NULL;
 }
 
-static void add_listen(const char *ircnet, int port)
+static void add_listen(const char *ircnet, int port, char *sslcert)
 {
        LISTEN_REC *rec;
        IPADDR ip4, ip6, *my_ip;
@@ -620,6 +652,51 @@ static void add_listen(const char *ircnet, int port)
                return;
        }
 
+       if(sslcert != NULL) {
+#ifdef HAVE_OPENSSL
+               rec->use_ssl = TRUE;
+               rec->ssl_method = SSLv3_server_method(); /* let's start with 3 */
+               rec->ssl_ctx = SSL_CTX_new(rec->ssl_method);
+               if(rec->ssl_ctx == NULL) {
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                         "Proxy: Error setting up SSL Context for port %d failed.",
+                         rec->port);
+                       g_free(rec->ircnet);
+            g_free(rec);
+            return;
+               }
+               
+               if(SSL_CTX_use_certificate_file(rec->ssl_ctx, sslcert, SSL_FILETYPE_PEM) <= 0) {
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading certificate.");
+                       SSL_CTX_free(rec->ssl_ctx);
+                       g_free(rec->ircnet);
+            g_free(rec);
+            return;
+               }
+               
+               if(SSL_CTX_use_PrivateKey_file(rec->ssl_ctx, sslcert, SSL_FILETYPE_PEM) <= 0) {
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading private key.");
+                       SSL_CTX_free(rec->ssl_ctx);     
+                       g_free(rec->ircnet);
+            g_free(rec);
+            return;
+               }
+               
+               if(!SSL_CTX_check_private_key(rec->ssl_ctx)) {
+                       printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading checking certificate agains private key.");
+                       SSL_CTX_free(rec->ssl_ctx);     
+                       g_free(rec->ircnet);
+            g_free(rec);
+            return;
+               }
+
+#else
+               printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+                       "Proxy: Specified SSL certificate/private key but irssi compiled WITHOUT OpenSSL!");
+#endif
+
+       }
+
        rec->tag = g_input_add(rec->handle, G_INPUT_READ,
                               (GInputFunction) sig_listen, rec);
 
@@ -634,6 +711,11 @@ static void remove_listen(LISTEN_REC *rec)
                remove_client(rec->clients->data);
 
        net_disconnect(rec->handle);
+#ifdef HAVE_OPENSSL
+       if(rec->use_ssl) {
+               SSL_CTX_free(rec->ssl_ctx);     
+       }
+#endif
        g_source_remove(rec->tag);
        g_free(rec->ircnet);
        g_free(rec);
@@ -644,7 +726,7 @@ static void read_settings(void)
        LISTEN_REC *rec;
        GSList *remove_listens = NULL;
        GSList *add_listens = NULL;
-       char **ports, **tmp, *ircnet, *port;
+       char **ports, **tmp, *ircnet, *port, *sslfile;
        int portnum;
 
        remove_listens = g_slist_copy(proxy_listens);
@@ -657,6 +739,13 @@ static void read_settings(void)
                        continue;
 
                *port++ = '\0';
+               
+               sslfile = strchr(port, ':');
+               
+               if (sslfile != NULL) {
+                       *sslfile++ = '\0';      
+               }
+               
                portnum = atoi(port);
                if (portnum <=  0)
                        continue;
@@ -680,7 +769,7 @@ static void read_settings(void)
 
        while (add_listens != NULL) {
                rec = add_listens->data;
-               add_listen(rec->ircnet, rec->port);
+               add_listen(rec->ircnet, rec->port, sslfile);
                g_free(rec);
                add_listens = g_slist_remove(add_listens, add_listens->data);
        }
index ff95227fcd50d54c46cf1139c22d955cfe05e757..74b43df6ac6388e16d2837fc1b6c2e0f0e275c7d 100644 (file)
@@ -24,3 +24,6 @@ void proxy_outdata_all(IRC_SERVER_REC *server, const char *data, ...);
 void proxy_outserver(CLIENT_REC *client, const char *data, ...);
 void proxy_outserver_all(IRC_SERVER_REC *server, const char *data, ...);
 void proxy_outserver_all_except(CLIENT_REC *client, const char *data, ...);
+
+void proxy_send(CLIENT_REC *client, char *d, int l);
+int proxy_readline(CLIENT_REC *client, char **str);
index ce79e2b7f17e4881ecfc57ae7854bcb5f4f7f163..841263ebd6b3be68fee6971d093a674a25892388 100644 (file)
@@ -78,6 +78,11 @@ void irc_proxy_init(void)
        settings_add_str("irssiproxy", "irssiproxy_bind", "");
        settings_add_bool("irssiproxy", "irssiproxy", TRUE);
 
+#ifdef HAVE_OPENSSL
+       SSL_load_error_strings();
+       OpenSSL_add_ssl_algorithms();
+#endif
+
        if (*settings_get_str("irssiproxy_password") == '\0') {
                /* no password - bad idea! */
                signal_emit("gui dialog", 2, "warning",
@@ -87,9 +92,16 @@ void irc_proxy_init(void)
        }
        if (*settings_get_str("irssiproxy_ports") == '\0') {
                signal_emit("gui dialog", 2, "warning",
-                           "No proxy ports specified. Use /SET "
+                           "No proxy ports specified. Use /set "
+#ifdef HAVE_OPENSSL
+                           "irssiproxy_ports <ircnet>=<port> <ircnet2>=<port2>:<sslcert> "
+                           "... to set them. You can add :filename.pem to secure the proxy with SSL."
+                           " (Should contain a cert and key in PEM format)");
+#else
                            "irssiproxy_ports <ircnet>=<port> <ircnet2>=<port2> "
                            "... to set them.");
+#endif
+
        }
 
        command_bind("irssiproxy", NULL, (SIGNAL_FUNC) cmd_irssiproxy);
@@ -101,7 +113,7 @@ void irc_proxy_init(void)
                proxy_listen_init();
        }
        settings_check();
-        module_register("proxy", "irc");
+       module_register("proxy", "irc");
 }
 
 void irc_proxy_deinit(void)
index 158b067570551c0922adfba38a0c0d3a50ca261e..e2ca67b2c6ee075a287208a6cc9bc8e9f7d361a7 100644 (file)
@@ -7,6 +7,15 @@
 #include "irc.h"
 #include "irc-servers.h"
 
+#ifdef HAVE_OPENSSL
+#include <openssl/rsa.h>
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
 typedef struct {
        int port;
        char *ircnet;
@@ -15,6 +24,11 @@ typedef struct {
        GIOChannel *handle;
 
        GSList *clients;
+#ifdef HAVE_OPENSSL
+       unsigned int use_ssl;
+       SSL_CTX *ssl_ctx;
+       SSL_METHOD *ssl_method;
+#endif
 } LISTEN_REC;
 
 typedef struct {
@@ -29,6 +43,9 @@ typedef struct {
        unsigned int user_sent:1;
        unsigned int connected:1;
        unsigned int want_ctcp:1;
+#ifdef HAVE_OPENSSL
+       SSL *ssl;
+#endif
 } CLIENT_REC;
 
 #endif