2 network-ssl.c : SSL support
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <openssl/crypto.h>
28 #include <openssl/x509.h>
29 #include <openssl/pem.h>
30 #include <openssl/ssl.h>
31 #include <openssl/err.h>
33 /* ssl i/o channel object */
41 unsigned int got_cert:1;
42 unsigned int verify:1;
45 static SSL_CTX *ssl_ctx = NULL;
47 static void irssi_ssl_free(GIOChannel *handle)
49 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
50 g_io_channel_unref(chan->giochan);
52 if (chan->ctx != ssl_ctx)
53 SSL_CTX_free(chan->ctx);
57 static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, X509 *cert)
59 if (SSL_get_verify_result(ssl) != X509_V_OK) {
60 unsigned char md[EVP_MAX_MD_SIZE];
64 g_warning("Could not verify SSL servers certificate:");
65 if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
66 g_warning(" Could not get subject-name from peer certificate");
68 g_warning(" Subject : %s", str);
71 if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL)
72 g_warning(" Could not get issuer-name from peer certificate");
74 g_warning(" Issuer : %s", str);
77 if (! X509_digest(cert, EVP_md5(), md, &n))
78 g_warning(" Could not get fingerprint from peer certificate");
80 char hex[] = "0123456789ABCDEF";
81 char fp[EVP_MAX_MD_SIZE*3];
84 for (i = 0; i < n; i++) {
85 fp[i*3+0] = hex[(md[i] >> 4) & 0xF];
86 fp[i*3+1] = hex[(md[i] >> 0) & 0xF];
87 fp[i*3+2] = i == n - 1 ? '\0' : ':';
89 g_warning(" MD5 Fingerprint : %s", fp);
98 #if GLIB_MAJOR_VERSION < 2
100 static GIOError ssl_errno(gint e)
105 return G_IO_ERROR_INVAL;
108 return G_IO_ERROR_AGAIN;
110 return G_IO_ERROR_INVAL;
113 return G_IO_ERROR_INVAL;
116 static GIOError irssi_ssl_cert_step(GIOSSLChannel *chan)
120 switch(err = SSL_do_handshake(chan->ssl))
123 if(!(cert = SSL_get_peer_certificate(chan->ssl)))
125 g_warning("SSL server supplied no certificate");
126 return G_IO_ERROR_INVAL;
128 if (chan->verify && ! irssi_ssl_verify(chan->ssl, chan->ctx, cert)) {
130 return G_IO_ERROR_INVAL;
133 return G_IO_ERROR_NONE;
135 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
136 return G_IO_ERROR_AGAIN;
137 return ssl_errno(errno);
140 return G_IO_ERROR_INVAL;
143 static GIOError irssi_ssl_read(GIOChannel *handle, gchar *buf, guint len, guint *ret)
145 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
150 gint cert_err = irssi_ssl_cert_step(chan);
151 if(cert_err != G_IO_ERROR_NONE)
155 err = SSL_read(chan->ssl, buf, len);
159 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
160 return G_IO_ERROR_AGAIN;
161 return ssl_errno(errno);
166 return G_IO_ERROR_NONE;
172 static GIOError irssi_ssl_write(GIOChannel *handle, gchar *buf, guint len, guint *ret)
174 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
179 gint cert_err = irssi_ssl_cert_step(chan);
180 if(cert_err != G_IO_ERROR_NONE)
185 err = SSL_write(chan->ssl, (const char *)buf, len);
189 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
190 return G_IO_ERROR_AGAIN;
191 return ssl_errno(errno);
196 return G_IO_ERROR_NONE;
199 return G_IO_ERROR_INVAL;
202 static GIOError irssi_ssl_seek(GIOChannel *handle, gint offset, GSeekType type)
204 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
206 e = g_io_channel_seek(chan->giochan, offset, type);
207 return (e == G_IO_ERROR_NONE) ? G_IO_ERROR_NONE : G_IO_ERROR_INVAL;
210 static void irssi_ssl_close(GIOChannel *handle)
212 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
213 g_io_channel_close(chan->giochan);
216 static guint irssi_ssl_create_watch(GIOChannel *handle, gint priority, GIOCondition cond,
217 GIOFunc func, gpointer data, GDestroyNotify notify)
219 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
221 return chan->giochan->funcs->io_add_watch(handle, priority, cond, func, data, notify);
224 /* ssl function pointers */
225 static GIOFuncs irssi_ssl_channel_funcs =
231 irssi_ssl_create_watch,
235 #else /* GLIB_MAJOR_VERSION < 2 */
237 static GIOStatus ssl_errno(gint e)
242 return G_IO_STATUS_ERROR;
245 return G_IO_STATUS_AGAIN;
247 return G_IO_STATUS_ERROR;
250 return G_IO_STATUS_ERROR;
253 static GIOStatus irssi_ssl_cert_step(GIOSSLChannel *chan)
257 switch(err = SSL_do_handshake(chan->ssl))
260 if(!(cert = SSL_get_peer_certificate(chan->ssl)))
262 g_warning("SSL server supplied no certificate");
263 return G_IO_STATUS_ERROR;
265 if (chan->verify && ! irssi_ssl_verify(chan->ssl, chan->ctx, cert)) {
267 return G_IO_STATUS_ERROR;
270 return G_IO_STATUS_NORMAL;
272 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
273 return G_IO_STATUS_AGAIN;
274 return ssl_errno(errno);
277 return G_IO_STATUS_ERROR;
280 static GIOStatus irssi_ssl_read(GIOChannel *handle, gchar *buf, gsize len, gsize *ret, GError **gerr)
282 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
287 gint cert_err = irssi_ssl_cert_step(chan);
288 if(cert_err != G_IO_STATUS_NORMAL)
292 err = SSL_read(chan->ssl, buf, len);
296 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
297 return G_IO_STATUS_AGAIN;
298 return ssl_errno(errno);
303 return G_IO_STATUS_NORMAL;
306 return G_IO_STATUS_ERROR;
309 static GIOStatus irssi_ssl_write(GIOChannel *handle, const gchar *buf, gsize len, gsize *ret, GError **gerr)
311 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
316 gint cert_err = irssi_ssl_cert_step(chan);
317 if(cert_err != G_IO_STATUS_NORMAL)
321 err = SSL_write(chan->ssl, (const char *)buf, len);
325 if(SSL_get_error(chan->ssl, err) == SSL_ERROR_WANT_READ)
326 return G_IO_STATUS_AGAIN;
327 return ssl_errno(errno);
332 return G_IO_STATUS_NORMAL;
335 return G_IO_STATUS_ERROR;
338 static GIOStatus irssi_ssl_seek(GIOChannel *handle, gint64 offset, GSeekType type, GError **gerr)
340 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
342 e = g_io_channel_seek(chan->giochan, offset, type);
343 return (e == G_IO_ERROR_NONE) ? G_IO_STATUS_NORMAL : G_IO_STATUS_ERROR;
346 static GIOStatus irssi_ssl_close(GIOChannel *handle, GError **gerr)
348 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
349 g_io_channel_close(chan->giochan);
351 return G_IO_STATUS_NORMAL;
354 static GSource *irssi_ssl_create_watch(GIOChannel *handle, GIOCondition cond)
356 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
358 return chan->giochan->funcs->io_create_watch(handle, cond);
361 static GIOStatus irssi_ssl_set_flags(GIOChannel *handle, GIOFlags flags, GError **gerr)
363 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
365 return chan->giochan->funcs->io_set_flags(handle, flags, gerr);
368 static GIOFlags irssi_ssl_get_flags(GIOChannel *handle)
370 GIOSSLChannel *chan = (GIOSSLChannel *)handle;
372 return chan->giochan->funcs->io_get_flags(handle);
375 static GIOFuncs irssi_ssl_channel_funcs = {
380 irssi_ssl_create_watch,
388 static gboolean irssi_ssl_init(void)
391 SSL_load_error_strings();
393 ssl_ctx = SSL_CTX_new(SSLv23_client_method());
396 g_error("Initialization of the SSL library failed");
404 static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycert, const char *mypkey, const char *cafile, const char *capath, gboolean verify)
413 g_return_val_if_fail(handle != NULL, NULL);
415 if(!ssl_ctx && !irssi_ssl_init())
418 if(!(fd = g_io_channel_unix_get_fd(handle)))
421 if (mycert && *mycert) {
422 char *scert = NULL, *spkey = NULL;
423 if ((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
424 g_error("Could not allocate memory for SSL context");
427 scert = convert_home(mycert);
428 if (mypkey && *mypkey)
429 spkey = convert_home(mypkey);
430 if (! SSL_CTX_use_certificate_file(ctx, scert, SSL_FILETYPE_PEM))
431 g_warning("Loading of client certificate '%s' failed", mycert);
432 else if (! SSL_CTX_use_PrivateKey_file(ctx, spkey ? spkey : scert, SSL_FILETYPE_PEM))
433 g_warning("Loading of private key '%s' failed", mypkey ? mypkey : mycert);
434 else if (! SSL_CTX_check_private_key(ctx))
435 g_warning("Private key does not match the certificate");
440 if ((cafile && *cafile) || (capath && *capath)) {
441 char *scafile = NULL;
442 char *scapath = NULL;
443 if (! ctx && (ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
444 g_error("Could not allocate memory for SSL context");
447 if (cafile && *cafile)
448 scafile = convert_home(cafile);
449 if (capath && *capath)
450 scapath = convert_home(capath);
451 if (! SSL_CTX_load_verify_locations(ctx, scafile, scapath)) {
452 g_warning("Could not load CA list for verifying SSL server certificate");
466 if(!(ssl = SSL_new(ctx)))
468 g_warning("Failed to allocate SSL structure");
472 if(!(err = SSL_set_fd(ssl, fd)))
474 g_warning("Failed to associate socket to SSL stream");
481 if((err = SSL_connect(ssl)) <= 0)
483 switch(err = SSL_get_error(ssl, err))
485 case SSL_ERROR_SYSCALL:
486 if(errno == EINTR || errno == EAGAIN)
487 case SSL_ERROR_WANT_READ:
488 case SSL_ERROR_WANT_WRITE:
497 else if(!(cert = SSL_get_peer_certificate(ssl)))
499 g_warning("SSL server supplied no certificate");
507 if (verify && ! irssi_ssl_verify(ssl, ctx, cert)) {
516 chan = g_new0(GIOSSLChannel, 1);
518 chan->giochan = handle;
521 chan->got_cert = cert != NULL;
522 chan->verify = verify;
524 gchan = (GIOChannel *)chan;
525 gchan->funcs = &irssi_ssl_channel_funcs;
526 g_io_channel_init(gchan);
531 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)
533 GIOChannel *handle, *ssl_handle;
535 handle = net_connect_ip(ip, port, my_ip);
536 ssl_handle = irssi_ssl_get_iochannel(handle, cert, pkey, cafile, capath, verify);
537 if (ssl_handle == NULL)
538 g_io_channel_unref(handle);
542 #else /* HAVE_OPENSSL */
544 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)
546 g_warning("Connection failed: SSL support not enabled in this build.");
551 #endif /* ! HAVE_OPENSSL */