static void perform_verification (EmpathyTLSVerifier *self, GcrCertificateChain *chain) { gboolean ret = FALSE; EmpTLSCertificateRejectReason reason = EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN; gnutls_x509_crt_t *list, *anchors; guint n_list, n_anchors; guint verify_output; gint res; gint i; gboolean matched = FALSE; EmpathyTLSVerifierPriv *priv = GET_PRIV (self); DEBUG ("Performing verification"); debug_certificate_chain (chain); list = anchors = NULL; n_list = n_anchors = 0; /* * If the first certificate is an pinned certificate then we completely * ignore the rest of the verification process. */ if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED) { DEBUG ("Found pinned certificate for %s", priv->hostname); complete_verification (self); goto out; } build_certificate_list_for_gnutls (chain, &list, &n_list, &anchors, &n_anchors); if (list == NULL || n_list == 0) { g_warn_if_reached (); abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN); goto out; } verify_output = 0; res = gnutls_x509_crt_list_verify (list, n_list, anchors, n_anchors, NULL, 0, 0, &verify_output); ret = verification_output_to_reason (res, verify_output, &reason); DEBUG ("Certificate verification gave result %d with reason %u", ret, reason); if (!ret) { abort_verification (self, reason); goto out; } /* now check if the certificate matches one of the reference identities. */ if (priv->reference_identities != NULL) { for (i = 0, matched = FALSE; priv->reference_identities[i] != NULL; ++i) { if (gnutls_x509_crt_check_hostname (list[0], priv->reference_identities[i]) == 1) { matched = TRUE; break; } } } if (!matched) { gchar *certified_hostname; certified_hostname = empathy_get_x509_certificate_hostname (list[0]); 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); g_free (certified_hostname); abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH); goto out; } DEBUG ("Hostname matched"); complete_verification (self); out: free_certificate_list_for_gnutls (list, n_list); free_certificate_list_for_gnutls (anchors, n_anchors); }
void empathy_tls_certificate_store_ca (EmpathyTLSCertificate *self) { GArray *last_cert; gnutls_x509_crt_t cert; gnutls_datum_t datum = { NULL, 0 }; gsize exported_len; guchar *exported_cert = NULL; gint res, offset; gchar *user_certs_dir = NULL, *filename = NULL, *path = NULL; gchar *hostname = NULL; GError *error = NULL; EmpathyTLSCertificatePriv *priv = GET_PRIV (self); last_cert = g_ptr_array_index (priv->cert_data, priv->cert_data->len - 1); datum.data = (guchar *) last_cert->data; datum.size = last_cert->len; gnutls_x509_crt_init (&cert); gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER); /* make sure it's self-signed, otherwise it's not a CA */ if (gnutls_x509_crt_check_issuer (cert, cert) <= 0) { DEBUG ("Can't import the CA, as it's not self-signed"); gnutls_x509_crt_deinit (cert); return; } if (gnutls_x509_crt_get_ca_status (cert, NULL) <= 0) { DEBUG ("Can't import the CA, it's not a valid CA certificate"); gnutls_x509_crt_deinit (cert); goto out; } exported_len = get_exported_size (cert); exported_cert = g_malloc (sizeof (guchar) * exported_len); res = gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, exported_cert, &exported_len); if (res < 0) { DEBUG ("Failed to export the CA certificate; GnuTLS returned %d," "and should be %lu bytes long", res, (gulong) exported_len); gnutls_x509_crt_deinit (cert); goto out; } hostname = empathy_get_x509_certificate_hostname (cert); if (hostname == NULL) hostname = g_strdup ("ca"); gnutls_x509_crt_deinit (cert); /* write the file */ user_certs_dir = g_build_filename (g_get_user_config_dir (), "telepathy", "certs", NULL); res = g_mkdir_with_parents (user_certs_dir, S_IRWXU | S_IRWXG); if (res < 0) { DEBUG ("Failed to create the user certificate directory: %s", g_strerror (errno)); goto out; } offset = 0; do { g_free (path); if (offset == 0) filename = g_strdup_printf ("cert-%s", hostname); else filename = g_strdup_printf ("cert-%s-%d", hostname, offset); path = g_build_filename (user_certs_dir, filename, NULL); offset++; g_free (filename); } while (g_file_test (path, G_FILE_TEST_EXISTS)); DEBUG ("Will save to %s", path); g_file_set_contents (path, (const gchar *) exported_cert, exported_len, &error); if (error != NULL) { DEBUG ("Can't save the CA certificate to %s: %s", path, error->message); g_error_free (error); } out: g_free (path); g_free (exported_cert); g_free (user_certs_dir); g_free (hostname); }