diff options
Diffstat (limited to 'src/plugins/irc/irc-server.c')
-rw-r--r-- | src/plugins/irc/irc-server.c | 327 |
1 files changed, 174 insertions, 153 deletions
diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c index bc0da8153..e6dfeeb75 100644 --- a/src/plugins/irc/irc-server.c +++ b/src/plugins/irc/irc-server.c @@ -3537,13 +3537,10 @@ irc_server_create_buffer (struct t_irc_server *server) */ int -irc_server_check_certificate_fingerprint (gnutls_session_t session, - struct t_irc_server *server, +irc_server_check_certificate_fingerprint (struct t_irc_server *server, + gnutls_x509_crt_t certificate, const char *good_fingerprint) { - const gnutls_datum_t *cert_list; - unsigned int cert_list_len; - gnutls_x509_crt_t certificate; unsigned char fingerprint[20]; size_t i, fingerprint_size; unsigned int value; @@ -3554,51 +3551,18 @@ irc_server_check_certificate_fingerprint (gnutls_session_t session, if (strlen (good_fingerprint) != fingerprint_size * 2) return 0; - /* get the peer's raw certificate (chain) as sent by the peer */ - cert_list_len = 0; - cert_list = gnutls_certificate_get_peers (session, &cert_list_len); - if (!cert_list || (cert_list_len == 0)) - { - weechat_printf (server->buffer, - _("%sgnutls: no server certificate found"), - weechat_prefix ("error")); - return 0; - } - - /* initialize the certificate structure */ - if (gnutls_x509_crt_init (&certificate) != GNUTLS_E_SUCCESS) - { - weechat_printf (server->buffer, - _("%sgnutls: failed to initialize certificate structure"), - weechat_prefix ("error")); - return 0; - } - - /* import the raw certificate data */ - if (gnutls_x509_crt_import (certificate, &cert_list[0], - GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) - { - weechat_printf (server->buffer, - _("%sgnutls: failed to import server certificate"), - weechat_prefix ("error")); - gnutls_x509_crt_deinit (certificate); - return 0; - } - /* calculate the SHA1 fingerprint for the certificate */ if (gnutls_x509_crt_get_fingerprint (certificate, GNUTLS_DIG_SHA1, fingerprint, &fingerprint_size) != GNUTLS_E_SUCCESS) { weechat_printf (server->buffer, - _("%sgnutls: failed to calculate server fingerprint"), + _("%sgnutls: failed to calculate certificate " + "fingerprint"), weechat_prefix ("error")); - gnutls_x509_crt_deinit (certificate); return 0; } - gnutls_x509_crt_deinit (certificate); - /* compare the fingerprints */ for (i = 0; i < fingerprint_size; i++) { @@ -3608,6 +3572,7 @@ irc_server_check_certificate_fingerprint (gnutls_session_t session, return 0; } + /* fingerprint matches */ return 1; } @@ -3640,11 +3605,11 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session, gnutls_x509_crt_t cert_temp; const gnutls_datum_t *cert_list; gnutls_datum_t filedatum; - unsigned int cert_list_len, status; + unsigned int i, cert_list_len, status; time_t cert_time; - char *cert_path0, *cert_path1, *cert_path2, *cert_str, *hostname; + char *cert_path0, *cert_path1, *cert_path2, *cert_str; const char *weechat_dir, *fingerprint; - int rc, ret, i, j, hostname_match; + int rc, ret, fingerprint_match, hostname_match, cert_temp_init; #if LIBGNUTLS_VERSION_NUMBER >= 0x010706 gnutls_datum_t cinfo; int rinfo; @@ -3662,147 +3627,199 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session, return -1; server = (struct t_irc_server *) data; - hostname = server->current_address; - hostname_match = 0; + cert_temp_init = 0; + cert_list = NULL; + cert_list_len = 0; if (action == WEECHAT_HOOK_CONNECT_GNUTLS_CB_VERIFY_CERT) { weechat_printf (server->buffer, - _("%sgnutls: connected using %d-bit Diffie-Hellman shared " - "secret exchange"), + _("%sgnutls: connected using %d-bit Diffie-Hellman " + "shared secret exchange"), weechat_prefix ("network"), IRC_SERVER_OPTION_INTEGER (server, IRC_SERVER_OPTION_SSL_DHKEY_SIZE)); - /* skip normal checks if ssl_fingerprint is set and just check that */ - fingerprint = IRC_SERVER_OPTION_STRING (server, - IRC_SERVER_OPTION_SSL_FINGERPRINT); - if (fingerprint && fingerprint[0]) - { - if (!irc_server_check_certificate_fingerprint (tls_session, server, - fingerprint)) - { - rc = -1; - weechat_printf (server->buffer, - _("%sgnutls: server fingerprint does NOT match"), - weechat_prefix ("error")); - } - else - { - weechat_printf (server->buffer, - _("%sgnutls: server fingerprint matches"), - weechat_prefix ("network")); - } - } - else if (gnutls_certificate_verify_peers2 (tls_session, &status) < 0) + /* initialize the certificate structure */ + if (gnutls_x509_crt_init (&cert_temp) != GNUTLS_E_SUCCESS) { weechat_printf (server->buffer, - _("%sgnutls: error while checking peer's certificate"), + _("%sgnutls: failed to initialize certificate " + "structure"), weechat_prefix ("error")); rc = -1; + goto end; } - else + + /* flag to do the "deinit" (at the end of function) */ + cert_temp_init = 1; + + /* get fingerprint option in server */ + fingerprint = IRC_SERVER_OPTION_STRING (server, + IRC_SERVER_OPTION_SSL_FINGERPRINT); + + /* set match options */ + fingerprint_match = (fingerprint && fingerprint[0]) ? 0 : 1; + hostname_match = 0; + + /* get the peer's raw certificate (chain) as sent by the peer */ + cert_list = gnutls_certificate_get_peers (tls_session, &cert_list_len); + if (cert_list) { - /* some checks */ - if (status & GNUTLS_CERT_INVALID) - { - weechat_printf (server->buffer, - _("%sgnutls: peer's certificate is NOT trusted"), - weechat_prefix ("error")); - rc = -1; - } - else - { - weechat_printf (server->buffer, - _("%sgnutls: peer's certificate is trusted"), - weechat_prefix ("network")); - } - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) - { - weechat_printf (server->buffer, - _("%sgnutls: peer's certificate issuer is unknown"), - weechat_prefix ("error")); - rc = -1; - } - if (status & GNUTLS_CERT_REVOKED) - { - weechat_printf (server->buffer, - _("%sgnutls: the certificate has been revoked"), - weechat_prefix ("error")); - rc = -1; - } + weechat_printf (server->buffer, + NG_("%sgnutls: receiving %d certificate", + "%sgnutls: receiving %d certificates", + cert_list_len), + weechat_prefix ("network"), + cert_list_len); - /* check certificates */ - if (gnutls_x509_crt_init (&cert_temp) >= 0) + for (i = 0; i < cert_list_len; i++) { - cert_list = gnutls_certificate_get_peers (tls_session, &cert_list_len); - if (cert_list) + if (gnutls_x509_crt_import (cert_temp, + &cert_list[i], + GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) { weechat_printf (server->buffer, - NG_("%sgnutls: receiving %d certificate", - "%sgnutls: receiving %d certificates", - cert_list_len), - weechat_prefix ("network"), - cert_list_len); - for (i = 0, j = (int) cert_list_len; i < j; i++) + _("%sgnutls: failed to import " + "certificate[%d]"), + weechat_prefix ("error"), i + 1); + rc = -1; + goto end; + } + + /* checks on first certificate received */ + if (i == 0) + { + /* check if fingerprint matches the first certificate */ + if (fingerprint && fingerprint[0]) { - if (gnutls_x509_crt_import (cert_temp, &cert_list[i], GNUTLS_X509_FMT_DER) >= 0) - { - /* checking if hostname matches in the first certificate */ - if ((i == 0) && (gnutls_x509_crt_check_hostname (cert_temp, hostname) != 0)) - { - hostname_match = 1; - } + fingerprint_match = irc_server_check_certificate_fingerprint (server, + cert_temp, + fingerprint); + } + /* check if hostname matches in the first certificate */ + if (gnutls_x509_crt_check_hostname (cert_temp, + server->current_address) != 0) + { + hostname_match = 1; + } + } #if LIBGNUTLS_VERSION_NUMBER >= 0x010706 - /* displaying infos about certificate */ + /* display infos about certificate */ #if LIBGNUTLS_VERSION_NUMBER < 0x020400 - rinfo = gnutls_x509_crt_print (cert_temp, GNUTLS_X509_CRT_ONELINE, &cinfo); + rinfo = gnutls_x509_crt_print (cert_temp, GNUTLS_X509_CRT_ONELINE, &cinfo); #else - rinfo = gnutls_x509_crt_print (cert_temp, GNUTLS_CRT_PRINT_ONELINE, &cinfo); + rinfo = gnutls_x509_crt_print (cert_temp, GNUTLS_CRT_PRINT_ONELINE, &cinfo); #endif - if (rinfo == 0) - { - weechat_printf (server->buffer, - _("%s - certificate[%d] info:"), - weechat_prefix ("network"), - i + 1); - weechat_printf (server->buffer, - "%s - %s", - weechat_prefix ("network"), - cinfo.data); - gnutls_free (cinfo.data); - } + if (rinfo == 0) + { + weechat_printf (server->buffer, + _("%s - certificate[%d] info:"), + weechat_prefix ("network"), i + 1); + weechat_printf (server->buffer, + "%s - %s", + weechat_prefix ("network"), cinfo.data); + gnutls_free (cinfo.data); + } #endif - /* check expiration date */ - cert_time = gnutls_x509_crt_get_expiration_time (cert_temp); - if (cert_time < time (NULL)) - { - weechat_printf (server->buffer, - _("%sgnutls: certificate has expired"), - weechat_prefix ("error")); - rc = -1; - } - /* check activation date */ - cert_time = gnutls_x509_crt_get_activation_time (cert_temp); - if (cert_time > time (NULL)) - { - weechat_printf (server->buffer, - _("%sgnutls: certificate is not yet activated"), - weechat_prefix ("error")); - rc = -1; - } - } + /* check dates, only if fingerprint is not set */ + if (!fingerprint || !fingerprint[0]) + { + /* check expiration date */ + cert_time = gnutls_x509_crt_get_expiration_time (cert_temp); + if (cert_time < time (NULL)) + { + weechat_printf (server->buffer, + _("%sgnutls: certificate has expired"), + weechat_prefix ("error")); + rc = -1; } - if (hostname_match == 0) + /* check activation date */ + cert_time = gnutls_x509_crt_get_activation_time (cert_temp); + if (cert_time > time (NULL)) { weechat_printf (server->buffer, - _("%sgnutls: the hostname in the " - "certificate does NOT match \"%s\""), - weechat_prefix ("error"), hostname); + _("%sgnutls: certificate is not yet activated"), + weechat_prefix ("error")); rc = -1; } } } + + /* + * if fingerprint is set, display if matches, and don't check + * anything else + */ + if (fingerprint && fingerprint[0]) + { + if (fingerprint_match) + { + weechat_printf (server->buffer, + _("%sgnutls: fingerprint matches"), + weechat_prefix ("network")); + } + else + { + weechat_printf (server->buffer, + _("%sgnutls: fingerprint does NOT match"), + weechat_prefix ("error")); + rc = -1; + } + goto end; + } + + if (!hostname_match) + { + weechat_printf (server->buffer, + _("%sgnutls: the hostname in the " + "certificate does NOT match \"%s\""), + weechat_prefix ("error"), + server->current_address); + rc = -1; + } + } + + /* verify the peer’s certificate */ + if (gnutls_certificate_verify_peers2 (tls_session, &status) < 0) + { + weechat_printf (server->buffer, + _("%sgnutls: error while checking peer's certificate"), + weechat_prefix ("error")); + rc = -1; + goto end; + } + + /* check if certificate is trusted */ + if (status & GNUTLS_CERT_INVALID) + { + weechat_printf (server->buffer, + _("%sgnutls: peer's certificate is NOT trusted"), + weechat_prefix ("error")); + rc = -1; + } + else + { + weechat_printf (server->buffer, + _("%sgnutls: peer's certificate is trusted"), + weechat_prefix ("network")); + } + + /* check if certificate issuer is known */ + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + { + weechat_printf (server->buffer, + _("%sgnutls: peer's certificate issuer is unknown"), + weechat_prefix ("error")); + rc = -1; + } + + /* check that certificate is not revoked */ + if (status & GNUTLS_CERT_REVOKED) + { + weechat_printf (server->buffer, + _("%sgnutls: the certificate has been revoked"), + weechat_prefix ("error")); + rc = -1; } } else if (action == WEECHAT_HOOK_CONNECT_GNUTLS_CB_SET_CERT) @@ -3911,6 +3928,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) && (IRC_SERVER_OPTION_BOOLEAN(server, IRC_SERVER_OPTION_SSL_VERIFY) == 0)) @@ -3918,6 +3936,9 @@ irc_server_gnutls_callback (void *data, gnutls_session_t tls_session, rc = 0; } + if (cert_temp_init) + gnutls_x509_crt_deinit (cert_temp); + return rc; } #endif |