summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEmanuele Giaquinta <exg@irssi.org>2007-05-31 23:56:51 +0000
committerexg <exg@dbcabf3a-b0e7-0310-adc4-f8d773084564>2007-05-31 23:56:51 +0000
commit273152762f4fe784344ee2a62ea90050fc1f3b8d (patch)
treec0a32bbdcb014ab79e3c24ef3a6bf588490a9728 /src
parent5bcafebbd4556f2b7e3f345d1785c04369774b30 (diff)
downloadirssi-273152762f4fe784344ee2a62ea90050fc1f3b8d.zip
Rewrite SSL connection/handshake code.
git-svn-id: http://svn.irssi.org/repos/irssi/trunk@4536 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src')
-rw-r--r--src/core/network-openssl.c105
-rw-r--r--src/core/network.h1
-rw-r--r--src/core/servers.c38
3 files changed, 65 insertions, 79 deletions
diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c
index 75d66050..d9bd948e 100644
--- a/src/core/network-openssl.c
+++ b/src/core/network-openssl.c
@@ -38,7 +38,6 @@ typedef struct
GIOChannel *giochan;
SSL *ssl;
SSL_CTX *ctx;
- unsigned int got_cert:1;
unsigned int verify:1;
} GIOSSLChannel;
@@ -110,45 +109,11 @@ static GIOStatus ssl_errno(gint e)
return G_IO_STATUS_ERROR;
}
-static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan)
-{
- X509 *cert;
- gint err;
- switch(err = SSL_do_handshake(chan->ssl))
- {
- case 1:
- if(!(cert = SSL_get_peer_certificate(chan->ssl)))
- {
- g_warning("SSL server supplied no certificate");
- return G_IO_STATUS_ERROR;
- }
- if (chan->verify && ! irssi_ssl_verify(chan->ssl, chan->ctx, cert)) {
- X509_free(cert);
- return G_IO_STATUS_ERROR;
- }
- X509_free(cert);
- return G_IO_STATUS_NORMAL;
- default:
- if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
- return G_IO_STATUS_AGAIN;
- return ssl_errno(errno);
- }
- /*UNREACH*/
- return G_IO_STATUS_ERROR;
-}
-
static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
{
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
gint err;
- if(! chan->got_cert)
- {
- gint cert_err = irssi_ssl_cert_step(chan);
- if(cert_err != G_IO_STATUS_NORMAL)
- return cert_err;
- }
-
err = SSL_read(chan->ssl, buf, len);
if(err < 0)
{
@@ -171,13 +136,6 @@ static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
gint err;
- if(! chan->got_cert)
- {
- gint cert_err = irssi_ssl_cert_step(chan);
- if(cert_err != G_IO_STATUS_NORMAL)
- return cert_err;
- }
-
err = SSL_write(chan->ssl, (const char *)buf, len);
if(err < 0)
{
@@ -265,7 +223,6 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycer
GIOChannel *gchan;
int err, fd;
SSL *ssl;
- X509 *cert = NULL;
SSL_CTX *ctx = NULL;
g_return_val_if_fail(handle != NULL, NULL);
@@ -336,47 +293,11 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycer
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:
- SSL_free(ssl);
- if (ctx != ssl_ctx)
- SSL_CTX_free(ctx);
- return NULL;
- }
- }
- else if(!(cert = SSL_get_peer_certificate(ssl)))
- {
- g_warning("SSL server supplied no certificate");
- if (ctx != ssl_ctx)
- SSL_CTX_free(ctx);
- SSL_free(ssl);
- return NULL;
- }
- else
- {
- if (verify && ! irssi_ssl_verify(ssl, ctx, cert)) {
- SSL_free(ssl);
- if (ctx != ssl_ctx)
- SSL_CTX_free(ctx);
- return NULL;
- }
- X509_free(cert);
- }
-
chan = g_new0(GIOSSLChannel, 1);
chan->fd = fd;
chan->giochan = handle;
chan->ssl = ssl;
chan->ctx = ctx;
- chan->got_cert = cert != NULL;
chan->verify = verify;
gchan = (GIOChannel *)chan;
@@ -397,6 +318,32 @@ GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *
return ssl_handle;
}
+int irssi_ssl_handshake(GIOChannel *handle)
+{
+ GIOSSLChannel *chan = (GIOSSLChannel *)handle;
+ int ret, err;
+ X509 *cert;
+
+ ret = SSL_connect(chan->ssl);
+ if (ret <= 0) {
+ err = SSL_get_error(chan->ssl, ret);
+ if (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) {
+ g_warning(ERR_reason_error_string(ERR_get_error()));
+ return -1;
+ }
+ return err == SSL_ERROR_WANT_READ ? 1 : 3;
+ }
+
+ cert = SSL_get_peer_certificate(chan->ssl);
+ if (cert == NULL) {
+ g_warning("SSL server supplied no certificate");
+ return -1;
+ }
+ ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, cert);
+ X509_free(cert);
+ return ret ? 0 : -1;
+}
+
#else /* HAVE_OPENSSL */
GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify)
diff --git a/src/core/network.h b/src/core/network.h
index 5e445e83..a3a4eb41 100644
--- a/src/core/network.h
+++ b/src/core/network.h
@@ -48,6 +48,7 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
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, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify);
+int irssi_ssl_handshake(GIOChannel *handle);
/* 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/servers.c b/src/core/servers.c
index 046fa072..2e9d11fc 100644
--- a/src/core/servers.c
+++ b/src/core/servers.c
@@ -167,6 +167,39 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
server_connect_finished(server);
}
+#ifdef HAVE_OPENSSL
+static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *handle)
+{
+ int error;
+
+ g_return_if_fail(IS_SERVER(server));
+
+ error = irssi_ssl_handshake(handle);
+ if (error == -1) {
+ server->connection_lost = TRUE;
+ server_connect_failed(server, NULL);
+ return;
+ }
+ if (error & 1) {
+ if (server->connect_tag != -1)
+ g_source_remove(server->connect_tag);
+ server->connect_tag = g_input_add(handle, error == 1 ? G_INPUT_READ : G_INPUT_WRITE,
+ (GInputFunction)
+ server_connect_callback_init_ssl,
+ server);
+ return;
+ }
+
+ lookup_servers = g_slist_remove(lookup_servers, server);
+ if (server->connect_tag != -1) {
+ g_source_remove(server->connect_tag);
+ server->connect_tag = -1;
+ }
+
+ server_connect_finished(server);
+}
+#endif
+
static void server_real_connect(SERVER_REC *server, IPADDR *ip,
const char *unix_socket)
{
@@ -218,6 +251,11 @@ server->connrec->ssl_cafile, server->connrec->ssl_capath, server->connrec->ssl_v
g_free(errmsg2);
} else {
server->handle = net_sendbuffer_create(handle, 0);
+#ifdef HAVE_OPENSSL
+ if (server->connrec->use_ssl)
+ server_connect_callback_init_ssl(server, handle);
+ else
+#endif
server->connect_tag =
g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
(GInputFunction)