diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/Makefile.am | 1 | ||||
-rw-r--r-- | src/core/chat-commands.c | 10 | ||||
-rw-r--r-- | src/core/network-openssl.c | 291 | ||||
-rw-r--r-- | src/core/network.h | 2 | ||||
-rw-r--r-- | src/core/server-connect-rec.h | 1 | ||||
-rw-r--r-- | src/core/server-setup-rec.h | 1 | ||||
-rw-r--r-- | src/core/servers-reconnect.c | 2 | ||||
-rw-r--r-- | src/core/servers-setup.c | 4 | ||||
-rw-r--r-- | src/core/servers.c | 7 | ||||
-rw-r--r-- | src/core/session.c | 2 |
10 files changed, 317 insertions, 4 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am index f543f361..beee1098 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -30,6 +30,7 @@ libcore_a_SOURCES = \ net-nonblock.c \ net-sendbuffer.c \ network.c \ + network-openssl.c \ nicklist.c \ nickmatch-cache.c \ pidwait.c \ diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index 4bc931dc..aff0d8b7 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -88,12 +88,16 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, else if (g_hash_table_lookup(optlist, "4") != NULL) conn->family = AF_INET; + if(g_hash_table_lookup(optlist, "ssl") != NULL) + conn->use_ssl = TRUE; + if (g_hash_table_lookup(optlist, "!") != NULL) conn->no_autojoin_channels = TRUE; if (g_hash_table_lookup(optlist, "noproxy") != NULL) g_free_and_null(conn->proxy); + *rawlog_file = g_strdup(g_hash_table_lookup(optlist, "rawlog")); host = g_hash_table_lookup(optlist, "host"); @@ -108,7 +112,7 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, return conn; } -/* SYNTAX: CONNECT [-4 | -6] [-ircnet <ircnet>] [-host <hostname>] +/* SYNTAX: CONNECT [-4 | -6] [-ssl] [-ircnet <ircnet>] [-host <hostname>] <address>|<chatnet> [<port> [<password> [<nick>]]] */ static void cmd_connect(const char *data) { @@ -209,7 +213,7 @@ static void sig_default_command_server(const char *data, SERVER_REC *server, signal_emit("command server connect", 3, data, server, item); } -/* SYNTAX: SERVER [-4 | -6] [-ircnet <ircnet>] [-host <hostname>] +/* SYNTAX: SERVER [-4 | -6] [-ssl] [-ircnet <ircnet>] [-host <hostname>] [+]<address>|<chatnet> [<port> [<password> [<nick>]]] */ static void cmd_server_connect(const char *data, SERVER_REC *server) { @@ -439,7 +443,7 @@ void chat_commands_init(void) signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server); - command_set_options("connect", "4 6 !! +host noproxy -rawlog"); + command_set_options("connect", "4 6 !! ssl +host noproxy -rawlog"); command_set_options("join", "invite"); command_set_options("msg", "channel nick"); } diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c new file mode 100644 index 00000000..7a4e974d --- /dev/null +++ b/src/core/network-openssl.c @@ -0,0 +1,291 @@ +/* + network-ssl.c : SSL support + + Copyright (C) 2002 vjt + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "network.h" + +#ifdef HAVE_OPENSSL + +#include <openssl/crypto.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +/* ssl read */ +GIOError irssi_ssl_read(GIOChannel *, gchar *, guint, guint *); +/* ssl write */ +GIOError irssi_ssl_write(GIOChannel *, gchar *, guint, guint*); +/* ssl seek */ +GIOError irssi_ssl_seek(GIOChannel *, gint, GSeekType); +/* ssl close */ +void irssi_ssl_close(GIOChannel *); +/* ssl create watch */ +guint irssi_ssl_create_watch(GIOChannel *, gint, GIOCondition, GIOFunc, gpointer, GDestroyNotify); +/* ssl free */ +void irssi_ssl_free(GIOChannel *); + +/* ssl i/o channel object */ +typedef struct +{ + GIOChannel pad; + gint fd; + GIOChannel *giochan; + SSL *ssl; + X509 *cert; +} GIOSSLChannel; + +/* ssl function pointers */ +GIOFuncs irssi_ssl_channel_funcs = +{ + irssi_ssl_read, + irssi_ssl_write, + irssi_ssl_seek, + irssi_ssl_close, + irssi_ssl_create_watch, + irssi_ssl_free +}; + +SSL_CTX *ssl_ctx = NULL; + +#ifdef G_CAN_INLINE +G_INLINE_FUNC +#endif +gint ssl_errno(gint e) +{ + switch(e) + { + case EINVAL: + return G_IO_ERROR_INVAL; + case EINTR: + case EAGAIN: + return G_IO_ERROR_AGAIN; + default: + return G_IO_ERROR_INVAL; + } + /*UNREACH*/ + return -1; +} + +gboolean irssi_ssl_cert_step(GIOSSLChannel *chan) +{ + gint err; + switch(err = SSL_do_handshake(chan->ssl)) + { + case 1: + if(!(chan->cert = SSL_get_peer_certificate(chan->ssl))) + { + g_warning("SSL server supplied no certificate"); + return G_IO_ERROR_INVAL; + } + return G_IO_ERROR_NONE; + default: + if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ) + return G_IO_ERROR_AGAIN; + return ssl_errno(errno); + } + /*UNREACH*/ + return -1; +} + +GIOError irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret) +{ + GIOSSLChannel *chan = (GIOSSLChannel *)handle; + gint err; + + if(chan->cert == NULL) + { + gint cert_err = irssi_ssl_cert_step(chan); + if(cert_err != G_IO_ERROR_NONE) + return cert_err; + } + + err = SSL_read(chan->ssl, buf, len); + if(err < 0) + { + *ret = 0; + if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ) + return G_IO_ERROR_AGAIN; + return ssl_errno(errno); + } + else + { + *ret = err; + return G_IO_ERROR_NONE; + } + /*UNREACH*/ + return -1; +} + +GIOError irssi_ssl_write(GIOChannel *handle, gchar *buf, guint len, guint *ret) +{ + GIOSSLChannel *chan = (GIOSSLChannel *)handle; + gint err; + + if(chan->cert == NULL) + { + gint cert_err = irssi_ssl_cert_step(chan); + if(cert_err != G_IO_ERROR_NONE) + return cert_err; + } + + + err = SSL_write(chan->ssl, (const char *)buf, len); + if(err < 0) + { + *ret = 0; + if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ) + return G_IO_ERROR_AGAIN; + return ssl_errno(errno); + } + else + { + *ret = err; + return G_IO_ERROR_NONE; + } + /*UNREACH*/ + return -1; +} + +GIOError irssi_ssl_seek(GIOChannel *handle, gint offset, GSeekType type) +{ + GIOSSLChannel *chan = (GIOSSLChannel *)handle; + GIOError e; + e = g_io_channel_seek(chan->giochan, offset, type); + return (e == G_IO_ERROR_NONE) ? G_IO_ERROR_NONE : G_IO_ERROR_INVAL; +} + +void irssi_ssl_close(GIOChannel *handle) +{ + GIOSSLChannel *chan = (GIOSSLChannel *)handle; + g_io_channel_close(chan->giochan); +} + +guint irssi_ssl_create_watch(GIOChannel *handle, gint priority, GIOCondition cond, + GIOFunc func, gpointer data, GDestroyNotify notify) +{ + GIOSSLChannel *chan = (GIOSSLChannel *)handle; + return chan->giochan->funcs->io_add_watch(handle, priority, cond, func, data, notify); +} + +void irssi_ssl_free(GIOChannel *handle) +{ + GIOSSLChannel *chan = (GIOSSLChannel *)handle; + g_io_channel_unref(chan->giochan); + SSL_free(chan->ssl); + g_free(chan); +} + +gboolean irssi_ssl_init(void) +{ + SSL_library_init(); + SSL_load_error_strings(); + + ssl_ctx = SSL_CTX_new(SSLv23_client_method()); + if(!ssl_ctx) + { + g_error("Initialization of the SSL library failed"); + return FALSE; + } + + return TRUE; + +} + +GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle) +{ + GIOSSLChannel *chan; + GIOChannel *gchan; + int err, fd; + SSL *ssl; + X509 *cert = NULL; + + g_return_val_if_fail(handle != NULL, NULL); + + if(!ssl_ctx && !irssi_ssl_init()) + return NULL; + + if(!(fd = g_io_channel_unix_get_fd(handle))) + return NULL; + + if(!(ssl = SSL_new(ssl_ctx))) + { + g_warning("Failed to allocate SSL structure"); + return NULL; + } + + if(!(err = SSL_set_fd(ssl, fd))) + { + g_warning("Failed to associate socket to SSL stream"); + return NULL; + } + + if((err = SSL_connect(ssl)) <= 0) + { + switch(err = SSL_get_error(ssl, err)) + { + case SSL_ERROR_SYSCALL: + if(errno == EINTR || errno == EAGAIN) + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + break; + default: + return NULL; + } + } + else if(!(cert = SSL_get_peer_certificate(ssl))) + { + g_warning("SSL server supplied no certificate"); + return NULL; + } + else + X509_free(cert); + + chan = g_new0(GIOSSLChannel, 1); + chan->fd = fd; + chan->giochan = handle; + chan->ssl = ssl; + chan->cert = cert; + g_io_channel_ref(handle); + + gchan = (GIOChannel *)chan; + gchan->funcs = &irssi_ssl_channel_funcs; + g_io_channel_init(gchan); + + return gchan; +} + +GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip) +{ + GIOChannel *gret = net_connect_ip(ip, port, my_ip); + gret = irssi_ssl_get_iochannel(gret); + return gret; +} + +#else /* HAVE_OPENSSL */ + +GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip) +{ + g_warning("Connection failed: SSL support not enabled in this build."); + errno = ENOSYS; + return NULL; +} + +#endif /* ! HAVE_OPENSSL */ diff --git a/src/core/network.h b/src/core/network.h index 646bc05e..c6b08f9f 100644 --- a/src/core/network.h +++ b/src/core/network.h @@ -44,6 +44,8 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2); /* Connect to socket */ GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip); +/* Connect to socket with ip address and SSL*/ +GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip); /* Connect to socket with ip address */ GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip); /* Connect to named UNIX socket */ diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h index 3613759e..ce1e48a4 100644 --- a/src/core/server-connect-rec.h +++ b/src/core/server-connect-rec.h @@ -29,5 +29,6 @@ GIOChannel *connect_handle; /* connect using this handle */ unsigned int reconnection:1; /* we're trying to reconnect */ unsigned int no_autojoin_channels:1; /* don't autojoin any channels */ unsigned int unix_socket:1; /* Connect using named unix socket */ +unsigned int use_ssl:1; /* this connection uses SSL */ char *channels; char *away_reason; diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h index f94ec9ae..d04fa1fe 100644 --- a/src/core/server-setup-rec.h +++ b/src/core/server-setup-rec.h @@ -18,5 +18,6 @@ unsigned int no_proxy:1; unsigned int last_failed:1; /* if last connection attempt failed */ unsigned int banned:1; /* if we're banned from this server */ unsigned int dns_error:1; /* DNS said the host doesn't exist */ +unsigned int use_ssl:1; /* this connection uses SSL */ GHashTable *module_data; diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index 93067b2b..eee3e843 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -164,6 +164,8 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) dest->away_reason = g_strdup(src->away_reason); dest->no_autojoin_channels = src->no_autojoin_channels; + dest->use_ssl = src->use_ssl; + return dest; } diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index d16f93a7..8ccfcc27 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -108,6 +108,8 @@ void server_setup_fill_reconn(SERVER_CONNECT_REC *conn, if (sserver->password != NULL && conn->password == NULL) conn->password = g_strdup(sserver->password); + conn->use_ssl = sserver->use_ssl; + signal_emit("server setup fill reconn", 2, conn, sserver); } @@ -391,6 +393,7 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) (g_strcasecmp(family, "inet") == 0 ? AF_INET : 0); rec->address = g_strdup(server); rec->password = g_strdup(config_node_get_str(node, "password", NULL)); + rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE); rec->port = port; rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE); rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE); @@ -420,6 +423,7 @@ static void server_setup_save(SERVER_SETUP_REC *rec) iconfig_node_set_int(node, "port", rec->port); iconfig_node_set_str(node, "password", rec->password); + iconfig_node_set_bool(node, "use_ssl", rec->use_ssl); iconfig_node_set_str(node, "own_host", rec->own_host); iconfig_node_set_str(node, "family", diff --git a/src/core/servers.c b/src/core/servers.c index f26481d5..6bd843af 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -178,13 +178,18 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, server->connrec->own_ip4); port = server->connrec->proxy != NULL ? server->connrec->proxy_port : server->connrec->port; - handle = net_connect_ip(ip, port, own_ip); + handle = server->connrec->use_ssl ? + net_connect_ip_ssl(ip, port, own_ip) : + net_connect_ip(ip, port, own_ip); } else { handle = net_connect_unix(unix_socket); } if (handle == NULL) { /* failed */ + if (server->connrec->use_ssl && errno == ENOSYS) + server->no_reconnect = TRUE; + server->connection_lost = TRUE; server_connect_failed(server, g_strerror(errno)); } else { diff --git a/src/core/session.c b/src/core/session.c index 0f3d089c..004cd4f7 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -182,6 +182,8 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config, config_node_set_str(config, node, "password", server->connrec->password); config_node_set_str(config, node, "nick", server->nick); + config_node_set_bool(config, node, "use_ssl", server->connrec->use_ssl); + handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle)); config_node_set_int(config, node, "handle", handle); |