Permalink
Browse files

irc: implement support for DANE

Use gnutls-dane to check DANE TLSA records on SSL connections.

Performs a check of the cached TLSA data when verifying the
certificate.
  • Loading branch information...
1 parent 6d69397 commit e9d033abd9669315b73e3f8b707a592964ae932c @lp0 committed Jul 6, 2014
Showing with 136 additions and 5 deletions.
  1. +9 −0 src/core/wee-hook.c
  2. +5 −0 src/core/wee-hook.h
  3. +4 −0 src/core/wee-network.c
  4. +2 −2 src/plugins/irc/Makefile.am
  5. +116 −3 src/plugins/irc/irc-server.c
View
@@ -2040,6 +2040,12 @@ hook_connect_gnutls_verify_certificates (gnutls_session_t tls_session)
rc = (int) (HOOK_CONNECT(ptr_hook, gnutls_cb))
(ptr_hook->callback_data, tls_session, NULL, 0,
NULL, 0, NULL,
+#ifdef HAVE_GNUTLS_DANE
+ HOOK_CONNECT(ptr_hook, dane_data),
+ HOOK_CONNECT(ptr_hook, dane_data_len),
+ HOOK_CONNECT(ptr_hook, dane_secure),
+ HOOK_CONNECT(ptr_hook, dane_bogus),
+#endif
WEECHAT_HOOK_CONNECT_GNUTLS_CB_VERIFY_CERT);
break;
}
@@ -2081,6 +2087,9 @@ hook_connect_gnutls_set_certificates (gnutls_session_t tls_session,
rc = (int) (HOOK_CONNECT(ptr_hook, gnutls_cb))
(ptr_hook->callback_data, tls_session, req_ca, nreq,
pk_algos, pk_algos_len, answer,
+#ifdef HAVE_GNUTLS_DANE
+ NULL, NULL, 0, 0,
+#endif
WEECHAT_HOOK_CONNECT_GNUTLS_CB_SET_CERT);
break;
}
View
@@ -242,6 +242,11 @@ typedef int (gnutls_callback_t)(void *data, gnutls_session_t tls_session,
#else
gnutls_retr_st *answer,
#endif
+#ifdef HAVE_GNUTLS_DANE
+ char * const*dane_data,
+ const int *dane_data_len,
+ int dane_secure, int dane_bogus,
+#endif
int action);
#endif
View
@@ -150,6 +150,8 @@ network_init_gnutls ()
network_init_gnutls_ok = 1;
}
+#ifdef HAVE_GNUTLS
+# ifdef HAVE_GNUTLS_DANE
/*
* Implementation of dane_query_to_raw_tlsa helper function.
*/
@@ -222,6 +224,8 @@ weechat__dane_query_to_raw_tlsa(dane_query_t q, unsigned int *dane_entries,
return DANE_E_SUCCESS;
}
+# endif
+#endif
/*
* Ends network.
@@ -17,7 +17,7 @@
# along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
#
-AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(GCRYPT_CFLAGS) $(GNUTLS_CFLAGS)
+AM_CPPFLAGS = -DLOCALEDIR=\"$(datadir)/locale\" $(GCRYPT_CFLAGS) $(GNUTLS_CFLAGS) $(GNUTLS_DANE_CFLAGS)
libdir = ${weechat_libdir}/plugins
@@ -73,6 +73,6 @@ irc_la_SOURCES = irc.c \
irc-upgrade.h
irc_la_LDFLAGS = -module -no-undefined
-irc_la_LIBADD = $(IRC_LFLAGS) $(GCRYPT_LFLAGS) $(GNUTLS_LFLAGS)
+irc_la_LIBADD = $(IRC_LFLAGS) $(GCRYPT_LFLAGS) $(GNUTLS_LFLAGS) $(GNUTLS_DANE_LFLAGS)
EXTRA_DIST = CMakeLists.txt
@@ -44,6 +44,9 @@
#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
+# ifdef HAVE_GNUTLS_DANE
+# include <gnutls/dane.h>
+# endif
#endif
#include "../weechat-plugin.h"
@@ -3670,6 +3673,11 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
#else
gnutls_retr_st *answer,
#endif
+#ifdef HAVE_GNUTLS_DANE
+ char * const*dane_data,
+ const int *dane_data_len,
+ int dane_secure, int dane_bogus,
+#endif
int action)
{
struct t_irc_server *server;
@@ -3690,6 +3698,7 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
gnutls_datum_t cinfo;
int rinfo;
#endif
+ int using_dane = 0;
/* make C compiler happy */
(void) req_ca;
@@ -3716,6 +3725,89 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
IRC_SERVER_OPTION_INTEGER (server,
IRC_SERVER_OPTION_SSL_DHKEY_SIZE));
+#ifdef HAVE_GNUTLS_DANE
+ if (dane_data != NULL)
+ {
+ dane_state_t dane_s;
+ dane_query_t dane_r;
+ unsigned int dane_status = 0;
+
+ ret = dane_state_init (&dane_s, 0);
+ if (ret == DANE_E_SUCCESS)
+ {
+ ret = dane_raw_tlsa (dane_s, &dane_r, dane_data,
+ dane_data_len,
+ dane_secure,
+ dane_bogus);
+ if (ret == DANE_E_SUCCESS)
+ {
+ const gnutls_datum_t *chain;
+ unsigned int chain_size = 0;
+ unsigned int chain_type;
+
+ chain = gnutls_certificate_get_peers(tls_session, &chain_size);
+ chain_type = gnutls_certificate_type_get(tls_session);
+
+ if (chain_size > 0)
+ {
+ ret = dane_verify_crt_raw (dane_s, chain, chain_size, chain_type,
+ dane_r, 0, DANE_VFLAG_FAIL_IF_NOT_CHECKED,
+ &dane_status);
+ }
+ else
+ {
+ ret = DANE_E_NO_CERT;
+ }
+
+ if (ret == DANE_E_SUCCESS)
+ {
+ using_dane = 1;
+
+ if (dane_status == 0)
+ {
+ weechat_printf (server->buffer,
+ _("%sgnutls: hostname %s port %d verified using DANE"),
+ weechat_prefix ("network"), server->current_address,
+ server->current_port);
+ }
+ else
+ {
+ weechat_printf (server->buffer,
+ _("%sgnutls: hostname %s port %d rejected by DANE"),
+ weechat_prefix ("error"), server->current_address,
+ server->current_port);
+ rc = -1;
+ }
+ }
+ else
+ {
+ weechat_printf (server->buffer,
+ _("%sgnutls: hostname %s port %d could not be verified with DANE: %s"),
+ weechat_prefix ("network"), server->current_address,
+ server->current_port, dane_strerror(ret));
+ }
+
+ dane_query_deinit(dane_r);
+ }
+
+ dane_state_deinit(dane_s);
+ }
+ else
+ {
+ weechat_printf (server->buffer,
+ _("%sgnutls: hostname %s port %d could not be verified with DANE: %s"),
+ weechat_prefix ("network"), server->current_address,
+ server->current_port, dane_strerror(ret));
+ }
+ }
+ else
+ {
+ weechat_printf (server->buffer,
+ _("%sgnutls: hostname %s port %d could not be verified with DANE"),
+ weechat_prefix ("network"), server->current_address, server->current_port);
+ }
+#endif
+
/* initialize the certificate structure */
if (gnutls_x509_crt_init (&cert_temp) != GNUTLS_E_SUCCESS)
{
@@ -3798,8 +3890,8 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
gnutls_free (cinfo.data);
}
#endif
- /* check dates, only if fingerprint is not set */
- if (!fingerprint || !fingerprint[0])
+ /* check dates, only if fingerprint is not set or when using DANE */
+ if (using_dane || !fingerprint || !fingerprint[0])
{
/* check expiration date */
cert_time = gnutls_x509_crt_get_expiration_time (cert_temp);
@@ -3834,6 +3926,12 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
_("%sgnutls: certificate fingerprint "
"matches"),
weechat_prefix ("network"));
+
+ if (!using_dane)
+ {
+ /* fingerprint check must not bypass DANE */
+ goto end;
+ }
}
else
{
@@ -3843,8 +3941,8 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
"irc.server.%s.ssl_fingerprint)"),
weechat_prefix ("error"), server->name);
rc = -1;
+ goto end;
}
- goto end;
}
if (!hostname_match)
@@ -3856,6 +3954,20 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
server->current_address);
rc = -1;
}
+
+ if (using_dane)
+ {
+ /* no more checks required */
+ goto end;
+ }
+ }
+ else if (using_dane)
+ {
+ weechat_printf (server->buffer,
+ _("%sgnutls: error while checking peer's certificate"),
+ weechat_prefix ("error"));
+ rc = -1;
+ goto end;
}
/* verify the peer’s certificate */
@@ -4011,6 +4123,7 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session,
end:
/* an error should stop the handshake unless the user doesn't care */
if ((rc == -1)
+ && !using_dane
&& (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL_VERIFY) == 0))
{
rc = 0;

0 comments on commit e9d033a

Please sign in to comment.