~ubuntu-branches/ubuntu/precise/empathy/precise-proposed-201205180810

« back to all changes in this revision

Viewing changes to libempathy/empathy-tls-verifier.c

  • Committer: Bazaar Package Importer
  • Author(s): Brian Curtis, Brian Curtis, Ken VanDine
  • Date: 2011-06-01 10:35:24 UTC
  • mfrom: (1.1.70 upstream) (6.3.44 experimental)
  • Revision ID: james.westby@ubuntu.com-20110601103524-wx3wgp71394730jt
Tags: 3.1.1-1ubuntu1
[ Brian Curtis ]
* Merge with Debian experimental, remaining Ubuntu changes:
* debian/control:
  - Drop geoclue/mapping build-depends (they are in Universe)
  - Add Vcz-Bzr link
  - Add Suggests on telepathy-idle
  - Bump telepathy-butterfly, telepathy-haze to recommends
  - Don't recommend the freedesktop sound theme we have an ubuntu one
  - Add build depend for libunity-dev
* debian/rules:
  - Use autoreconf.mk
  - Disable map and location
* debian/empathy.install:
  - Install message indicator configuration
* debian/indicators/empathy:
  - Message indicator configuration
* debian/patches/01_lpi.patch:
  - Add Launchpad integration
* debian/patches/10_use_notify_osd_icons.patch:
  - Use the notify-osd image for new messages
* debian/patches/34_start_raised_execpt_in_session.patch
  - If not started with the session, we should always raise
* debian/patches/36_chat_window_default_size.patch:
  - Make the default chat window size larger
* debian/patches/37_facebook_default.patch:
  - Make facebook the default chat account type
* debian/patches/38_lp_569289.patch
  - Set freenode as default IRC network for new IRC accounts 
* debian/patches/41_unity_launcher_progress.patch
  - Display file transfer progress in the unity launcher

[ Ken VanDine ]
* debian/control
  - build depend on libgcr-3-dev instead of libgcr-dev
  - dropped build depends for libindicate, we will use telepathy-indicator
  - Depend on dconf-gsettings-backend | gsettings-backend
  - Added a Recommends for telepathy-indicator
* +debian/empathy.gsettings-override
  - Added an override for notifications-focus
* debian/patches/series
  - commented out 23_idomessagedialog_for_voip_and_ft.patch, until ido has 
    been ported to gtk3

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
 * empathy-tls-verifier.c - Source for EmpathyTLSVerifier
3
3
 * Copyright (C) 2010 Collabora Ltd.
4
4
 * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
 
5
 * @author Stef Walter <stefw@collabora.co.uk>
5
6
 *
6
7
 * This library is free software; you can redistribute it and/or
7
8
 * modify it under the terms of the GNU Lesser General Public
27
28
 
28
29
#include "empathy-tls-verifier.h"
29
30
 
 
31
#include <gcr/gcr.h>
 
32
 
30
33
#define DEBUG_FLAG EMPATHY_DEBUG_TLS
31
34
#include "empathy-debug.h"
32
35
#include "empathy-utils.h"
44
47
  LAST_PROPERTY,
45
48
};
46
49
 
47
 
static const gchar* system_ca_paths[] = {
48
 
#ifdef GTLS_SYSTEM_CA_FILE
49
 
  GTLS_SYSTEM_CA_FILE,
50
 
#endif
51
 
  NULL,
52
 
};
53
 
 
54
50
typedef struct {
55
 
  GPtrArray *cert_chain;
56
 
 
57
 
  GPtrArray *trusted_ca_list;
58
 
  GPtrArray *trusted_crl_list;
59
 
 
60
51
  EmpathyTLSCertificate *certificate;
61
52
  gchar *hostname;
62
53
  gchar **reference_identities;
67
58
  gboolean dispose_run;
68
59
} EmpathyTLSVerifierPriv;
69
60
 
70
 
static gnutls_x509_crt_t *
71
 
ptr_array_to_x509_crt_list (GPtrArray *chain)
72
 
{
73
 
  gnutls_x509_crt_t *retval;
74
 
  gint idx;
75
 
 
76
 
  retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * chain->len);
77
 
 
78
 
  for (idx = 0; idx < (gint) chain->len; idx++)
79
 
    retval[idx] = g_ptr_array_index (chain, idx);
80
 
 
81
 
  return retval;
82
 
}
83
 
 
84
61
static gboolean
85
62
verification_output_to_reason (gint res,
86
63
    guint verify_output,
136
113
  return retval;
137
114
}
138
115
 
139
 
static gboolean
140
 
verify_last_certificate (EmpathyTLSVerifier *self,
141
 
    gnutls_x509_crt_t cert,
142
 
    EmpTLSCertificateRejectReason *reason)
 
116
static void
 
117
build_certificate_list_for_gnutls (GcrCertificateChain *chain,
 
118
        gnutls_x509_crt_t **list,
 
119
        guint *n_list,
 
120
        gnutls_x509_crt_t **anchors,
 
121
        guint *n_anchors)
143
122
{
144
 
  guint verify_output;
145
 
  gint res;
146
 
  gnutls_x509_crt_t *trusted_ca_list;
147
 
  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
148
 
 
149
 
  if (priv->trusted_ca_list->len > 0)
150
 
    {
151
 
      trusted_ca_list = ptr_array_to_x509_crt_list (priv->trusted_ca_list);
152
 
      res = gnutls_x509_crt_verify (cert, trusted_ca_list,
153
 
          priv->trusted_ca_list->len, 0, &verify_output);
154
 
 
155
 
      DEBUG ("Checking last certificate %p against trusted CAs, output %u",
156
 
          cert, verify_output);
157
 
 
158
 
      g_free (trusted_ca_list);
159
 
    }
160
 
  else
161
 
    {
162
 
      /* check it against itself to see if it's structurally valid */
163
 
      res = gnutls_x509_crt_verify (cert, &cert, 1, 0, &verify_output);
164
 
 
165
 
      DEBUG ("Checking last certificate %p against itself, output %u", cert,
166
 
          verify_output);
167
 
 
168
 
      /* if it's valid, return the SelfSigned error, so that we can add it
169
 
       * later to our trusted CAs whitelist.
170
 
       */
171
 
      if (res == GNUTLS_E_SUCCESS)
172
 
        {
173
 
          *reason = EMP_TLS_CERTIFICATE_REJECT_REASON_SELF_SIGNED;
174
 
          return FALSE;
175
 
        }
176
 
    }
177
 
 
178
 
  return verification_output_to_reason (res, verify_output, reason);
 
123
  GcrCertificate *cert;
 
124
  guint idx, length;
 
125
  gnutls_x509_crt_t *retval;
 
126
  gnutls_x509_crt_t gcert;
 
127
  gnutls_datum_t datum;
 
128
  gsize n_data;
 
129
 
 
130
  g_assert (list);
 
131
  g_assert (n_list);
 
132
  g_assert (anchors);
 
133
  g_assert (n_anchors);
 
134
 
 
135
  *list = *anchors = NULL;
 
136
  *n_list = *n_anchors = 0;
 
137
 
 
138
  length = gcr_certificate_chain_get_length (chain);
 
139
  retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * length);
 
140
 
 
141
  /* Convert the main body of the chain to gnutls */
 
142
  for (idx = 0; idx < length; ++idx)
 
143
    {
 
144
      cert = gcr_certificate_chain_get_certificate (chain, idx);
 
145
      datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
 
146
      datum.size = n_data;
 
147
 
 
148
      gnutls_x509_crt_init (&gcert);
 
149
      if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
 
150
        g_return_if_reached ();
 
151
 
 
152
      retval[idx] = gcert;
 
153
    }
 
154
 
 
155
  *list = retval;
 
156
  *n_list = length;
 
157
 
 
158
  /* See if we have an anchor */
 
159
  if (gcr_certificate_chain_get_status (chain) ==
 
160
          GCR_CERTIFICATE_CHAIN_ANCHORED)
 
161
    {
 
162
      cert = gcr_certificate_chain_get_anchor (chain);
 
163
      g_return_if_fail (cert);
 
164
 
 
165
      datum.data = (gpointer)gcr_certificate_get_der_data (cert, &n_data);
 
166
      datum.size = n_data;
 
167
 
 
168
      gnutls_x509_crt_init (&gcert);
 
169
      if (gnutls_x509_crt_import (gcert, &datum, GNUTLS_X509_FMT_DER) < 0)
 
170
        g_return_if_reached ();
 
171
 
 
172
      retval = g_malloc0 (sizeof (gnutls_x509_crt_t) * 1);
 
173
      retval[0] = gcert;
 
174
      *anchors = retval;
 
175
      *n_anchors = 1;
 
176
    }
179
177
}
180
178
 
181
 
static gboolean
182
 
verify_certificate (EmpathyTLSVerifier *self,
183
 
    gnutls_x509_crt_t cert,
184
 
    gnutls_x509_crt_t issuer,
185
 
    EmpTLSCertificateRejectReason *reason)
 
179
static void
 
180
free_certificate_list_for_gnutls (gnutls_x509_crt_t *list,
 
181
        guint n_list)
186
182
{
187
 
  guint verify_output;
188
 
  gint res;
189
 
 
190
 
  res = gnutls_x509_crt_verify (cert, &issuer, 1, 0, &verify_output);
191
 
 
192
 
  DEBUG ("Verifying %p against %p, output %u", cert, issuer, verify_output);
193
 
 
194
 
  return verification_output_to_reason (res, verify_output, reason);
 
183
  guint idx;
 
184
 
 
185
  for (idx = 0; idx < n_list; idx++)
 
186
    gnutls_x509_crt_deinit (list[idx]);
 
187
  g_free (list);
195
188
}
196
189
 
197
190
static void
223
216
}
224
217
 
225
218
static void
226
 
real_start_verification (EmpathyTLSVerifier *self)
227
 
{
228
 
  gnutls_x509_crt_t first_cert, last_cert;
229
 
  gint idx;
230
 
  gboolean res = FALSE;
231
 
  gint num_certs;
 
219
debug_certificate (GcrCertificate *cert)
 
220
{
 
221
    gchar *subject = gcr_certificate_get_subject_dn (cert);
 
222
    DEBUG ("Certificate: %s", subject);
 
223
    g_free (subject);
 
224
}
 
225
 
 
226
static void
 
227
debug_certificate_chain (GcrCertificateChain *chain)
 
228
{
 
229
    GEnumClass *enum_class;
 
230
    GEnumValue *enum_value;
 
231
    gint idx, length;
 
232
    GcrCertificate *cert;
 
233
 
 
234
    enum_class = G_ENUM_CLASS
 
235
            (g_type_class_peek (GCR_TYPE_CERTIFICATE_CHAIN_STATUS));
 
236
    enum_value = g_enum_get_value (enum_class,
 
237
            gcr_certificate_chain_get_status (chain));
 
238
    length = gcr_certificate_chain_get_length (chain);
 
239
    DEBUG ("Certificate chain: length %u status %s",
 
240
            length, enum_value ? enum_value->value_nick : "XXX");
 
241
 
 
242
    for (idx = 0; idx < length; ++idx)
 
243
      {
 
244
        cert = gcr_certificate_chain_get_certificate (chain, idx);
 
245
        debug_certificate (cert);
 
246
      }
 
247
}
 
248
 
 
249
static void
 
250
perform_verification (EmpathyTLSVerifier *self,
 
251
        GcrCertificateChain *chain)
 
252
{
 
253
  gboolean ret = FALSE;
232
254
  EmpTLSCertificateRejectReason reason =
233
255
    EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN;
 
256
  gnutls_x509_crt_t *list, *anchors;
 
257
  guint n_list, n_anchors;
 
258
  guint verify_output;
 
259
  gint res;
234
260
  gint i;
235
261
  gboolean matched = FALSE;
236
262
  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
237
263
 
238
 
  DEBUG ("Starting verification");
239
 
 
240
 
  /* check if the certificate matches one of the reference identities. */
241
 
  first_cert = g_ptr_array_index (priv->cert_chain, 0);
 
264
  DEBUG ("Performing verification");
 
265
  debug_certificate_chain (chain);
 
266
 
 
267
  list = anchors = NULL;
 
268
  n_list = n_anchors = 0;
 
269
 
 
270
  /*
 
271
   * If the first certificate is an pinned certificate then we completely
 
272
   * ignore the rest of the verification process.
 
273
   */
 
274
  if (gcr_certificate_chain_get_status (chain) == GCR_CERTIFICATE_CHAIN_PINNED)
 
275
    {
 
276
      DEBUG ("Found pinned certificate for %s", priv->hostname);
 
277
      complete_verification (self);
 
278
      goto out;
 
279
  }
 
280
 
 
281
  build_certificate_list_for_gnutls (chain, &list, &n_list,
 
282
          &anchors, &n_anchors);
 
283
  if (list == NULL || n_list == 0) {
 
284
      g_warn_if_reached ();
 
285
      abort_verification (self, EMP_TLS_CERTIFICATE_REJECT_REASON_UNKNOWN);
 
286
      goto out;
 
287
  }
 
288
 
 
289
  verify_output = 0;
 
290
  res = gnutls_x509_crt_list_verify (list, n_list, anchors, n_anchors,
 
291
           NULL, 0, 0, &verify_output);
 
292
  ret = verification_output_to_reason (res, verify_output, &reason);
 
293
 
 
294
  DEBUG ("Certificate verification gave result %d with reason %u", ret,
 
295
          reason);
 
296
 
 
297
  if (!ret) {
 
298
      abort_verification (self, reason);
 
299
      goto out;
 
300
  }
 
301
 
 
302
  /* now check if the certificate matches one of the reference identities. */
242
303
  if (priv->reference_identities != NULL)
243
304
    {
244
 
      for (i = 0; priv->reference_identities[i] != NULL; ++i)
 
305
      for (i = 0, matched = FALSE; priv->reference_identities[i] != NULL; ++i)
245
306
        {
246
 
          if (gnutls_x509_crt_check_hostname (first_cert,
 
307
          if (gnutls_x509_crt_check_hostname (list[0],
247
308
                  priv->reference_identities[i]) == 1)
248
309
            {
249
310
              matched = TRUE;
256
317
    {
257
318
      gchar *certified_hostname;
258
319
 
259
 
      reason = EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH;
260
 
      certified_hostname = empathy_get_x509_certificate_hostname (first_cert);
 
320
      certified_hostname = empathy_get_x509_certificate_hostname (list[0]);
261
321
      tp_asv_set_string (priv->details,
262
322
          "expected-hostname", priv->hostname);
263
323
      tp_asv_set_string (priv->details,
267
327
          certified_hostname, priv->hostname);
268
328
 
269
329
      g_free (certified_hostname);
 
330
      abort_verification (self,
 
331
              EMP_TLS_CERTIFICATE_REJECT_REASON_HOSTNAME_MISMATCH);
270
332
      goto out;
271
333
    }
272
334
 
273
335
  DEBUG ("Hostname matched");
274
 
 
275
 
  num_certs = priv->cert_chain->len;
276
 
 
277
 
  if (priv->trusted_ca_list->len > 0)
278
 
    {
279
 
      /* if the last certificate is self-signed, and we have a list of
280
 
       * trusted CAs, ignore it, as we want to check the chain against our
281
 
       * trusted CAs list first.
282
 
       * if we have only one certificate in the chain, don't ignore it though,
283
 
       * as it's the CA certificate itself.
284
 
       */
285
 
      last_cert = g_ptr_array_index (priv->cert_chain, num_certs - 1);
286
 
 
287
 
      if (gnutls_x509_crt_check_issuer (last_cert, last_cert) > 0 &&
288
 
          num_certs > 1)
289
 
        num_certs--;
290
 
    }
291
 
 
292
 
  for (idx = 1; idx < num_certs; idx++)
293
 
    {
294
 
      res = verify_certificate (self,
295
 
          g_ptr_array_index (priv->cert_chain, idx -1),
296
 
          g_ptr_array_index (priv->cert_chain, idx),
297
 
          &reason);
298
 
 
299
 
      DEBUG ("Certificate verification %d gave result %d with reason %u", idx,
300
 
          res, reason);
301
 
 
302
 
      if (!res)
303
 
        {
304
 
          abort_verification (self, reason);
305
 
          return;
306
 
        }
307
 
    }
308
 
 
309
 
  res = verify_last_certificate (self,
310
 
      g_ptr_array_index (priv->cert_chain, num_certs - 1),
311
 
      &reason);
312
 
 
313
 
  DEBUG ("Last verification gave result %d with reason %u", res, reason);
314
 
 
315
 
 out:
316
 
  if (!res)
317
 
    {
318
 
      abort_verification (self, reason);
319
 
      return;
320
 
    }
321
 
 
322
336
  complete_verification (self);
323
 
}
324
 
 
325
 
static gboolean
326
 
start_verification (gpointer user_data)
327
 
{
328
 
  EmpathyTLSVerifier *self = user_data;
329
 
 
330
 
  real_start_verification (self);
331
 
 
332
 
  return FALSE;
 
337
 
 
338
 out:
 
339
  free_certificate_list_for_gnutls (list, n_list);
 
340
  free_certificate_list_for_gnutls (anchors, n_anchors);
333
341
}
334
342
 
335
343
static void
336
 
build_gnutls_cert_list (EmpathyTLSVerifier *self)
337
 
{
338
 
  guint num_certs;
339
 
  guint idx;
340
 
  GPtrArray *certificate_data = NULL;
341
 
  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
342
 
 
343
 
  g_object_get (priv->certificate,
344
 
      "cert-data", &certificate_data,
345
 
      NULL);
346
 
  num_certs = certificate_data->len;
347
 
 
348
 
  priv->cert_chain = g_ptr_array_new_with_free_func (
349
 
      (GDestroyNotify) gnutls_x509_crt_deinit);
350
 
 
351
 
  for (idx = 0; idx < num_certs; idx++)
352
 
    {
353
 
      gnutls_x509_crt_t cert;
354
 
      GArray *one_cert;
355
 
      gnutls_datum_t datum = { NULL, 0 };
356
 
 
357
 
      one_cert = g_ptr_array_index (certificate_data, idx);
358
 
      datum.data = (guchar *) one_cert->data;
359
 
      datum.size = one_cert->len;
360
 
 
361
 
      gnutls_x509_crt_init (&cert);
362
 
      gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_DER);
363
 
 
364
 
      g_ptr_array_add (priv->cert_chain, cert);
365
 
    }
366
 
}
367
 
 
368
 
static gint
369
 
get_number_and_type_of_certificates (gnutls_datum_t *datum,
370
 
    gnutls_x509_crt_fmt_t *format)
371
 
{
372
 
  gnutls_x509_crt_t fake;
373
 
  guint retval = 1;
374
 
  gint res;
375
 
 
376
 
  res = gnutls_x509_crt_list_import (&fake, &retval, datum,
377
 
      GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
378
 
 
379
 
  if (res == GNUTLS_E_SHORT_MEMORY_BUFFER || res > 0)
380
 
    {
381
 
      DEBUG ("Found PEM, with %u certificates", retval);
382
 
      *format = GNUTLS_X509_FMT_PEM;
383
 
      return retval;
384
 
    }
385
 
 
386
 
  /* try DER */
387
 
  res = gnutls_x509_crt_list_import (&fake, &retval, datum,
388
 
      GNUTLS_X509_FMT_DER, 0);
389
 
 
390
 
  if (res > 0)
391
 
    {
392
 
      *format = GNUTLS_X509_FMT_DER;
393
 
      return retval;
394
 
    }
395
 
 
396
 
  return res;
397
 
}
398
 
 
399
 
static gboolean
400
 
build_gnutls_ca_and_crl_lists (GIOSchedulerJob *job,
401
 
    GCancellable *cancellable,
402
 
    gpointer user_data)
403
 
{
404
 
  gint idx;
405
 
  gchar *user_certs_dir;
406
 
  GDir *dir;
 
344
perform_verification_cb (GObject *object,
 
345
        GAsyncResult *res,
 
346
        gpointer user_data)
 
347
{
407
348
  GError *error = NULL;
408
 
  EmpathyTLSVerifier *self = user_data;
409
 
  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
410
 
 
411
 
  priv->trusted_ca_list = g_ptr_array_new_with_free_func
412
 
    ((GDestroyNotify) gnutls_x509_crt_deinit);
413
 
 
414
 
  for (idx = 0; idx < (gint) G_N_ELEMENTS (system_ca_paths) - 1; idx++)
415
 
    {
416
 
      const gchar *path;
417
 
      gchar *contents = NULL;
418
 
      gsize length = 0;
419
 
      gint res, n_certs;
420
 
      gnutls_x509_crt_t *cert_list;
421
 
      gnutls_datum_t datum = { NULL, 0 };
422
 
      gnutls_x509_crt_fmt_t format = 0;
423
 
 
424
 
      path = system_ca_paths[idx];
425
 
      g_file_get_contents (path, &contents, &length, &error);
426
 
 
427
 
      if (error != NULL)
428
 
        {
429
 
          DEBUG ("Unable to read system CAs from path %s: %s", path,
430
 
              error->message);
431
 
          g_clear_error (&error);
432
 
          continue;
433
 
        }
434
 
 
435
 
      datum.data = (guchar *) contents;
436
 
      datum.size = length;
437
 
      n_certs = get_number_and_type_of_certificates (&datum, &format);
438
 
 
439
 
      if (n_certs < 0)
440
 
        {
441
 
          DEBUG ("Unable to parse the system CAs from path %s: GnuTLS "
442
 
              "returned error %d", path, n_certs);
443
 
 
444
 
          g_free (contents);
445
 
          continue;
446
 
        }
447
 
 
448
 
      cert_list = g_malloc0 (sizeof (gnutls_x509_crt_t) * n_certs);
449
 
      res = gnutls_x509_crt_list_import (cert_list, (guint *) &n_certs, &datum,
450
 
          format, 0);
451
 
 
452
 
      if (res < 0)
453
 
        {
454
 
          DEBUG ("Unable to import system CAs from path %s; "
455
 
              "GnuTLS returned error %d", path, res);
456
 
 
457
 
          g_free (contents);
458
 
          continue;
459
 
        }
460
 
 
461
 
      DEBUG ("Successfully imported %d system CA certificates from path %s",
462
 
          n_certs, path);
463
 
 
464
 
      /* append the newly created cert structutes into the global GPtrArray */
465
 
      for (idx = 0; idx < n_certs; idx++)
466
 
        g_ptr_array_add (priv->trusted_ca_list, cert_list[idx]);
467
 
 
468
 
      g_free (contents);
469
 
      g_free (cert_list);
470
 
    }
471
 
 
472
 
  /* user certs */
473
 
  user_certs_dir = g_build_filename (g_get_user_config_dir (),
474
 
      "telepathy", "certs", NULL);
475
 
  dir = g_dir_open (user_certs_dir, 0, &error);
476
 
 
477
 
  if (error != NULL)
478
 
    {
479
 
      DEBUG ("Can't open the user certs dir at %s: %s", user_certs_dir,
480
 
          error->message);
481
 
 
482
 
      g_error_free (error);
483
 
    }
484
 
  else
485
 
    {
486
 
      const gchar *cert_name;
487
 
 
488
 
      while ((cert_name = g_dir_read_name (dir)) != NULL)
489
 
        {
490
 
          gchar *contents = NULL, *cert_path = NULL;
491
 
          gsize length = 0;
492
 
          gint res;
493
 
          gnutls_datum_t datum = { NULL, 0 };
494
 
          gnutls_x509_crt_t cert;
495
 
 
496
 
          cert_path = g_build_filename (user_certs_dir, cert_name, NULL);
497
 
 
498
 
          g_file_get_contents (cert_path, &contents, &length, &error);
499
 
 
500
 
          if (error != NULL)
501
 
            {
502
 
              DEBUG ("Can't open the certificate file at path %s: %s",
503
 
                  cert_path, error->message);
504
 
 
505
 
              g_clear_error (&error);
506
 
              g_free (cert_path);
507
 
              continue;
508
 
            }
509
 
 
510
 
          datum.data = (guchar *) contents;
511
 
          datum.size = length;
512
 
 
513
 
          gnutls_x509_crt_init (&cert);
514
 
          res = gnutls_x509_crt_import (cert, &datum, GNUTLS_X509_FMT_PEM);
515
 
 
516
 
          if (res != GNUTLS_E_SUCCESS)
517
 
            {
518
 
              DEBUG ("Can't import the certificate at path %s: "
519
 
                  "GnuTLS returned %d", cert_path, res);
520
 
            }
521
 
          else
522
 
            {
523
 
              g_ptr_array_add (priv->trusted_ca_list, cert);
524
 
            }
525
 
 
526
 
          g_free (contents);
527
 
          g_free (cert_path);
528
 
        }
529
 
 
530
 
      g_dir_close (dir);
531
 
    }
532
 
 
533
 
  g_free (user_certs_dir);
534
 
 
535
 
  /* TODO: do the CRL too */
536
 
 
537
 
  g_io_scheduler_job_send_to_mainloop_async (job,
538
 
      start_verification, self, NULL);
539
 
 
540
 
  return FALSE;
 
349
 
 
350
  GcrCertificateChain *chain = GCR_CERTIFICATE_CHAIN (object);
 
351
  EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (user_data);
 
352
 
 
353
  /* Even if building the chain fails, try verifying what we have */
 
354
  if (!gcr_certificate_chain_build_finish (chain, res, &error))
 
355
    {
 
356
      DEBUG ("Building of certificate chain failed: %s", error->message);
 
357
      g_clear_error (&error);
 
358
    }
 
359
 
 
360
  perform_verification (self, chain);
 
361
 
 
362
  /* Matches ref when staring chain build */
 
363
  g_object_unref (self);
541
364
}
542
365
 
543
366
static void
612
435
 
613
436
  DEBUG ("%p", object);
614
437
 
615
 
  tp_clear_pointer (&priv->trusted_ca_list, g_ptr_array_unref);
616
 
  tp_clear_pointer (&priv->cert_chain, g_ptr_array_unref);
617
438
  tp_clear_boxed (G_TYPE_HASH_TABLE, &priv->details);
618
439
  g_free (priv->hostname);
619
440
  g_strfreev (priv->reference_identities);
622
443
}
623
444
 
624
445
static void
625
 
empathy_tls_verifier_constructed (GObject *object)
626
 
{
627
 
  EmpathyTLSVerifier *self = EMPATHY_TLS_VERIFIER (object);
628
 
 
629
 
  build_gnutls_cert_list (self);
630
 
 
631
 
  if (G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed != NULL)
632
 
    G_OBJECT_CLASS (empathy_tls_verifier_parent_class)->constructed (object);
633
 
}
634
 
 
635
 
static void
636
446
empathy_tls_verifier_init (EmpathyTLSVerifier *self)
637
447
{
638
448
  EmpathyTLSVerifierPriv *priv;
654
464
  oclass->get_property = empathy_tls_verifier_get_property;
655
465
  oclass->finalize = empathy_tls_verifier_finalize;
656
466
  oclass->dispose = empathy_tls_verifier_dispose;
657
 
  oclass->constructed = empathy_tls_verifier_constructed;
658
467
 
659
468
  pspec = g_param_spec_object ("certificate", "The EmpathyTLSCertificate",
660
469
      "The EmpathyTLSCertificate to be verified.",
678
487
 
679
488
EmpathyTLSVerifier *
680
489
empathy_tls_verifier_new (EmpathyTLSCertificate *certificate,
681
 
    const gchar *hostname,
682
 
    const gchar **reference_identities)
 
490
    const gchar *hostname, const gchar **reference_identities)
683
491
{
684
492
  g_assert (EMPATHY_IS_TLS_CERTIFICATE (certificate));
685
493
  g_assert (hostname != NULL);
697
505
    GAsyncReadyCallback callback,
698
506
    gpointer user_data)
699
507
{
 
508
  GcrCertificateChain *chain;
 
509
  GcrCertificate *cert;
 
510
  GPtrArray *cert_data = NULL;
 
511
  GArray *data;
 
512
  guint idx;
700
513
  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
701
514
 
 
515
  DEBUG ("Starting verification");
 
516
 
702
517
  g_return_if_fail (priv->verify_result == NULL);
703
518
 
 
519
  g_object_get (priv->certificate, "cert-data", &cert_data, NULL);
 
520
  g_return_if_fail (cert_data);
 
521
 
704
522
  priv->verify_result = g_simple_async_result_new (G_OBJECT (self),
705
523
      callback, user_data, NULL);
706
524
 
707
 
  g_io_scheduler_push_job (build_gnutls_ca_and_crl_lists,
708
 
      self, NULL, G_PRIORITY_DEFAULT, NULL);
 
525
  /* Create a certificate chain */
 
526
  chain = gcr_certificate_chain_new ();
 
527
  for (idx = 0; idx < cert_data->len; ++idx) {
 
528
    data = g_ptr_array_index (cert_data, idx);
 
529
    cert = gcr_simple_certificate_new (data->data, data->len);
 
530
    gcr_certificate_chain_add (chain, cert);
 
531
    g_object_unref (cert);
 
532
  }
 
533
 
 
534
  gcr_certificate_chain_build_async (chain, GCR_PURPOSE_CLIENT_AUTH, priv->hostname, 0,
 
535
          NULL, perform_verification_cb, g_object_ref (self));
 
536
 
 
537
  g_object_unref (chain);
 
538
  g_boxed_free (TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, cert_data);
709
539
}
710
540
 
711
541
gboolean
739
569
 
740
570
  return TRUE;
741
571
}
 
572
 
 
573
void
 
574
empathy_tls_verifier_store_exception (EmpathyTLSVerifier *self)
 
575
{
 
576
  GArray *data;
 
577
  GcrCertificate *cert;
 
578
  GPtrArray *cert_data = NULL;
 
579
  GError *error = NULL;
 
580
  EmpathyTLSVerifierPriv *priv = GET_PRIV (self);
 
581
 
 
582
  g_object_get (priv->certificate, "cert-data", &cert_data, NULL);
 
583
  g_return_if_fail (cert_data);
 
584
 
 
585
  if (!cert_data->len)
 
586
    {
 
587
      DEBUG ("No certificate to pin.");
 
588
      return;
 
589
    }
 
590
 
 
591
  /* The first certificate in the chain is for the host */
 
592
  data = g_ptr_array_index (cert_data, 0);
 
593
  cert = gcr_simple_certificate_new ((gpointer)data->data, data->len);
 
594
 
 
595
  DEBUG ("Storing pinned certificate:");
 
596
  debug_certificate (cert);
 
597
 
 
598
  if (!gcr_trust_add_pinned_certificate (cert, GCR_PURPOSE_CLIENT_AUTH,
 
599
          priv->hostname, NULL, &error))
 
600
      DEBUG ("Can't store the pinned certificate: %s", error->message);
 
601
 
 
602
  g_object_unref (cert);
 
603
  g_boxed_free (TP_ARRAY_TYPE_UCHAR_ARRAY_LIST, cert_data);
 
604
}