Add pinned TLS fingerprint support.

This patch adds support for pinning TLS fingerprints. Setting a pinned
fingerprint will override all other TLS verification and will thus be
the only variable that will be looked at when connecting to a TLS
enabled IRC server.
This commit is contained in:
Alexander Færøy 2015-09-19 01:26:09 +02:00
commit 78f007d493
No known key found for this signature in database
GPG key ID: E15081D5D3C3DB53
31 changed files with 1114 additions and 357 deletions

View file

@ -24,6 +24,7 @@ libfe_common_core_a_SOURCES = \
fe-queries.c \
fe-server.c \
fe-settings.c \
fe-tls.c \
formats.c \
hilight-text.c \
keyboard.c \
@ -48,6 +49,7 @@ pkginc_fe_common_core_HEADERS = \
fe-exec.h \
fe-messages.h \
fe-queries.h \
fe-tls.h \
formats.h \
hilight-text.h \
keyboard.h \

View file

@ -88,6 +88,9 @@ void fe_server_deinit(void);
void fe_settings_init(void);
void fe_settings_deinit(void);
void fe_tls_init(void);
void fe_tls_deinit(void);
void window_commands_init(void);
void window_commands_deinit(void);
@ -161,21 +164,24 @@ void fe_common_core_init(void)
g_get_charset(&str);
settings_add_str("lookandfeel", "term_charset", str);
themes_init();
theme_register(fecommon_core_formats);
theme_register(fecommon_core_formats);
command_history_init();
completion_init();
keyboard_init();
printtext_init();
formats_init();
fe_exec_init();
fe_expandos_init();
#ifndef WIN32
fe_exec_init();
#endif
fe_expandos_init();
fe_help_init();
fe_ignore_init();
fe_log_init();
fe_modules_init();
fe_server_init();
fe_settings_init();
fe_tls_init();
windows_init();
window_activity_init();
window_commands_init();
@ -183,8 +189,8 @@ void fe_common_core_init(void)
windows_layout_init();
fe_core_commands_init();
fe_channels_init();
fe_queries_init();
fe_channels_init();
fe_queries_init();
fe_messages_init();
hilight_text_init();
@ -193,10 +199,10 @@ void fe_common_core_init(void)
settings_check();
signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created);
signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
module_register("core", "fe");
}
@ -209,14 +215,17 @@ void fe_common_core_deinit(void)
keyboard_deinit();
printtext_deinit();
formats_deinit();
fe_exec_deinit();
fe_expandos_deinit();
#ifndef WIN32
fe_exec_deinit();
#endif
fe_expandos_deinit();
fe_help_deinit();
fe_ignore_deinit();
fe_log_deinit();
fe_modules_deinit();
fe_server_deinit();
fe_settings_deinit();
fe_tls_deinit();
windows_deinit();
window_activity_deinit();
window_commands_deinit();
@ -224,21 +233,21 @@ void fe_common_core_deinit(void)
windows_layout_deinit();
fe_core_commands_deinit();
fe_channels_deinit();
fe_queries_deinit();
fe_channels_deinit();
fe_queries_deinit();
fe_messages_deinit();
fe_ignore_messages_deinit();
fe_recode_deinit();
theme_unregister();
theme_unregister();
themes_deinit();
signal_remove("setup changed", (SIGNAL_FUNC) sig_setup_changed);
signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_remove("setup changed", (SIGNAL_FUNC) sig_setup_changed);
signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
}
void glog_func(const char *log_domain, GLogLevelFlags log_level,

View file

@ -154,42 +154,66 @@ static void cmd_server_add_modify(const char *data, gboolean add)
else if (g_hash_table_lookup(optlist, "4"))
rec->family = AF_INET;
if (g_hash_table_lookup(optlist, "ssl"))
rec->use_ssl = TRUE;
if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl"))
rec->use_tls = TRUE;
value = g_hash_table_lookup(optlist, "ssl_cert");
value = g_hash_table_lookup(optlist, "tls_cert");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_cert");
if (value != NULL && *value != '\0')
rec->ssl_cert = g_strdup(value);
rec->tls_cert = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_pkey");
value = g_hash_table_lookup(optlist, "tls_pkey");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pkey");
if (value != NULL && *value != '\0')
rec->ssl_pkey = g_strdup(value);
rec->tls_pkey = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_pass");
value = g_hash_table_lookup(optlist, "tls_pass");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pass");
if (value != NULL && *value != '\0')
rec->ssl_pass = g_strdup(value);
rec->tls_pass = g_strdup(value);
if (g_hash_table_lookup(optlist, "ssl_verify"))
rec->ssl_verify = TRUE;
if (g_hash_table_lookup(optlist, "tls_verify") || g_hash_table_lookup(optlist, "ssl_verify"))
rec->tls_verify = TRUE;
value = g_hash_table_lookup(optlist, "ssl_cafile");
value = g_hash_table_lookup(optlist, "tls_cafile");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_cafile");
if (value != NULL && *value != '\0')
rec->ssl_cafile = g_strdup(value);
rec->tls_cafile = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_capath");
value = g_hash_table_lookup(optlist, "tls_capath");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_capath");
if (value != NULL && *value != '\0')
rec->ssl_capath = g_strdup(value);
rec->tls_capath = g_strdup(value);
value = g_hash_table_lookup(optlist, "ssl_ciphers");
value = g_hash_table_lookup(optlist, "tls_ciphers");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_ciphers");
if (value != NULL && *value != '\0')
rec->ssl_ciphers = g_strdup(value);
rec->tls_ciphers = g_strdup(value);
if ((rec->ssl_cafile != NULL && rec->ssl_cafile[0] != '\0')
|| (rec->ssl_capath != NULL && rec->ssl_capath[0] != '\0'))
rec->ssl_verify = TRUE;
value = g_hash_table_lookup(optlist, "tls_pinned_cert");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pinned_cert");
if (value != NULL && *value != '\0')
rec->tls_pinned_cert = g_strdup(value);
if ((rec->ssl_cert != NULL && rec->ssl_cert[0] != '\0') || rec->ssl_verify == TRUE)
rec->use_ssl = TRUE;
value = g_hash_table_lookup(optlist, "tls_pinned_pubkey");
if (value == NULL)
value = g_hash_table_lookup(optlist, "ssl_pinned_pubkey");
if (value != NULL && *value != '\0')
rec->tls_pinned_pubkey = g_strdup(value);
if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0')
|| (rec->tls_capath != NULL && rec->tls_capath[0] != '\0'))
rec->tls_verify = TRUE;
if ((rec->tls_cert != NULL && rec->tls_cert[0] != '\0') || rec->tls_verify == TRUE)
rec->use_tls = TRUE;
if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE;
if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE;
@ -331,8 +355,7 @@ static void sig_server_connected(SERVER_REC *server)
{
g_return_if_fail(server != NULL);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE,
TXT_CONNECTION_ESTABLISHED, server->connrec->address);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CONNECTION_ESTABLISHED, server->connrec->address);
}
static void sig_connect_failed(SERVER_REC *server, gchar *msg)
@ -409,8 +432,9 @@ void fe_server_init(void)
command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove);
command_bind_first("server", NULL, (SIGNAL_FUNC) server_command);
command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command);
command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/
#include "module.h"
#include "signals.h"
#include "settings.h"
#include "levels.h"
#include "tls.h"
#include "module-formats.h"
#include "printtext.h"
#include "fe-tls.h"
static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls)
{
if (! settings_get_bool("tls_verbose_connect"))
return;
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER);
GSList *certs = NULL;
for (certs = tls->certs; certs != NULL; certs = certs->next) {
TLS_CERT_REC *tls_cert_rec = certs->data;
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT_HEADER);
GSList *subject = NULL;
for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) {
TLS_CERT_ENTRY_REC *subject_data = subject->data;
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, subject_data->name, subject_data->value);
}
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER_HEADER);
GSList *issuer = NULL;
for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) {
TLS_CERT_ENTRY_REC *issuer_data = issuer->data;
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, issuer_data->name, issuer_data->value);
}
}
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher);
#ifdef SSL_get_server_tmp_key
if (tls->ephemeral_key_algorithm != NULL)
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY, tls->ephemeral_key_size, tls->ephemeral_key_algorithm);
else
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE);
#endif
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBLIC_KEY, tls->public_key_size, tls->public_key_algorithm, tls->not_before, tls->not_after);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBLIC_KEY_FINGERPRINT, tls->public_key_fingerprint, tls->public_key_fingerprint_algorithm);
printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERTIFICATE_FINGERPRINT, tls->certificate_fingerprint, tls->certificate_fingerprint_algorithm);
}
void fe_tls_init(void)
{
settings_add_bool("lookandfeel", "tls_verbose_connect", TRUE);
signal_add("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
}
void fe_tls_deinit(void)
{
signal_remove("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished);
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
*/
#ifndef __FE_TLS_H
#define __FE_TLS_H
void fe_tls_init(void);
void fe_tls_deinit(void);
#endif

View file

@ -291,5 +291,19 @@ FORMAT_REC fecommon_core_formats[] = {
{ "completion_line", "%#$[10]0 $[!40]1 $2", 3, { 0, 0, 0 } },
{ "completion_footer", "", 0 },
/* ---- */
{ NULL, "TLS", 0 },
{ "tls_ephemeral_key", "EDH Key: {hilight $0} bit {hilight $1}", 2, { 1, 0 } },
{ "tls_ephemeral_key_unavailable", "EDH Key: {error N/A}", 0 },
{ "tls_public_key", "Public Key: {hilight $0} bit {hilight $1}, valid from {hilight $2} to {hilight $3}", 4, { 1, 0, 0, 0 } },
{ "tls_cert_header", "Certificate Chain:", 0 },
{ "tls_cert_subject_header", " Subject:", 0 },
{ "tls_cert_issuer_header", " Issuer:", 0 },
{ "tls_cert_named_entry", " $[-2]0: {hilight $1}", 2, { 0, 0 } },
{ "tls_public_key_fingerprint", "Public Key Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
{ "tls_certificate_fingerprint", "Certificate Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } },
{ "tls_protocol_version", "Protocol: {hilight $0} ({hilight $1} bit, {hilight $2})", 3, { 0, 1, 0 } },
{ NULL, NULL, 0 }
};

View file

@ -48,8 +48,8 @@ enum {
TXT_LOOKING_UP,
TXT_CONNECTING,
TXT_RECONNECTING,
TXT_CONNECTION_ESTABLISHED,
TXT_RECONNECTING,
TXT_CONNECTION_ESTABLISHED,
TXT_CANT_CONNECT,
TXT_CONNECTION_LOST,
TXT_LAG_DISCONNECTED,
@ -254,7 +254,20 @@ enum {
TXT_COMPLETION_REMOVED,
TXT_COMPLETION_HEADER,
TXT_COMPLETION_LINE,
TXT_COMPLETION_FOOTER
TXT_COMPLETION_FOOTER,
TLS_FILL_15,
TXT_TLS_EPHEMERAL_KEY,
TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE,
TXT_TLS_PUBLIC_KEY,
TXT_TLS_CERT_HEADER,
TXT_TLS_CERT_SUBJECT_HEADER,
TXT_TLS_CERT_ISSUER_HEADER,
TXT_TLS_CERT_NAMED_ENTRY,
TXT_TLS_PUBLIC_KEY_FINGERPRINT,
TXT_TLS_CERTIFICATE_FINGERPRINT,
TXT_TLS_PROTOCOL_VERSION
};
extern FORMAT_REC fecommon_core_formats[];