diff --git a/src/irc/proxy/dump.c b/src/irc/proxy/dump.c index e39c21a6..432eadc0 100644 --- a/src/irc/proxy/dump.c +++ b/src/irc/proxy/dump.c @@ -29,6 +29,39 @@ #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) +{ + if(client->listen->use_ssl) { + SSL_write(client->ssl, d, l); + return; + } + net_sendbuffer_send(client->handle, d, l); +} + +int proxy_readline(CLIENT_REC *client, char **str) +{ + 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 */ + } + } + return net_sendbuffer_receive_line(client->handle, str, 1); +} void proxy_outdata(CLIENT_REC *client, const char *data, ...) { @@ -41,7 +74,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 +98,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); diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c index dcc94e6b..8db84e15 100644 --- a/src/irc/proxy/listen.c +++ b/src/irc/proxy/listen.c @@ -50,6 +50,9 @@ static void remove_client(CLIENT_REC *rec) printtext(rec->server, NULL, MSGLEVEL_CLIENTNOTICE, "Proxy: Client %s:%d disconnected", rec->host, rec->port); + if(rec->listen->use_ssl) { + SSL_free(rec->ssl); + } g_free(rec->proxy_address); net_sendbuffer_destroy(rec->handle, TRUE); g_source_remove(rec->recv_tag); @@ -133,6 +136,11 @@ static void handle_client_connect_cmd(CLIENT_REC *client, "Proxy: Client %s:%d connected", client->host, client->port); client->connected = TRUE; + 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)); + } + proxy_dump_data(client); } } @@ -310,7 +318,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 +358,24 @@ static void sig_listen(LISTEN_REC *listen) net_ip2host(&ip, host); sendbuf = net_sendbuffer_create(handle, 0); rec = g_new0(CLIENT_REC, 1); + + 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; + } + } + } + rec->listen = listen; rec->handle = sendbuf; rec->host = g_strdup(host); @@ -416,7 +442,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 +459,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 +608,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,10 +646,45 @@ static void add_listen(const char *ircnet, int port) return; } + if(sslcert != NULL) { + rec->use_ssl = TRUE; + rec->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); + if(rec->ssl_ctx == NULL) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, + "Proxy: Error setting up SSL Context for port %d failed.", + rec->port); + goto error; + } + SSL_CTX_set_options(rec->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + + if(SSL_CTX_use_certificate_file(rec->ssl_ctx, sslcert, SSL_FILETYPE_PEM) <= 0) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading certificate."); + goto error; + } + + if(SSL_CTX_use_PrivateKey_file(rec->ssl_ctx, sslcert, SSL_FILETYPE_PEM) <= 0) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading private key."); + goto error; + } + + if(!SSL_CTX_check_private_key(rec->ssl_ctx)) { + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Proxy: Error loading checking certificate agains private key."); + goto error; + } + } + rec->tag = g_input_add(rec->handle, G_INPUT_READ, (GInputFunction) sig_listen, rec); proxy_listens = g_slist_append(proxy_listens, rec); + + return; +error: + if (rec->ssl_ctx != NULL) { + SSL_CTX_free(rec->ssl_ctx); + } + g_free(rec->ircnet); + g_free(rec); } static void remove_listen(LISTEN_REC *rec) @@ -634,6 +695,9 @@ static void remove_listen(LISTEN_REC *rec) remove_client(rec->clients->data); net_disconnect(rec->handle); + if(rec->use_ssl) { + SSL_CTX_free(rec->ssl_ctx); + } g_source_remove(rec->tag); g_free(rec->ircnet); g_free(rec); @@ -645,6 +709,7 @@ static void read_settings(void) GSList *remove_listens = NULL; GSList *add_listens = NULL; char **ports, **tmp, *ircnet, *port; + char *sslfile = NULL; int portnum; remove_listens = g_slist_copy(proxy_listens); @@ -657,6 +722,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 +752,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); } diff --git a/src/irc/proxy/module.h b/src/irc/proxy/module.h index ff95227f..74b43df6 100644 --- a/src/irc/proxy/module.h +++ b/src/irc/proxy/module.h @@ -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); diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c index ce79e2b7..3e537f5c 100644 --- a/src/irc/proxy/proxy.c +++ b/src/irc/proxy/proxy.c @@ -78,6 +78,9 @@ void irc_proxy_init(void) settings_add_str("irssiproxy", "irssiproxy_bind", ""); settings_add_bool("irssiproxy", "irssiproxy", TRUE); + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + if (*settings_get_str("irssiproxy_password") == '\0') { /* no password - bad idea! */ signal_emit("gui dialog", 2, "warning", @@ -87,9 +90,11 @@ void irc_proxy_init(void) } if (*settings_get_str("irssiproxy_ports") == '\0') { signal_emit("gui dialog", 2, "warning", - "No proxy ports specified. Use /SET " - "irssiproxy_ports = = " - "... to set them."); + "No proxy ports specified. Use /set " + "irssiproxy_ports = =: " + "... to set them. You can add :filename.pem to secure the proxy with SSL." + " (Should contain a cert and key in PEM format)"); + } command_bind("irssiproxy", NULL, (SIGNAL_FUNC) cmd_irssiproxy); @@ -101,7 +106,7 @@ void irc_proxy_init(void) proxy_listen_init(); } settings_check(); - module_register("proxy", "irc"); + module_register("proxy", "irc"); } void irc_proxy_deinit(void) diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h index 158b0675..0b6b9385 100644 --- a/src/irc/proxy/proxy.h +++ b/src/irc/proxy/proxy.h @@ -7,6 +7,13 @@ #include "irc.h" #include "irc-servers.h" +#include +#include +#include +#include +#include +#include + typedef struct { int port; char *ircnet; @@ -15,6 +22,8 @@ typedef struct { GIOChannel *handle; GSList *clients; + unsigned int use_ssl; + SSL_CTX *ssl_ctx; } LISTEN_REC; typedef struct { @@ -29,6 +38,7 @@ typedef struct { unsigned int user_sent:1; unsigned int connected:1; unsigned int want_ctcp:1; + SSL *ssl; } CLIENT_REC; #endif