summaryrefslogtreecommitdiff
path: root/src/plugins/irc
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2015-04-18 18:52:20 +0200
committerSébastien Helleu <flashcode@flashtux.org>2015-04-18 18:52:20 +0200
commitfe9a9fbfce9ba2095ead12ddb001d8bebd0bae62 (patch)
treeacffb16b9916d92c9655742a7d21cbd6bcbbe6cb /src/plugins/irc
parent9598bd49197cc9699db58aeda246fbadfbe9e6a4 (diff)
downloadweechat-fe9a9fbfce9ba2095ead12ddb001d8bebd0bae62.zip
irc: add support of SHA-256 and SHA-512 algorithms in server option "ssl_fingerprint" (closes #281)
Diffstat (limited to 'src/plugins/irc')
-rw-r--r--src/plugins/irc/irc-config.c73
-rw-r--r--src/plugins/irc/irc-server.c143
-rw-r--r--src/plugins/irc/irc-server.h17
3 files changed, 194 insertions, 39 deletions
diff --git a/src/plugins/irc/irc-config.c b/src/plugins/irc/irc-config.c
index d3fa7e611..11f189639 100644
--- a/src/plugins/irc/irc-config.c
+++ b/src/plugins/irc/irc-config.c
@@ -23,6 +23,7 @@
#include <unistd.h>
#include <stdio.h>
#include <string.h>
+#include <ctype.h>
#include <time.h>
#include <limits.h>
#include <pwd.h>
@@ -1060,10 +1061,13 @@ irc_config_server_check_value_cb (void *data,
struct t_config_option *option,
const char *value)
{
- int i, index_option, proxy_found, rc;
+ int index_option, proxy_found;
const char *pos_error, *proxy_name;
- char **fingerprints;
struct t_infolist *infolist;
+ #ifdef HAVE_GNUTLS
+ char **fingerprints, *str_sizes;
+ int i, j, rc, algo, length;
+ #endif
/* make C compiler happy */
(void) option;
@@ -1115,33 +1119,63 @@ irc_config_server_check_value_cb (void *data,
}
break;
case IRC_SERVER_OPTION_SSL_FINGERPRINT:
- if (value && value[0] && (strlen (value) != 40))
+ #ifdef HAVE_GNUTLS
+ if (value && value[0])
{
fingerprints = weechat_string_split (value, ",", 0, 0,
NULL);
if (fingerprints)
{
- rc = 1;
+ rc = 0;
for (i = 0; fingerprints[i]; i++)
{
- if (strlen (fingerprints[i]) != 40)
+ length = strlen (fingerprints[i]);
+ algo = irc_server_fingerprint_search_algo_with_size (
+ length * 4);
+ if (algo < 0)
{
- rc = 0;
+ rc = -1;
break;
}
+ for (j = 0; j < length; j++)
+ {
+ if (!isxdigit (fingerprints[i][j]))
+ {
+ rc = -2;
+ break;
+ }
+ }
+ if (rc < 0)
+ break;
}
weechat_string_free_split (fingerprints);
- if (!rc)
+ switch (rc)
{
- weechat_printf (
- NULL,
- _("%s%s: fingerprint must have exactly 40 "
- "hexadecimal digits"),
- weechat_prefix ("error"), IRC_PLUGIN_NAME);
- return 0;
+ case -1: /* invalid size */
+ str_sizes = irc_server_fingerprint_str_sizes ();
+ weechat_printf (
+ NULL,
+ _("%s%s: invalid fingerprint size, the "
+ "number of hexadecimal digits must be "
+ "one of: %s"),
+ weechat_prefix ("error"),
+ IRC_PLUGIN_NAME,
+ (str_sizes) ? str_sizes : "?");
+ if (str_sizes)
+ free (str_sizes);
+ return 0;
+ case -2: /* invalid content */
+ weechat_printf (
+ NULL,
+ _("%s%s: invalid fingerprint, it must "
+ "contain only hexadecimal digits (0-9, "
+ "a-f)"),
+ weechat_prefix ("error"), IRC_PLUGIN_NAME);
+ return 0;
}
}
}
+ #endif
break;
}
}
@@ -1621,12 +1655,13 @@ irc_config_server_new_option (struct t_config_file *config_file,
new_option = weechat_config_new_option (
config_file, section,
option_name, "string",
- N_("SHA1 fingerprint of certificate which is trusted and "
- "accepted for the server (it must be exactly 40 hexadecimal "
- "digits without separators); many fingerprints can be "
- "separated by commas; if this option is set, the other "
- "checks on certificates are NOT performed (option "
- "\"ssl_verify\")"),
+ N_("fingerprint of certificate which is trusted and accepted "
+ "for the server; only hexadecimal digits are allowed (0-9, "
+ "a-f): 64 chars for SHA-512, 32 chars for SHA-256, "
+ "20 chars for SHA-1 (insecure, not recommended); many "
+ "fingerprints can be separated by commas; if this option "
+ "is set, the other checks on certificates are NOT "
+ "performed (option \"ssl_verify\")"),
NULL, 0, 0,
default_value, value,
null_value_allowed,
diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c
index ecf6e559d..4d639a5d9 100644
--- a/src/plugins/irc/irc-server.c
+++ b/src/plugins/irc/irc-server.c
@@ -125,6 +125,15 @@ char *irc_server_chanmodes_default = "beI,k,l";
const char *irc_server_send_default_tags = NULL; /* default tags when */
/* sending a message */
+#ifdef HAVE_GNUTLS
+gnutls_digest_algorithm_t irc_fingerprint_digest_algos[IRC_FINGERPRINT_NUM_ALGOS] =
+{ GNUTLS_DIG_SHA1, GNUTLS_DIG_SHA256, GNUTLS_DIG_SHA512 };
+char *irc_fingerprint_digest_algos_name[IRC_FINGERPRINT_NUM_ALGOS] =
+{ "SHA-1", "SHA-256", "SHA-512" };
+int irc_fingerprint_digest_algos_size[IRC_FINGERPRINT_NUM_ALGOS] =
+{ 160, 256, 512 };
+#endif
+
void irc_server_reconnect (struct t_irc_server *server);
void irc_server_free_data (struct t_irc_server *server);
@@ -3707,6 +3716,62 @@ irc_server_create_buffer (struct t_irc_server *server)
}
/*
+ * Searches for a fingerprint digest algorithm with the size (in bits).
+ *
+ * Returns index of algo in enum t_irc_fingerprint_digest_algo,
+ * -1 if not found.
+ */
+
+#ifdef HAVE_GNUTLS
+int
+irc_server_fingerprint_search_algo_with_size (int size)
+{
+ int i;
+
+ for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
+ {
+ if (irc_fingerprint_digest_algos_size[i] == size)
+ return i;
+ }
+
+ /* digest algorithm not found */
+ return -1;
+}
+#endif
+
+/*
+ * Returns a string with sizes of allowed fingerprint,
+ * in number of hexadecimal digits (== bits / 4).
+ *
+ * Example of output: "64=SHA-512, 32=SHA-256, 20=SHA-1".
+ *
+ * Note: result must be freed after use.
+ */
+
+#ifdef HAVE_GNUTLS
+char *
+irc_server_fingerprint_str_sizes ()
+{
+ char str_sizes[1024], str_one_size[128];
+ int i;
+
+ str_sizes[0] = '\0';
+
+ for (i = IRC_FINGERPRINT_NUM_ALGOS - 1; i >= 0; i--)
+ {
+ snprintf (str_one_size, sizeof (str_one_size),
+ "%d=%s%s",
+ irc_fingerprint_digest_algos_size[i] / 8,
+ irc_fingerprint_digest_algos_name[i],
+ (i > 0) ? ", " : "");
+ strcat (str_sizes, str_one_size);
+ }
+
+ return strdup (str_sizes);
+}
+#endif
+
+/*
* Compares two fingerprints: one hexadecimal (given by user), the second binary
* (received from IRC server).
*
@@ -3754,23 +3819,14 @@ irc_server_check_certificate_fingerprint (struct t_irc_server *server,
gnutls_x509_crt_t certificate,
const char *good_fingerprints)
{
- unsigned char fingerprint_server[20];
+ unsigned char *fingerprint_server[IRC_FINGERPRINT_NUM_ALGOS];
char **fingerprints;
- int i, rc;
- size_t fingerprint_size;
-
- fingerprint_size = sizeof (fingerprint_server);
+ int i, rc, algo;
+ size_t size_bits, size_bytes;
- /* calculate the SHA1 fingerprint for the certificate */
- if (gnutls_x509_crt_get_fingerprint (certificate, GNUTLS_DIG_SHA1,
- fingerprint_server,
- &fingerprint_size) != GNUTLS_E_SUCCESS)
+ for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
{
- weechat_printf (
- server->buffer,
- _("%sgnutls: failed to calculate certificate fingerprint"),
- weechat_prefix ("error"));
- return 0;
+ fingerprint_server[i] = NULL;
}
/* split good_fingerprints */
@@ -3782,18 +3838,65 @@ irc_server_check_certificate_fingerprint (struct t_irc_server *server,
for (i = 0; fingerprints[i]; i++)
{
- /* check if the fingerprint matches */
- if (irc_server_compare_fingerprints (fingerprints[i],
- fingerprint_server,
- fingerprint_size) == 0)
+ size_bits = strlen (fingerprints[i]) * 4;
+ size_bytes = size_bits / 8;
+
+ algo = irc_server_fingerprint_search_algo_with_size (size_bits);
+ if (algo < 0)
+ continue;
+
+ if (!fingerprint_server[algo])
{
- rc = 1;
- break;
+ fingerprint_server[algo] = malloc (size_bytes);
+ if (fingerprint_server[algo])
+ {
+ /* calculate the fingerprint for the certificate */
+ if (gnutls_x509_crt_get_fingerprint (
+ certificate,
+ irc_fingerprint_digest_algos[algo],
+ fingerprint_server[algo],
+ &size_bytes) != GNUTLS_E_SUCCESS)
+ {
+ weechat_printf (
+ server->buffer,
+ _("%sgnutls: failed to calculate certificate "
+ "fingerprint (%s)"),
+ weechat_prefix ("error"),
+ irc_fingerprint_digest_algos_name[algo]);
+ free (fingerprint_server[algo]);
+ fingerprint_server[algo] = NULL;
+ }
+ }
+ else
+ {
+ weechat_printf (
+ server->buffer,
+ _("%s%s: not enough memory"),
+ weechat_prefix ("error"), IRC_PLUGIN_NAME);
+ }
+ }
+
+ if (fingerprint_server[algo])
+ {
+ /* check if the fingerprint matches */
+ if (irc_server_compare_fingerprints (fingerprints[i],
+ fingerprint_server[algo],
+ size_bytes) == 0)
+ {
+ rc = 1;
+ break;
+ }
}
}
weechat_string_free_split (fingerprints);
+ for (i = 0; i < IRC_FINGERPRINT_NUM_ALGOS; i++)
+ {
+ if (fingerprint_server[i])
+ free (fingerprint_server[i]);
+ }
+
return rc;
}
#endif /* HAVE_GNUTLS */
diff --git a/src/plugins/irc/irc-server.h b/src/plugins/irc/irc-server.h
index 215dfa7df..51cb20dff 100644
--- a/src/plugins/irc/irc-server.h
+++ b/src/plugins/irc/irc-server.h
@@ -241,6 +241,19 @@ struct t_irc_message
struct t_irc_message *next_message; /* link to next message */
};
+/* digest algorithms for fingerprint */
+
+#ifdef HAVE_GNUTLS
+enum t_irc_fingerprint_digest_algo
+{
+ IRC_FINGERPRINT_ALGO_SHA1 = 0,
+ IRC_FINGERPRINT_ALGO_SHA256,
+ IRC_FINGERPRINT_ALGO_SHA512,
+ /* number of digest algorithms */
+ IRC_FINGERPRINT_NUM_ALGOS,
+};
+#endif
+
extern struct t_irc_server *irc_servers;
#ifdef HAVE_GNUTLS
extern const int gnutls_cert_type_prio[];
@@ -307,6 +320,10 @@ extern void irc_server_msgq_add_buffer (struct t_irc_server *server,
extern void irc_server_msgq_flush ();
extern void irc_server_set_buffer_title (struct t_irc_server *server);
extern struct t_gui_buffer *irc_server_create_buffer (struct t_irc_server *server);
+#ifdef HAVE_GNUTLS
+int irc_server_fingerprint_search_algo_with_size (int size);
+char *irc_server_fingerprint_str_sizes ();
+#endif
extern int irc_server_connect (struct t_irc_server *server);
extern void irc_server_auto_connect (int auto_connect);
extern void irc_server_autojoin_channels ();