# HG changeset patch # User William Ehlhardt # Date 1180471806 0 # Node ID e0eb1eb5b47bce008056c6dbfec357d2f884690a # Parent 8b322c8afeb68ec9c662c82d85c9510c3a90806d - Added PURPLE_SSL_PEER_AUTH_FAILED to show that an SSL connection was unable to authenticate the other end of the connection. - Wrote (non-working) GnuTLS request code to prompt the user to check the cert diff -r 8b322c8afeb6 -r e0eb1eb5b47b libpurple/plugins/ssl/ssl-gnutls.c --- a/libpurple/plugins/ssl/ssl-gnutls.c Tue May 29 16:20:39 2007 +0000 +++ b/libpurple/plugins/ssl/ssl-gnutls.c Tue May 29 20:50:06 2007 +0000 @@ -22,6 +22,7 @@ #include "internal.h" #include "debug.h" #include "plugin.h" +#include "request.h" #include "sslconn.h" #include "version.h" #include "util.h" @@ -49,8 +50,8 @@ gnutls_global_init(); gnutls_certificate_allocate_credentials(&xcred); - gnutls_certificate_set_x509_trust_file(xcred, "ca.pem", - GNUTLS_X509_FMT_PEM); + /*gnutls_certificate_set_x509_trust_file(xcred, "ca.pem", + GNUTLS_X509_FMT_PEM);*/ } static gboolean @@ -67,6 +68,163 @@ gnutls_certificate_free_credentials(xcred); } +/** Callback from the dialog in ssl_gnutls_authcheck_ask */ +static void ssl_gnutls_authcheck_cb(PurpleSslConnection * gsc, gint choice) +{ + if (NULL == gsc) + { + purple_debug_error("gnutls","Inappropriate NULL argument at %s:%d\n", + __FILE__, (int) __LINE__); + return; + } + + switch(choice) + { + case 1: /* "Accept" */ + /* TODO: Shoud PURPLE_INPUT_READ be hardcoded? */ + gsc->connect_cb(gsc->connect_cb_data, gsc, PURPLE_INPUT_READ); + break; + + default: /* "Cancel" or otherwise...? */ + purple_debug_info("gnutls", + "User rejected certificate from %s\n", + gsc->host); + if(gsc->error_cb != NULL) + gsc->error_cb(gsc, PURPLE_SSL_PEER_AUTH_FAILED, + gsc->connect_cb_data); + purple_ssl_close(gsc); + } +} + +/** Pop up a dialog asking for verification of the given certificate */ +static void ssl_gnutls_authcheck_ask(PurpleSslConnection * gsc) +{ + PurpleSslGnutlsData *gnutls_data = PURPLE_SSL_GNUTLS_DATA(gsc); + + const gnutls_datum_t *cert_list; + unsigned int cert_list_size = 0; + gnutls_session_t session=gnutls_data->session; + + cert_list = + gnutls_certificate_get_peers(session, &cert_list_size); + + if (0 == cert_list_size || NULL == cert_list) + { + /* Peer provided no certificates at all. + TODO: We should write a witty message here. + */ + gchar * primary = g_strdup_printf + ( + _("Peer %s provided no certificates.\n Connect anyway?"), + gsc->host + ); + + purple_request_accept_cancel + (gsc, + _("SSL Authorization Request"), + primary, + _("The server you are connecting to presented no certificates identifying itself. You have no assurance that you are not connecting to an imposter. Connect anyway?"), + 2, /* Default action is "Cancel" */ + NULL, NULL, /* There is no way to extract account data from + a connection handle, it seems. */ + NULL, /* Same goes for the conversation data */ + gsc, /* Pass connection ptr to callback */ + ssl_gnutls_authcheck_cb, /* Accept */ + ssl_gnutls_authcheck_cb /* Cancel */ + ); + g_free(primary); + } + else + { + /* Grab the first certificate and display some data about it */ + gchar fpr_bin[256]; /* Raw binary key fingerprint */ + gsize fpr_bin_sz = sizeof(fpr_bin); /* Size of above (used later) */ + gchar * fpr_asc = NULL; /* ASCII representation of key fingerprint */ + gchar ser_bin[256]; /* Certificate Serial Number field */ + gsize ser_bin_sz = sizeof(ser_bin); + gchar * ser_asc = NULL; + gchar dn[1024]; /* Certificate Name field */ + gsize dn_sz = sizeof(dn); + /* TODO: Analyze certificate time/date stuff */ + gboolean CERT_OK = TRUE; /* Is the certificate "good"? */ + + gnutls_x509_crt_t cert; /* Certificate data itself */ + + /* Suck the certificate data into the structure */ + gnutls_x509_crt_init(&cert); + gnutls_x509_crt_import (cert, &cert_list[0], + GNUTLS_X509_FMT_DER); + + /* Read key fingerprint */ + gnutls_x509_crt_get_fingerprint(cert, GNUTLS_MAC_SHA, + fpr_bin, &fpr_bin_sz); + fpr_asc = purple_base16_encode_chunked(fpr_bin,fpr_bin_sz); + + /* Read serial number */ + gnutls_x509_crt_get_serial(cert, ser_bin, &ser_bin_sz); + ser_asc = purple_base16_encode_chunked(ser_bin,ser_bin_sz); + + /* Read the certificate DN field */ + gnutls_x509_crt_get_dn(cert, dn, &dn_sz); + + /* TODO: Certificate checking here */ + + + /* Build the dialog */ + { + gchar * primary = NULL; + gchar * secondary = NULL; + + if ( CERT_OK == TRUE ) + { + primary = g_strdup_printf + ( + _("Certificate from %s is valid. Accept?"), + gsc->host + ); + } + else + { + primary = g_strdup_printf + ( + _("Certificate from %s not valid! Accept anyway?"), + gsc->host + ); + } + + secondary = g_strdup_printf + ( + _("Certificate name: %s\nKey fingerprint (SHA1):%s\nSerial Number:%s\nTODO: Expiration dates, etc.\n"), + dn, fpr_asc, ser_asc + ); + + purple_request_accept_cancel + (gsc, + _("SSL Authorization Request"), + primary, + secondary, + (CERT_OK == TRUE ? 1:2), /* Default action depends on certificate + status. */ + NULL, NULL, /* There is no way to extract account data from + a connection handle, it seems. */ + NULL, /* Same goes for the conversation data */ + gsc, /* Pass connection ptr to callback */ + ssl_gnutls_authcheck_cb, /* Accept */ + ssl_gnutls_authcheck_cb /* Cancel */ + ); + + g_free(primary); + g_free(secondary); + } + + + /* Cleanup! */ + g_free(fpr_asc); + g_free(ser_asc); + + gnutls_x509_crt_deinit(cert); + } +} static void ssl_gnutls_handshake_cb(gpointer data, gint source, PurpleInputCondition cond) @@ -96,6 +254,7 @@ } else { purple_debug_info("gnutls", "Handshake complete\n"); + /* Spit some key info to debug */ { const gnutls_datum_t *cert_list; unsigned int cert_list_size = 0; @@ -133,12 +292,12 @@ i, fpr_asc); tsz=sizeof(tbuf); - gnutls_x509_crt_get_serial(cert,tbuf,&tsz); + int ret = gnutls_x509_crt_get_serial(cert,tbuf,&tsz); tasc= purple_base16_encode_chunked(tbuf, tsz); purple_debug_info("gnutls", - "Serial: %s\n", - tasc); + "Serial: %s(%d bytes, ret=%d)\n", + tasc, tsz, ret); g_free(tasc); tsz=sizeof(tbuf); @@ -152,12 +311,21 @@ "Cert Issuer DN: %s\n", tbuf); + tsz=sizeof(tbuf); + gnutls_x509_crt_get_key_id(cert,0, tbuf, &tsz); + tasc = purple_base16_encode_chunked(tbuf, tsz); + purple_debug_info("gnutls", + "Key ID: %s\n", + tasc); + g_free(tasc); + g_free(fpr_asc); fpr_asc = NULL; gnutls_x509_crt_deinit(cert); - } + } /* for */ - } - gsc->connect_cb(gsc->connect_cb_data, gsc, cond); + } /* End keydata spitting */ + + /* Ask for cert verification */ } } diff -r 8b322c8afeb6 -r e0eb1eb5b47b libpurple/sslconn.h --- a/libpurple/sslconn.h Tue May 29 16:20:39 2007 +0000 +++ b/libpurple/sslconn.h Tue May 29 20:50:06 2007 +0000 @@ -32,7 +32,8 @@ typedef enum { PURPLE_SSL_HANDSHAKE_FAILED = 1, - PURPLE_SSL_CONNECT_FAILED = 2 + PURPLE_SSL_CONNECT_FAILED = 2, + PURPLE_SSL_PEER_AUTH_FAILED = 3 } PurpleSslErrorType; typedef struct _PurpleSslConnection PurpleSslConnection;