X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=apps%2Firssi%2Fsrc%2Fcore%2Fnetwork-openssl.c;h=68fb8ea82776b837f4b00c618a06ea5963104b1e;hb=63ca577177f9f5db8e553372a9793b0c85f10715;hp=c88b5050de51a36d14dfbdb25017ce31a4c25744;hpb=60c165f23f8d5553bd8b4fd43366fbf5f24c2832;p=silc.git diff --git a/apps/irssi/src/core/network-openssl.c b/apps/irssi/src/core/network-openssl.c index c88b5050..68fb8ea8 100644 --- a/apps/irssi/src/core/network-openssl.c +++ b/apps/irssi/src/core/network-openssl.c @@ -20,6 +20,7 @@ #include "module.h" #include "network.h" +#include "misc.h" #ifdef HAVE_OPENSSL @@ -36,25 +37,67 @@ typedef struct gint fd; GIOChannel *giochan; SSL *ssl; - X509 *cert; + SSL_CTX *ctx; + unsigned int got_cert:1; + unsigned int verify:1; } GIOSSLChannel; +static SSL_CTX *ssl_ctx = NULL; + static void irssi_ssl_free(GIOChannel *handle) { GIOSSLChannel *chan = (GIOSSLChannel *)handle; g_io_channel_unref(chan->giochan); SSL_free(chan->ssl); + if (chan->ctx != ssl_ctx) + SSL_CTX_free(chan->ctx); g_free(chan); } +static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, X509 *cert) +{ + if (SSL_get_verify_result(ssl) != X509_V_OK) { + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int n; + char *str; + + g_warning("Could not verify SSL servers certificate:"); + if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL) + g_warning(" Could not get subject-name from peer certificate"); + else { + g_warning(" Subject : %s", str); + free(str); + } + if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL) + g_warning(" Could not get issuer-name from peer certificate"); + else { + g_warning(" Issuer : %s", str); + free(str); + } + if (! X509_digest(cert, EVP_md5(), md, &n)) + g_warning(" Could not get fingerprint from peer certificate"); + else { + char hex[] = "0123456789ABCDEF"; + char fp[EVP_MAX_MD_SIZE*3]; + if (n < sizeof(fp)) { + unsigned int i; + for (i = 0; i < n; i++) { + fp[i*3+0] = hex[(md[i] >> 4) & 0xF]; + fp[i*3+1] = hex[(md[i] >> 0) & 0xF]; + fp[i*3+2] = i == n - 1 ? '\0' : ':'; + } + g_warning(" MD5 Fingerprint : %s", fp); + } + } + return FALSE; + } + return TRUE; +} + + #if GLIB_MAJOR_VERSION < 2 -#ifdef G_CAN_INLINE -G_INLINE_FUNC -#else -static -#endif -GIOError ssl_errno(gint e) +static GIOError ssl_errno(gint e) { switch(e) { @@ -72,15 +115,21 @@ GIOError ssl_errno(gint e) static GIOError irssi_ssl_cert_step(GIOSSLChannel *chan) { + X509 *cert; gint err; switch(err = SSL_do_handshake(chan->ssl)) { case 1: - if(!(chan->cert = SSL_get_peer_certificate(chan->ssl))) + if(!(cert = SSL_get_peer_certificate(chan->ssl))) { g_warning("SSL server supplied no certificate"); return G_IO_ERROR_INVAL; } + if (chan->verify && ! irssi_ssl_verify(chan->ssl, chan->ctx, cert)) { + X509_free(cert); + return G_IO_ERROR_INVAL; + } + X509_free(cert); return G_IO_ERROR_NONE; default: if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ) @@ -96,7 +145,7 @@ static GIOError irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint GIOSSLChannel *chan = (GIOSSLChannel *)handle; gint err; - if(chan->cert == NULL) + if(! chan->got_cert) { gint cert_err = irssi_ssl_cert_step(chan); if(cert_err != G_IO_ERROR_NONE) @@ -125,7 +174,7 @@ static GIOError irssi_ssl_write(GIOChannel *handle, gchar *buf, guint len, guint GIOSSLChannel *chan = (GIOSSLChannel *)handle; gint err; - if(chan->cert == NULL) + if(chan->got_cert) { gint cert_err = irssi_ssl_cert_step(chan); if(cert_err != G_IO_ERROR_NONE) @@ -185,12 +234,7 @@ static GIOFuncs irssi_ssl_channel_funcs = #else /* GLIB_MAJOR_VERSION < 2 */ -#ifdef G_CAN_INLINE -G_INLINE_FUNC -#else -static -#endif -GIOStatus ssl_errno(gint e) +static GIOStatus ssl_errno(gint e) { switch(e) { @@ -208,15 +252,21 @@ GIOStatus ssl_errno(gint e) static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan) { + X509 *cert; gint err; switch(err = SSL_do_handshake(chan->ssl)) { case 1: - if(!(chan->cert = SSL_get_peer_certificate(chan->ssl))) + 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) @@ -227,12 +277,12 @@ static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan) return G_IO_STATUS_ERROR; } -static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret, GError **gerr) +static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr) { GIOSSLChannel *chan = (GIOSSLChannel *)handle; gint err; - if(chan->cert == NULL) + if(! chan->got_cert) { gint cert_err = irssi_ssl_cert_step(chan); if(cert_err != G_IO_STATUS_NORMAL) @@ -261,7 +311,7 @@ static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len GIOSSLChannel *chan = (GIOSSLChannel *)handle; gint err; - if(chan->cert == NULL) + if(! chan->got_cert) { gint cert_err = irssi_ssl_cert_step(chan); if(cert_err != G_IO_STATUS_NORMAL) @@ -335,8 +385,6 @@ static GIOFuncs irssi_ssl_channel_funcs = { #endif -static SSL_CTX *ssl_ctx = NULL; - static gboolean irssi_ssl_init(void) { SSL_library_init(); @@ -353,13 +401,14 @@ static gboolean irssi_ssl_init(void) } -static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle) +static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycert, const char *mypkey, const char *cafile, const char *capath, gboolean verify) { GIOSSLChannel *chan; GIOChannel *gchan; int err, fd; SSL *ssl; X509 *cert = NULL; + SSL_CTX *ctx = NULL; g_return_val_if_fail(handle != NULL, NULL); @@ -369,7 +418,52 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle) if(!(fd = g_io_channel_unix_get_fd(handle))) return NULL; - if(!(ssl = SSL_new(ssl_ctx))) + if (mycert && *mycert) { + char *scert = NULL, *spkey = NULL; + if ((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { + g_error("Could not allocate memory for SSL context"); + return NULL; + } + scert = convert_home(mycert); + if (mypkey && *mypkey) + spkey = convert_home(mypkey); + if (! SSL_CTX_use_certificate_file(ctx, scert, SSL_FILETYPE_PEM)) + g_warning("Loading of client certificate '%s' failed", mycert); + else if (! SSL_CTX_use_PrivateKey_file(ctx, spkey ? spkey : scert, SSL_FILETYPE_PEM)) + g_warning("Loading of private key '%s' failed", mypkey ? mypkey : mycert); + else if (! SSL_CTX_check_private_key(ctx)) + g_warning("Private key does not match the certificate"); + g_free(scert); + g_free(spkey); + } + + if ((cafile && *cafile) || (capath && *capath)) { + char *scafile = NULL; + char *scapath = NULL; + if (! ctx && (ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { + g_error("Could not allocate memory for SSL context"); + return NULL; + } + if (cafile && *cafile) + scafile = convert_home(cafile); + if (capath && *capath) + scapath = convert_home(capath); + if (! SSL_CTX_load_verify_locations(ctx, scafile, scapath)) { + g_warning("Could not load CA list for verifying SSL server certificate"); + g_free(scafile); + g_free(scapath); + SSL_CTX_free(ctx); + return NULL; + } + g_free(scafile); + g_free(scapath); + verify = TRUE; + } + + if (ctx == NULL) + ctx = ssl_ctx; + + if(!(ssl = SSL_new(ctx))) { g_warning("Failed to allocate SSL structure"); return NULL; @@ -378,6 +472,9 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle) if(!(err = SSL_set_fd(ssl, fd))) { g_warning("Failed to associate socket to SSL stream"); + SSL_free(ssl); + if (ctx != ssl_ctx) + SSL_CTX_free(ctx); return NULL; } @@ -391,23 +488,38 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle) 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->cert = cert; - g_io_channel_ref(handle); + chan->ctx = ctx; + chan->got_cert = cert != NULL; + chan->verify = verify; gchan = (GIOChannel *)chan; gchan->funcs = &irssi_ssl_channel_funcs; @@ -416,16 +528,20 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle) return gchan; } -GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip) +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 *gret = net_connect_ip(ip, port, my_ip); - gret = irssi_ssl_get_iochannel(gret); - return gret; + GIOChannel *handle, *ssl_handle; + + handle = net_connect_ip(ip, port, my_ip); + ssl_handle = irssi_ssl_get_iochannel(handle, cert, pkey, cafile, capath, verify); + if (ssl_handle == NULL) + g_io_channel_unref(handle); + return ssl_handle; } #else /* HAVE_OPENSSL */ -GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip) +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) { g_warning("Connection failed: SSL support not enabled in this build."); errno = ENOSYS;