1
0
mirror of https://github.com/irssi/irssi.git synced 2024-12-04 14:46:39 -05:00

Rewrite SSL connection/handshake code.

git-svn-id: http://svn.irssi.org/repos/irssi/trunk@4536 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
Emanuele Giaquinta 2007-05-31 23:56:51 +00:00 committed by exg
parent 5bcafebbd4
commit 273152762f
3 changed files with 65 additions and 79 deletions

View File

@ -38,7 +38,6 @@ typedef struct
GIOChannel *giochan; GIOChannel *giochan;
SSL *ssl; SSL *ssl;
SSL_CTX *ctx; SSL_CTX *ctx;
unsigned int got_cert:1;
unsigned int verify:1; unsigned int verify:1;
} GIOSSLChannel; } GIOSSLChannel;
@ -110,45 +109,11 @@ static GIOStatus ssl_errno(gint e)
return G_IO_STATUS_ERROR; 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) static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
{ {
GIOSSLChannel *chan = (GIOSSLChannel *)handle; GIOSSLChannel *chan = (GIOSSLChannel *)handle;
gint err; 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); err = SSL_read(chan->ssl, buf, len);
if(err < 0) if(err < 0)
{ {
@ -171,13 +136,6 @@ static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len
GIOSSLChannel *chan = (GIOSSLChannel *)handle; GIOSSLChannel *chan = (GIOSSLChannel *)handle;
gint err; 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); err = SSL_write(chan->ssl, (const char *)buf, len);
if(err < 0) if(err < 0)
{ {
@ -265,7 +223,6 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycer
GIOChannel *gchan; GIOChannel *gchan;
int err, fd; int err, fd;
SSL *ssl; SSL *ssl;
X509 *cert = NULL;
SSL_CTX *ctx = NULL; SSL_CTX *ctx = NULL;
g_return_val_if_fail(handle != NULL, 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; 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 = g_new0(GIOSSLChannel, 1);
chan->fd = fd; chan->fd = fd;
chan->giochan = handle; chan->giochan = handle;
chan->ssl = ssl; chan->ssl = ssl;
chan->ctx = ctx; chan->ctx = ctx;
chan->got_cert = cert != NULL;
chan->verify = verify; chan->verify = verify;
gchan = (GIOChannel *)chan; gchan = (GIOChannel *)chan;
@ -397,6 +318,32 @@ GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *
return ssl_handle; 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 */ #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) 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)

View File

@ -48,6 +48,7 @@ int net_ip_compare(IPADDR *ip1, IPADDR *ip2);
GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip); GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
/* Connect to socket with ip address and SSL*/ /* 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); 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 */ /* Connect to socket with ip address */
GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip); GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip);
/* Connect to named UNIX socket */ /* Connect to named UNIX socket */

View File

@ -167,6 +167,39 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle)
server_connect_finished(server); 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, static void server_real_connect(SERVER_REC *server, IPADDR *ip,
const char *unix_socket) const char *unix_socket)
{ {
@ -218,6 +251,11 @@ server->connrec->ssl_cafile, server->connrec->ssl_capath, server->connrec->ssl_v
g_free(errmsg2); g_free(errmsg2);
} else { } else {
server->handle = net_sendbuffer_create(handle, 0); 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 = server->connect_tag =
g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ, g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
(GInputFunction) (GInputFunction)