};
static const gchar* system_ca_paths[] = {
- "/etc/ssl/certs/ca-certificates.crt",
+ GTLS_SYSTEM_CA_FILE,
NULL,
};
gchar *hostname;
GSimpleAsyncResult *verify_result;
+ GHashTable *details;
gboolean dispose_run;
} EmpathyTLSVerifierPriv;
{
gboolean retval = TRUE;
+ g_assert (reason != NULL);
+
if (res != GNUTLS_E_SUCCESS)
{
retval = FALSE;
res = gnutls_x509_crt_verify (cert, &issuer, 1, 0, &verify_output);
+ DEBUG ("Verifying %p against %p, output %u", cert, issuer, verify_output);
+
return verification_output_to_reason (res, verify_output, reason);
}
g_simple_async_result_complete_in_idle (priv->verify_result);
- tp_clear_object (&priv->verify_result);
+ tp_clear_object (&priv->verify_result);
}
static void
tp_clear_object (&priv->verify_result);
}
-static gchar *
-get_certified_hostname (gnutls_x509_crt_t cert)
-{
- gchar dns_name[256];
- gsize dns_name_size;
- gint idx;
- gint res = 0;
-
- /* this is taken from GnuTLS */
- for (idx = 0; res >= 0; idx++)
- {
- dns_name_size = sizeof (dns_name);
- res = gnutls_x509_crt_get_subject_alt_name (cert, idx,
- dns_name, &dns_name_size, NULL);
-
- if (res == GNUTLS_SAN_DNSNAME || res == GNUTLS_SAN_IPADDRESS)
- return g_strndup (dns_name, dns_name_size);
- }
-
- dns_name_size = sizeof (dns_name);
- res = gnutls_x509_crt_get_dn_by_oid (cert, GNUTLS_OID_X520_COMMON_NAME,
- 0, 0, dns_name, &dns_name_size);
-
- if (res >= 0)
- return g_strndup (dns_name, dns_name_size);
-
- return NULL;
-}
-
static void
real_start_verification (EmpathyTLSVerifier *self)
{
{
gchar *certified_hostname;
- certified_hostname = get_certified_hostname (first_cert);
+ reason = EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH;
+ certified_hostname = empathy_get_x509_certificate_hostname (first_cert);
+ tp_asv_set_string (priv->details,
+ "expected-hostname", priv->hostname);
+ tp_asv_set_string (priv->details,
+ "certificate-hostname", certified_hostname);
+
DEBUG ("Hostname mismatch: got %s but expected %s",
certified_hostname, priv->hostname);
- /* TODO: pass-through the expected hostname in the reject details */
- reason = EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH;
-
g_free (certified_hostname);
goto out;
}
/* if the last certificate is self-signed, and we have a list of
* trusted CAs, ignore it, as we want to check the chain against our
* trusted CAs list first.
+ * if we have only one certificate in the chain, don't ignore it though,
+ * as it's the CA certificate itself.
*/
last_cert = g_ptr_array_index (priv->cert_chain, num_certs - 1);
- if (gnutls_x509_crt_check_issuer (last_cert, last_cert) > 0)
+ if (gnutls_x509_crt_check_issuer (last_cert, last_cert) > 0 &&
+ num_certs > 1)
num_certs--;
}
NULL);
num_certs = certificate_data->len;
- priv->cert_chain = g_ptr_array_sized_new (num_certs);
+ priv->cert_chain = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) gnutls_x509_crt_deinit);
for (idx = 0; idx < num_certs; idx++)
{
gnutls_x509_crt_fmt_t *format)
{
gnutls_x509_crt_t fake;
- gint retval = 1;
+ guint retval = 1;
gint res;
- res = gnutls_x509_crt_list_import (&fake, (guint *) &retval, datum,
- GNUTLS_X509_FMT_PEM, 0);
+ res = gnutls_x509_crt_list_import (&fake, &retval, datum,
+ GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
if (res == GNUTLS_E_SHORT_MEMORY_BUFFER || res > 0)
{
+ DEBUG ("Found PEM, with %u certificates", retval);
*format = GNUTLS_X509_FMT_PEM;
return retval;
}
/* try DER */
- res = gnutls_x509_crt_list_import (&fake, (guint *) &retval, datum,
+ res = gnutls_x509_crt_list_import (&fake, &retval, datum,
GNUTLS_X509_FMT_DER, 0);
if (res > 0)
gpointer user_data)
{
gint idx;
+ gchar *user_certs_dir;
+ GDir *dir;
+ GError *error = NULL;
EmpathyTLSVerifier *self = user_data;
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
- priv->trusted_ca_list = g_ptr_array_new ();
+ priv->trusted_ca_list = g_ptr_array_new_with_free_func
+ ((GDestroyNotify) gnutls_x509_crt_deinit);
for (idx = 0; idx < (gint) G_N_ELEMENTS (system_ca_paths) - 1; idx++)
{
gnutls_x509_crt_t *cert_list;
gnutls_datum_t datum = { NULL, 0 };
gnutls_x509_crt_fmt_t format = 0;
- GError *error = NULL;
path = system_ca_paths[idx];
g_file_get_contents (path, &contents, &length, &error);
if (error != NULL)
{
- DEBUG ("Unable to read system CAs from path %s", path);
- g_error_free (error);
+ DEBUG ("Unable to read system CAs from path %s: %s", path,
+ error->message);
+ g_clear_error (&error);
continue;
}
g_ptr_array_add (priv->trusted_ca_list, cert_list[idx]);
g_free (contents);
+ g_free (cert_list);
+ }
+
+ /* user certs */
+ user_certs_dir = g_build_filename (g_get_user_config_dir (),
+ "telepathy", "certs", NULL);
+ dir = g_dir_open (user_certs_dir, 0, &error);
+
+ if (error != NULL)
+ {
+ DEBUG ("Can't open the user certs dir at %s: %s", user_certs_dir,
+ error->message);
+
+ g_error_free (error);
}
+ else
+ {
+ const gchar *cert_name;
+
+ while ((cert_name = g_dir_read_name (dir)) != NULL)
+ {
+ gchar *contents = NULL, *cert_path = NULL;
+ gsize length = 0;
+ gint res;
+ gnutls_datum_t datum = { NULL, 0 };
+ gnutls_x509_crt_t cert;
+
+ cert_path = g_build_filename (user_certs_dir, cert_name, NULL);
+
+ g_file_get_contents (cert_path, &contents, &length, &error);
+
+ if (error != NULL)
+ {
+ DEBUG ("Can't open the certificate file at path %s: %s",
+ cert_path, error->message);
+
+ g_clear_error (&error);
+ g_free (cert_path);
+ continue;
+ }
+
+ datum.data = (guchar *) contents;
+ datum.size = length;
+
+ gnutls_x509_crt_init (&cert);
+ res = gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_PEM);
+
+ if (res != GNUTLS_E_SUCCESS)
+ {
+ DEBUG ("Can't import the certificate at path %s: "
+ "GnuTLS returned %d", cert_path, res);
+ }
+ else
+ {
+ g_ptr_array_add (priv->trusted_ca_list, cert);
+ }
+
+ g_free (contents);
+ g_free (cert_path);
+ }
+
+ g_dir_close (dir);
+ }
+
+ g_free (user_certs_dir);
/* TODO: do the CRL too */
EmpathyTLSVerifierPriv *priv = GET_PRIV (object);
DEBUG ("%p", object);
-
+
+ tp_clear_pointer (&priv->trusted_ca_list, g_ptr_array_unref);
+ tp_clear_pointer (&priv->cert_chain, g_ptr_array_unref);
+ tp_clear_boxed (G_TYPE_HASH_TABLE, &priv->details);
g_free (priv->hostname);
G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->finalize (object);
EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (object);
build_gnutls_cert_list (self);
-
+
if (G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed != NULL)
G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed (object);
}
static void
empathy_tls_verifier_init (EmpathyTLSVerifier *self)
{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ EmpathyTLSVerifierPriv *priv;
+
+ priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
EMPATHY_TYPE_TLS_VERIFIER, EmpathyTLSVerifierPriv);
+ priv->details = tp_asv_new (NULL, NULL);
}
static void
{
EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
+ g_return_if_fail (priv->verify_result == NULL);
+
priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
callback, user_data, NULL);
empathy_tls_verifier_verify_finish (EmpathyTLSVerifier *self,
GAsyncResult *res,
EmpTLSCertificateRejectReason *reason,
+ GHashTable **details,
GError **error)
{
+ EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
+
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
error))
{
- *reason = (*error)->code;
+ if (reason != NULL)
+ *reason = (*error)->code;
+
+ if (details != NULL)
+ {
+ *details = tp_asv_new (NULL, NULL);
+ tp_g_hash_table_update (*details, priv->details,
+ (GBoxedCopyFunc) g_strdup,
+ (GBoxedCopyFunc) tp_g_value_slice_dup);
+ }
+
return FALSE;
}
- *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+ if (reason != NULL)
+ *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
+
return TRUE;
}