gnutls: Fix up GTlsCertificateFlags handling
[platform/upstream/glib-networking.git] / tls / gnutls / gtlscertificate-gnutls.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2009 Red Hat, Inc
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include <gnutls/gnutls.h>
24 #include <gnutls/x509.h>
25 #include <string.h>
26
27 #include "gtlscertificate-gnutls.h"
28 #include <glib/gi18n-lib.h>
29
30 static void     g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface);
31
32 G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE,
33                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
34                                                 g_tls_certificate_gnutls_initable_iface_init);)
35
36 enum
37 {
38   PROP_0,
39
40   PROP_CERTIFICATE,
41   PROP_CERTIFICATE_PEM,
42   PROP_PRIVATE_KEY,
43   PROP_PRIVATE_KEY_PEM,
44   PROP_ISSUER
45 };
46
47 struct _GTlsCertificateGnutlsPrivate
48 {
49   gnutls_x509_crt_t cert;
50   gnutls_x509_privkey_t key;
51
52   GTlsCertificateGnutls *issuer;
53
54   GError *construct_error;
55
56   guint have_cert : 1;
57   guint have_key  : 1;
58 };
59
60 static void
61 g_tls_certificate_gnutls_finalize (GObject *object)
62 {
63   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
64
65   gnutls_x509_crt_deinit (gnutls->priv->cert);
66   gnutls_x509_privkey_deinit (gnutls->priv->key);
67
68   if (gnutls->priv->issuer)
69     g_object_unref (gnutls->priv->issuer);
70
71   g_clear_error (&gnutls->priv->construct_error);
72
73   G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object);
74 }
75
76 static void
77 g_tls_certificate_gnutls_get_property (GObject    *object,
78                                        guint       prop_id,
79                                        GValue     *value,
80                                        GParamSpec *pspec)
81 {
82   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
83   GByteArray *certificate;
84   char *certificate_pem;
85   int status;
86   size_t size;
87
88   switch (prop_id)
89     {
90     case PROP_CERTIFICATE:
91       size = 0;
92       status = gnutls_x509_crt_export (gnutls->priv->cert,
93                                        GNUTLS_X509_FMT_DER,
94                                        NULL, &size);
95       if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
96         certificate = NULL;
97       else
98         {
99           certificate = g_byte_array_sized_new (size);
100           certificate->len = size;
101           status = gnutls_x509_crt_export (gnutls->priv->cert,
102                                            GNUTLS_X509_FMT_DER,
103                                            certificate->data, &size);
104           if (status != 0)
105             {
106               g_byte_array_free (certificate, TRUE);
107               certificate = NULL;
108             }
109         }
110       g_value_take_boxed (value, certificate);
111       break;
112
113     case PROP_CERTIFICATE_PEM:
114       size = 0;
115       status = gnutls_x509_crt_export (gnutls->priv->cert,
116                                        GNUTLS_X509_FMT_PEM,
117                                        NULL, &size);
118       if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
119         certificate_pem = NULL;
120       else
121         {
122           certificate_pem = g_malloc (size);
123           status = gnutls_x509_crt_export (gnutls->priv->cert,
124                                            GNUTLS_X509_FMT_PEM,
125                                            certificate_pem, &size);
126           if (status != 0)
127             {
128               g_free (certificate_pem);
129               certificate_pem = NULL;
130             }
131         }
132       g_value_take_string (value, certificate_pem);
133       break;
134
135     case PROP_ISSUER:
136       g_value_set_object (value, gnutls->priv->issuer);
137       break;
138
139     default:
140       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
141     }
142 }
143
144 static void
145 g_tls_certificate_gnutls_set_property (GObject      *object,
146                                        guint         prop_id,
147                                        const GValue *value,
148                                        GParamSpec   *pspec)
149 {
150   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
151   GByteArray *bytes;
152   const char *string;
153   gnutls_datum_t data;
154   int status;
155
156   switch (prop_id)
157     {
158     case PROP_CERTIFICATE:
159       bytes = g_value_get_boxed (value);
160       if (!bytes)
161         break;
162       g_return_if_fail (gnutls->priv->have_cert == FALSE);
163       data.data = bytes->data;
164       data.size = bytes->len;
165       status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
166                                        GNUTLS_X509_FMT_DER);
167       if (status == 0)
168         gnutls->priv->have_cert = TRUE;
169       else if (!gnutls->priv->construct_error)
170         {
171           gnutls->priv->construct_error =
172             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
173                          _("Could not parse DER certificate: %s"),
174                          gnutls_strerror (status));
175         }
176
177       break;
178
179     case PROP_CERTIFICATE_PEM:
180       string = g_value_get_string (value);
181       if (!string)
182         break;
183       g_return_if_fail (gnutls->priv->have_cert == FALSE);
184       data.data = (void *)string;
185       data.size = strlen (string);
186       status = gnutls_x509_crt_import (gnutls->priv->cert, &data,
187                                        GNUTLS_X509_FMT_PEM);
188       if (status == 0)
189         gnutls->priv->have_cert = TRUE;
190       else if (!gnutls->priv->construct_error)
191         {
192           gnutls->priv->construct_error =
193             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
194                          _("Could not parse PEM certificate: %s"),
195                          gnutls_strerror (status));
196         }
197       break;
198
199     case PROP_PRIVATE_KEY:
200       bytes = g_value_get_boxed (value);
201       if (!bytes)
202         break;
203       g_return_if_fail (gnutls->priv->have_key == FALSE);
204       data.data = bytes->data;
205       data.size = bytes->len;
206       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
207                                            GNUTLS_X509_FMT_DER);
208       if (status == 0)
209         gnutls->priv->have_key = TRUE;
210       else if (!gnutls->priv->construct_error)
211         {
212           gnutls->priv->construct_error =
213             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
214                          _("Could not parse DER private key: %s"),
215                          gnutls_strerror (status));
216         }
217       break;
218
219     case PROP_PRIVATE_KEY_PEM:
220       string = g_value_get_string (value);
221       if (!string)
222         break;
223       g_return_if_fail (gnutls->priv->have_key == FALSE);
224       data.data = (void *)string;
225       data.size = strlen (string);
226       status = gnutls_x509_privkey_import (gnutls->priv->key, &data,
227                                            GNUTLS_X509_FMT_PEM);
228       if (status == 0)
229         gnutls->priv->have_key = TRUE;
230       else if (!gnutls->priv->construct_error)
231         {
232           gnutls->priv->construct_error =
233             g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
234                          _("Could not parse PEM private key: %s"),
235                          gnutls_strerror (status));
236         }
237       break;
238
239     case PROP_ISSUER:
240       gnutls->priv->issuer = g_value_dup_object (value);
241       break;
242
243     default:
244       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
245     }
246 }
247
248 static void
249 g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls)
250 {
251   gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls,
252                                               G_TYPE_TLS_CERTIFICATE_GNUTLS,
253                                               GTlsCertificateGnutlsPrivate);
254
255   gnutls_x509_crt_init (&gnutls->priv->cert);
256   gnutls_x509_privkey_init (&gnutls->priv->key);
257 }
258
259 static gboolean
260 g_tls_certificate_gnutls_initable_init (GInitable       *initable,
261                                         GCancellable    *cancellable,
262                                         GError         **error)
263 {
264   GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (initable);
265
266   if (gnutls->priv->construct_error)
267     {
268       g_propagate_error (error, gnutls->priv->construct_error);
269       gnutls->priv->construct_error = NULL;
270       return FALSE;
271     }
272   else if (!gnutls->priv->have_cert)
273     {
274       g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
275                            _("No certificate data provided"));
276       return FALSE;
277     }
278   else
279     return TRUE;
280 }
281
282 static GTlsCertificateFlags
283 g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
284                                  GSocketConnectable  *identity,
285                                  GTlsCertificate     *trusted_ca)
286 {
287   GTlsCertificateGnutls *cert_gnutls;
288   guint num_certs, i;
289   gnutls_x509_crt_t *chain;
290   GTlsCertificateFlags gtls_flags;
291   time_t t, now;
292   
293   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
294   for (num_certs = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer)
295     num_certs++;
296   chain = g_new (gnutls_x509_crt_t, num_certs);
297   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
298   for (i = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer, i++)
299     chain[i] = cert_gnutls->priv->cert;
300
301   if (trusted_ca)
302     {
303       gnutls_x509_crt_t ca;
304       guint gnutls_flags;
305       int status;
306
307       ca = G_TLS_CERTIFICATE_GNUTLS (trusted_ca)->priv->cert;
308       status = gnutls_x509_crt_list_verify (chain, num_certs,
309                                             &ca, 1,
310                                             NULL, 0,
311                                             GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
312                                             &gnutls_flags);
313       if (status != 0)
314         {
315           g_free (chain);
316           return G_TLS_CERTIFICATE_GENERIC_ERROR;
317         }
318
319       gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags);
320     }
321
322   /* We have to check these ourselves since gnutls_x509_crt_list_verify
323    * won't bother if it gets an UNKNOWN_CA.
324    */
325   now = time (NULL);
326   for (i = 0; i < num_certs; i++)
327     {
328       t = gnutls_x509_crt_get_activation_time (chain[i]);
329       if (t == (time_t) -1 || t > now)
330         gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
331
332       t = gnutls_x509_crt_get_expiration_time (chain[i]);
333       if (t == (time_t) -1 || t < now)
334         gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
335     }
336
337   g_free (chain);
338
339   if (identity)
340     gtls_flags |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (cert), identity);
341
342   return gtls_flags;
343 }
344
345 static void
346 g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
347 {
348   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
349   GTlsCertificateClass *certificate_class = G_TLS_CERTIFICATE_CLASS (klass);
350
351   g_type_class_add_private (klass, sizeof (GTlsCertificateGnutlsPrivate));
352
353   gobject_class->get_property = g_tls_certificate_gnutls_get_property;
354   gobject_class->set_property = g_tls_certificate_gnutls_set_property;
355   gobject_class->finalize     = g_tls_certificate_gnutls_finalize;
356
357   certificate_class->verify = g_tls_certificate_gnutls_verify;
358
359   g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
360   g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
361   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
362   g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
363   g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
364 }
365
366 static void
367 g_tls_certificate_gnutls_initable_iface_init (GInitableIface  *iface)
368 {
369   iface->init = g_tls_certificate_gnutls_initable_init;
370 }
371
372 GTlsCertificate *
373 g_tls_certificate_gnutls_new (const gnutls_datum *datum,
374                               GTlsCertificate    *issuer)
375 {
376   GTlsCertificateGnutls *gnutls;
377
378   gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
379                          "issuer", issuer,
380                          NULL);
381   if (gnutls_x509_crt_import (gnutls->priv->cert, datum,
382                               GNUTLS_X509_FMT_DER) == 0)
383     gnutls->priv->have_cert = TRUE;
384
385   return G_TLS_CERTIFICATE (gnutls);
386 }
387
388 const gnutls_x509_crt_t
389 g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls)
390 {
391   return gnutls->priv->cert;
392 }
393
394 const gnutls_x509_privkey_t
395 g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls)
396 {
397   return gnutls->priv->key;
398 }
399
400 gnutls_x509_crt_t
401 g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls)
402 {
403   gnutls_x509_crt_t cert;
404   gnutls_datum data;
405   size_t size;
406
407   size = 0;
408   gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
409                           NULL, &size);
410   data.data = g_malloc (size);
411   data.size = size;
412   gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER,
413                           data.data, &size);
414
415   gnutls_x509_crt_init (&cert);
416   gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER);
417   g_free (data.data);
418
419   return cert;
420 }
421
422 gnutls_x509_privkey_t
423 g_tls_certificate_gnutls_copy_key  (GTlsCertificateGnutls *gnutls)
424 {
425   gnutls_x509_privkey_t key;
426
427   gnutls_x509_privkey_init (&key);
428   gnutls_x509_privkey_cpy (key, gnutls->priv->key);
429   return key;
430 }
431
432 static const struct {
433   int gnutls_flag;
434   GTlsCertificateFlags gtls_flag;
435 } flags_map[] = {
436   { GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA, G_TLS_CERTIFICATE_UNKNOWN_CA },
437   { GNUTLS_CERT_NOT_ACTIVATED, G_TLS_CERTIFICATE_NOT_ACTIVATED },
438   { GNUTLS_CERT_EXPIRED, G_TLS_CERTIFICATE_EXPIRED },
439   { GNUTLS_CERT_REVOKED, G_TLS_CERTIFICATE_REVOKED },
440   { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE }
441 };
442 static const int flags_map_size = G_N_ELEMENTS (flags_map);
443
444 GTlsCertificateFlags
445 g_tls_certificate_gnutls_convert_flags (guint gnutls_flags)
446 {
447   int i;
448   GTlsCertificateFlags gtls_flags;
449
450   /* Convert GNUTLS status to GTlsCertificateFlags. GNUTLS sets
451    * GNUTLS_CERT_INVALID if it sets any other flag, so we want to
452    * strip that out unless it's the only flag set. Then we convert
453    * specific flags we recognize, and if there are any flags left over
454    * at the end, we add G_TLS_CERTIFICATE_GENERIC_ERROR.
455    */
456   gtls_flags = 0;
457
458   if (gnutls_flags != GNUTLS_CERT_INVALID)
459     gnutls_flags = gnutls_flags & ~GNUTLS_CERT_INVALID;
460   for (i = 0; i < flags_map_size && gnutls_flags != 0; i++)
461     {
462       if (gnutls_flags & flags_map[i].gnutls_flag)
463         {
464           gnutls_flags &= ~flags_map[i].gnutls_flag;
465           gtls_flags |= flags_map[i].gtls_flag;
466         }
467     }
468   if (gnutls_flags)
469     gtls_flags |= G_TLS_CERTIFICATE_GENERIC_ERROR;
470
471   return gtls_flags;
472 }
473
474 GTlsCertificateFlags
475 g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
476                                           GSocketConnectable    *identity)
477 {
478   const char *hostname;
479
480   if (G_IS_NETWORK_ADDRESS (identity))
481     hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
482   else if (G_IS_NETWORK_SERVICE (identity))
483     hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
484   else
485     hostname = NULL;
486
487   if (hostname)
488     {
489       if (gnutls_x509_crt_check_hostname (gnutls->priv->cert, hostname))
490         return 0;
491     }
492
493   /* FIXME: check sRVName and uniformResourceIdentifier
494    * subjectAltNames, if appropriate for @identity.
495    */
496
497   return G_TLS_CERTIFICATE_BAD_IDENTITY;
498 }