diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2015-04-18 18:52:20 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2015-04-18 18:52:20 +0200 |
commit | fe9a9fbfce9ba2095ead12ddb001d8bebd0bae62 (patch) | |
tree | acffb16b9916d92c9655742a7d21cbd6bcbbe6cb /src/plugins/irc | |
parent | 9598bd49197cc9699db58aeda246fbadfbe9e6a4 (diff) | |
download | weechat-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.c | 73 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.c | 143 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.h | 17 |
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 (); |