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
commit 273152762f
3 changed files with 65 additions and 79 deletions

View file

@ -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)