X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=tls%2Fgnutls%2Fgtlscertificate-gnutls.c;h=cc309365df0cc8f6b69d0a1db646e991c574151a;hb=refs%2Ftags%2Fsubmit%2Ftizen_6.0_hotfix%2F20201102.192901;hp=30497e68953e3a00a057b90e53f27bda16048b30;hpb=ee6ed146ee6cabf8c64ea3389fbaa5fea4ff2008;p=platform%2Fupstream%2Fglib-networking.git diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c old mode 100644 new mode 100755 index 30497e6..cc30936 --- a/tls/gnutls/gtlscertificate-gnutls.c +++ b/tls/gnutls/gtlscertificate-gnutls.c @@ -1,11 +1,13 @@ -/* GIO - GLib Input, Output and Streaming Library +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * GIO - GLib Input, Output and Streaming Library * * Copyright 2009 Red Hat, Inc * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -15,6 +17,9 @@ * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see * . + * + * In addition, when the library is used with OpenSSL, a special + * exception applies. Refer to the LICENSE_EXCEPTION file for details. */ #include "config.h" @@ -25,12 +30,7 @@ #include "gtlscertificate-gnutls.h" #include - -static void g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface); - -G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - g_tls_certificate_gnutls_initable_iface_init);) +#include "TIZEN.h" enum { @@ -43,8 +43,10 @@ enum PROP_ISSUER }; -struct _GTlsCertificateGnutlsPrivate +struct _GTlsCertificateGnutls { + GTlsCertificate parent_instance; + gnutls_x509_crt_t cert; gnutls_x509_privkey_t key; @@ -56,28 +58,32 @@ struct _GTlsCertificateGnutlsPrivate guint have_key : 1; }; +static void g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_tls_certificate_gnutls_initable_iface_init);) + static void g_tls_certificate_gnutls_finalize (GObject *object) { GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object); - gnutls_x509_crt_deinit (gnutls->priv->cert); - if (gnutls->priv->key) - gnutls_x509_privkey_deinit (gnutls->priv->key); + g_clear_pointer (&gnutls->cert, gnutls_x509_crt_deinit); + g_clear_pointer (&gnutls->key, gnutls_x509_privkey_deinit); - if (gnutls->priv->issuer) - g_object_unref (gnutls->priv->issuer); + g_clear_object (&gnutls->issuer); - g_clear_error (&gnutls->priv->construct_error); + g_clear_error (&gnutls->construct_error); G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object); } static void g_tls_certificate_gnutls_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) + guint prop_id, + GValue *value, + GParamSpec *pspec) { GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object); GByteArray *certificate; @@ -89,51 +95,51 @@ g_tls_certificate_gnutls_get_property (GObject *object, { case PROP_CERTIFICATE: size = 0; - status = gnutls_x509_crt_export (gnutls->priv->cert, - GNUTLS_X509_FMT_DER, - NULL, &size); + status = gnutls_x509_crt_export (gnutls->cert, + GNUTLS_X509_FMT_DER, + NULL, &size); if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) - certificate = NULL; + certificate = NULL; else - { - certificate = g_byte_array_sized_new (size); - certificate->len = size; - status = gnutls_x509_crt_export (gnutls->priv->cert, - GNUTLS_X509_FMT_DER, - certificate->data, &size); - if (status != 0) - { - g_byte_array_free (certificate, TRUE); - certificate = NULL; - } - } + { + certificate = g_byte_array_sized_new (size); + certificate->len = size; + status = gnutls_x509_crt_export (gnutls->cert, + GNUTLS_X509_FMT_DER, + certificate->data, &size); + if (status != 0) + { + g_byte_array_free (certificate, TRUE); + certificate = NULL; + } + } g_value_take_boxed (value, certificate); break; case PROP_CERTIFICATE_PEM: size = 0; - status = gnutls_x509_crt_export (gnutls->priv->cert, - GNUTLS_X509_FMT_PEM, - NULL, &size); + status = gnutls_x509_crt_export (gnutls->cert, + GNUTLS_X509_FMT_PEM, + NULL, &size); if (status != GNUTLS_E_SHORT_MEMORY_BUFFER) - certificate_pem = NULL; + certificate_pem = NULL; else - { - certificate_pem = g_malloc (size); - status = gnutls_x509_crt_export (gnutls->priv->cert, - GNUTLS_X509_FMT_PEM, - certificate_pem, &size); - if (status != 0) - { - g_free (certificate_pem); - certificate_pem = NULL; - } - } + { + certificate_pem = g_malloc (size); + status = gnutls_x509_crt_export (gnutls->cert, + GNUTLS_X509_FMT_PEM, + certificate_pem, &size); + if (status != 0) + { + g_free (certificate_pem); + certificate_pem = NULL; + } + } g_value_take_string (value, certificate_pem); break; case PROP_ISSUER: - g_value_set_object (value, gnutls->priv->issuer); + g_value_set_object (value, gnutls->issuer); break; default: @@ -143,9 +149,9 @@ g_tls_certificate_gnutls_get_property (GObject *object, static void g_tls_certificate_gnutls_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) + guint prop_id, + const GValue *value, + GParamSpec *pspec) { GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object); GByteArray *bytes; @@ -158,108 +164,108 @@ g_tls_certificate_gnutls_set_property (GObject *object, case PROP_CERTIFICATE: bytes = g_value_get_boxed (value); if (!bytes) - break; - g_return_if_fail (gnutls->priv->have_cert == FALSE); + break; + g_return_if_fail (gnutls->have_cert == FALSE); data.data = bytes->data; data.size = bytes->len; - status = gnutls_x509_crt_import (gnutls->priv->cert, &data, - GNUTLS_X509_FMT_DER); + status = gnutls_x509_crt_import (gnutls->cert, &data, + GNUTLS_X509_FMT_DER); if (status == 0) - gnutls->priv->have_cert = TRUE; - else if (!gnutls->priv->construct_error) - { - gnutls->priv->construct_error = - g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, - _("Could not parse DER certificate: %s"), - gnutls_strerror (status)); - } + gnutls->have_cert = TRUE; + else if (!gnutls->construct_error) + { + gnutls->construct_error = + g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + _("Could not parse DER certificate: %s"), + gnutls_strerror (status)); + } break; case PROP_CERTIFICATE_PEM: string = g_value_get_string (value); if (!string) - break; - g_return_if_fail (gnutls->priv->have_cert == FALSE); + break; + g_return_if_fail (gnutls->have_cert == FALSE); data.data = (void *)string; data.size = strlen (string); - status = gnutls_x509_crt_import (gnutls->priv->cert, &data, - GNUTLS_X509_FMT_PEM); + status = gnutls_x509_crt_import (gnutls->cert, &data, + GNUTLS_X509_FMT_PEM); if (status == 0) - gnutls->priv->have_cert = TRUE; - else if (!gnutls->priv->construct_error) - { - gnutls->priv->construct_error = - g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, - _("Could not parse PEM certificate: %s"), - gnutls_strerror (status)); - } + gnutls->have_cert = TRUE; + else if (!gnutls->construct_error) + { + gnutls->construct_error = + g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + _("Could not parse PEM certificate: %s"), + gnutls_strerror (status)); + } break; case PROP_PRIVATE_KEY: bytes = g_value_get_boxed (value); if (!bytes) - break; - g_return_if_fail (gnutls->priv->have_key == FALSE); + break; + g_return_if_fail (gnutls->have_key == FALSE); data.data = bytes->data; data.size = bytes->len; - if (!gnutls->priv->key) - gnutls_x509_privkey_init (&gnutls->priv->key); - status = gnutls_x509_privkey_import (gnutls->priv->key, &data, - GNUTLS_X509_FMT_DER); + if (!gnutls->key) + gnutls_x509_privkey_init (&gnutls->key); + status = gnutls_x509_privkey_import (gnutls->key, &data, + GNUTLS_X509_FMT_DER); if (status != 0) - { - int pkcs8_status = - gnutls_x509_privkey_import_pkcs8 (gnutls->priv->key, &data, - GNUTLS_X509_FMT_DER, NULL, - GNUTLS_PKCS_PLAIN); - if (pkcs8_status == 0) - status = 0; - } + { + int pkcs8_status = + gnutls_x509_privkey_import_pkcs8 (gnutls->key, &data, + GNUTLS_X509_FMT_DER, NULL, + GNUTLS_PKCS_PLAIN); + if (pkcs8_status == 0) + status = 0; + } if (status == 0) - gnutls->priv->have_key = TRUE; - else if (!gnutls->priv->construct_error) - { - gnutls->priv->construct_error = - g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, - _("Could not parse DER private key: %s"), - gnutls_strerror (status)); - } + gnutls->have_key = TRUE; + else if (!gnutls->construct_error) + { + gnutls->construct_error = + g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + _("Could not parse DER private key: %s"), + gnutls_strerror (status)); + } break; case PROP_PRIVATE_KEY_PEM: string = g_value_get_string (value); if (!string) - break; - g_return_if_fail (gnutls->priv->have_key == FALSE); + break; + g_return_if_fail (gnutls->have_key == FALSE); data.data = (void *)string; data.size = strlen (string); - if (!gnutls->priv->key) - gnutls_x509_privkey_init (&gnutls->priv->key); - status = gnutls_x509_privkey_import (gnutls->priv->key, &data, - GNUTLS_X509_FMT_PEM); + if (!gnutls->key) + gnutls_x509_privkey_init (&gnutls->key); + status = gnutls_x509_privkey_import (gnutls->key, &data, + GNUTLS_X509_FMT_PEM); if (status != 0) - { - int pkcs8_status = - gnutls_x509_privkey_import_pkcs8 (gnutls->priv->key, &data, - GNUTLS_X509_FMT_PEM, NULL, - GNUTLS_PKCS_PLAIN); - if (pkcs8_status == 0) - status = 0; - } + { + int pkcs8_status = + gnutls_x509_privkey_import_pkcs8 (gnutls->key, &data, + GNUTLS_X509_FMT_PEM, NULL, + GNUTLS_PKCS_PLAIN); + if (pkcs8_status == 0) + status = 0; + } if (status == 0) - gnutls->priv->have_key = TRUE; - else if (!gnutls->priv->construct_error) - { - gnutls->priv->construct_error = - g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, - _("Could not parse PEM private key: %s"), - gnutls_strerror (status)); - } + gnutls->have_key = TRUE; + else if (!gnutls->construct_error) + { + gnutls->construct_error = + g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, + _("Could not parse PEM private key: %s"), + gnutls_strerror (status)); + } break; case PROP_ISSUER: - gnutls->priv->issuer = g_value_dup_object (value); + gnutls->issuer = g_value_dup_object (value); break; default: @@ -267,33 +273,42 @@ g_tls_certificate_gnutls_set_property (GObject *object, } } +#if ENABLE(TIZEN_TV_ADJUST_TIME) +extern double soupTimeOffset; + +static time_t +correct_time_func(time_t *t) +{ + return time(NULL) + (time_t)(soupTimeOffset / 1000); +} +#endif + static void g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls) { - gnutls->priv = G_TYPE_INSTANCE_GET_PRIVATE (gnutls, - G_TYPE_TLS_CERTIFICATE_GNUTLS, - GTlsCertificateGnutlsPrivate); - - gnutls_x509_crt_init (&gnutls->priv->cert); + gnutls_x509_crt_init (&gnutls->cert); +#if ENABLE(TIZEN_TV_ADJUST_TIME) + gnutls_global_set_time_function(correct_time_func); +#endif } static gboolean g_tls_certificate_gnutls_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) + GCancellable *cancellable, + GError **error) { GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (initable); - if (gnutls->priv->construct_error) + if (gnutls->construct_error) { - g_propagate_error (error, gnutls->priv->construct_error); - gnutls->priv->construct_error = NULL; + g_propagate_error (error, gnutls->construct_error); + gnutls->construct_error = NULL; return FALSE; } - else if (!gnutls->priv->have_cert) + else if (!gnutls->have_cert) { g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE, - _("No certificate data provided")); + _("No certificate data provided")); return FALSE; } else @@ -302,22 +317,31 @@ g_tls_certificate_gnutls_initable_init (GInitable *initable, static GTlsCertificateFlags g_tls_certificate_gnutls_verify (GTlsCertificate *cert, - GSocketConnectable *identity, - GTlsCertificate *trusted_ca) + GSocketConnectable *identity, + GTlsCertificate *trusted_ca) { GTlsCertificateGnutls *cert_gnutls; guint num_certs, i; gnutls_x509_crt_t *chain; GTlsCertificateFlags gtls_flags; - time_t t, now; - + cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert); - for (num_certs = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer) - num_certs++; + num_certs = 0; + do + { + cert_gnutls = cert_gnutls->issuer; + num_certs++; + } + while (cert_gnutls); + chain = g_new (gnutls_x509_crt_t, num_certs); cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert); - for (i = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer, i++) - chain[i] = cert_gnutls->priv->cert; + for (i = 0; i < num_certs; i++) + { + chain[i] = cert_gnutls->cert; + cert_gnutls = cert_gnutls->issuer; + } + g_assert (!cert_gnutls); if (trusted_ca) { @@ -325,38 +349,22 @@ g_tls_certificate_gnutls_verify (GTlsCertificate *cert, guint gnutls_flags; int status; - ca = G_TLS_CERTIFICATE_GNUTLS (trusted_ca)->priv->cert; + ca = G_TLS_CERTIFICATE_GNUTLS (trusted_ca)->cert; status = gnutls_x509_crt_list_verify (chain, num_certs, - &ca, 1, - NULL, 0, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, - &gnutls_flags); + &ca, 1, + NULL, 0, 0, + &gnutls_flags); if (status != 0) - { - g_free (chain); - return G_TLS_CERTIFICATE_GENERIC_ERROR; - } + { + g_free (chain); + return G_TLS_CERTIFICATE_GENERIC_ERROR; + } gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags); } else gtls_flags = 0; - /* We have to check these ourselves since gnutls_x509_crt_list_verify - * won't bother if it gets an UNKNOWN_CA. - */ - now = time (NULL); - for (i = 0; i < num_certs; i++) - { - t = gnutls_x509_crt_get_activation_time (chain[i]); - if (t == (time_t) -1 || t > now) - gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED; - - t = gnutls_x509_crt_get_expiration_time (chain[i]); - if (t == (time_t) -1 || t < now) - gtls_flags |= G_TLS_CERTIFICATE_EXPIRED; - } - g_free (chain); if (identity) @@ -366,55 +374,17 @@ g_tls_certificate_gnutls_verify (GTlsCertificate *cert, } static void -g_tls_certificate_gnutls_real_copy (GTlsCertificateGnutls *gnutls, - const gchar *interaction_id, - gnutls_retr2_st *st) -{ - gnutls_x509_crt_t cert; - gnutls_datum data; - size_t size = 0; - - gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER, - NULL, &size); - data.data = g_malloc (size); - data.size = size; - gnutls_x509_crt_export (gnutls->priv->cert, GNUTLS_X509_FMT_DER, - data.data, &size); - - gnutls_x509_crt_init (&cert); - gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER); - g_free (data.data); - - st->ncerts = 1; - st->cert.x509 = gnutls_malloc (sizeof (gnutls_x509_crt_t)); - st->cert.x509[0] = cert; - - if (gnutls->priv->key != NULL) - { - gnutls_x509_privkey_init (&st->key.x509); - gnutls_x509_privkey_cpy (st->key.x509, gnutls->priv->key); - st->key_type = GNUTLS_PRIVKEY_X509; - } - - st->deinit_all = TRUE; -} - -static void g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GTlsCertificateClass *certificate_class = G_TLS_CERTIFICATE_CLASS (klass); - g_type_class_add_private (klass, sizeof (GTlsCertificateGnutlsPrivate)); - gobject_class->get_property = g_tls_certificate_gnutls_get_property; gobject_class->set_property = g_tls_certificate_gnutls_set_property; gobject_class->finalize = g_tls_certificate_gnutls_finalize; certificate_class->verify = g_tls_certificate_gnutls_verify; - klass->copy = g_tls_certificate_gnutls_real_copy; - g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate"); g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem"); g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key"); @@ -429,14 +399,14 @@ g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface) } GTlsCertificate * -g_tls_certificate_gnutls_new (const gnutls_datum *datum, - GTlsCertificate *issuer) +g_tls_certificate_gnutls_new (const gnutls_datum_t *datum, + GTlsCertificate *issuer) { GTlsCertificateGnutls *gnutls; gnutls = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS, - "issuer", issuer, - NULL); + "issuer", issuer, + NULL); g_tls_certificate_gnutls_set_data (gnutls, datum); return G_TLS_CERTIFICATE (gnutls); @@ -444,37 +414,111 @@ g_tls_certificate_gnutls_new (const gnutls_datum *datum, void g_tls_certificate_gnutls_set_data (GTlsCertificateGnutls *gnutls, - const gnutls_datum *datum) + const gnutls_datum_t *datum) { g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls)); - g_return_if_fail (!gnutls->priv->have_cert); + g_return_if_fail (!gnutls->have_cert); - if (gnutls_x509_crt_import (gnutls->priv->cert, datum, + if (gnutls_x509_crt_import (gnutls->cert, datum, GNUTLS_X509_FMT_DER) == 0) - gnutls->priv->have_cert = TRUE; + gnutls->have_cert = TRUE; } const gnutls_x509_crt_t g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls) { - return gnutls->priv->cert; + return gnutls->cert; } gboolean g_tls_certificate_gnutls_has_key (GTlsCertificateGnutls *gnutls) { - return gnutls->priv->have_key; + return gnutls->have_key; } void -g_tls_certificate_gnutls_copy (GTlsCertificateGnutls *gnutls, - const gchar *interaction_id, - gnutls_retr2_st *st) +g_tls_certificate_gnutls_copy (GTlsCertificateGnutls *gnutls, + const gchar *interaction_id, + gnutls_pcert_st **pcert, + unsigned int *pcert_length, + gnutls_privkey_t *pkey) { + GTlsCertificateGnutls *chain; + guint num_certs = 0; + int status; + g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls)); - g_return_if_fail (st != NULL); - g_return_if_fail (G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy); - G_TLS_CERTIFICATE_GNUTLS_GET_CLASS (gnutls)->copy (gnutls, interaction_id, st); + g_return_if_fail (pcert != NULL); + g_return_if_fail (pcert_length != NULL); + g_return_if_fail (pkey != NULL); + + /* We will do this loop twice. It's probably more efficient than + * re-allocating memory. + */ + chain = gnutls; + while (chain != NULL) + { + num_certs++; + chain = chain->issuer; + } + + *pcert_length = 0; + *pcert = g_malloc (sizeof (gnutls_pcert_st) * num_certs); + + /* Now do the actual copy of the whole chain. */ + chain = gnutls; + while (chain != NULL) + { + gnutls_x509_crt_t cert; + gnutls_datum_t data; + + gnutls_x509_crt_export2 (chain->cert, GNUTLS_X509_FMT_DER, &data); + + gnutls_x509_crt_init (&cert); + status = gnutls_x509_crt_import (cert, &data, GNUTLS_X509_FMT_DER); + g_warn_if_fail (status == 0); + gnutls_free (data.data); + + gnutls_pcert_import_x509 (*pcert + *pcert_length, cert, 0); + gnutls_x509_crt_deinit (cert); + (*pcert_length)++; + + chain = chain->issuer; + } + + if (gnutls->key != NULL) + { + gnutls_x509_privkey_t x509_privkey; + gnutls_privkey_t privkey; + + gnutls_x509_privkey_init (&x509_privkey); + gnutls_x509_privkey_cpy (x509_privkey, gnutls->key); + + gnutls_privkey_init (&privkey); + gnutls_privkey_import_x509 (privkey, x509_privkey, GNUTLS_PRIVKEY_IMPORT_COPY); + *pkey = privkey; + gnutls_x509_privkey_deinit (x509_privkey); + } + else + { + *pkey = NULL; + } +} + +void +g_tls_certificate_gnutls_copy_free (gnutls_pcert_st *pcert, + unsigned int pcert_length, + gnutls_privkey_t pkey) +{ + if (pcert != NULL) + { + for (unsigned int i = 0; i < pcert_length; i++) + gnutls_pcert_deinit (&pcert[i]); + g_free (pcert); + } + + if (pkey != NULL) + gnutls_privkey_deinit (pkey); } static const struct { @@ -485,7 +529,8 @@ static const struct { { GNUTLS_CERT_NOT_ACTIVATED, G_TLS_CERTIFICATE_NOT_ACTIVATED }, { GNUTLS_CERT_EXPIRED, G_TLS_CERTIFICATE_EXPIRED }, { GNUTLS_CERT_REVOKED, G_TLS_CERTIFICATE_REVOKED }, - { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE } + { GNUTLS_CERT_INSECURE_ALGORITHM, G_TLS_CERTIFICATE_INSECURE }, + { GNUTLS_CERT_UNEXPECTED_OWNER, G_TLS_CERTIFICATE_BAD_IDENTITY } }; static const int flags_map_size = G_N_ELEMENTS (flags_map); @@ -508,10 +553,10 @@ g_tls_certificate_gnutls_convert_flags (guint gnutls_flags) for (i = 0; i < flags_map_size && gnutls_flags != 0; i++) { if (gnutls_flags & flags_map[i].gnutls_flag) - { - gnutls_flags &= ~flags_map[i].gnutls_flag; - gtls_flags |= flags_map[i].gtls_flag; - } + { + gnutls_flags &= ~flags_map[i].gnutls_flag; + gtls_flags |= flags_map[i].gtls_flag; + } } if (gnutls_flags) gtls_flags |= G_TLS_CERTIFICATE_GENERIC_ERROR; @@ -519,9 +564,9 @@ g_tls_certificate_gnutls_convert_flags (guint gnutls_flags) return gtls_flags; } -GTlsCertificateFlags -g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls, - GSocketConnectable *identity) +static gboolean +verify_identity_hostname (GTlsCertificateGnutls *gnutls, + GSocketConnectable *identity) { const char *hostname; @@ -530,18 +575,75 @@ g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls, else if (G_IS_NETWORK_SERVICE (identity)) hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity)); else - hostname = NULL; + return FALSE; + + return gnutls_x509_crt_check_hostname (gnutls->cert, hostname); +} - if (hostname) +static gboolean +verify_identity_ip (GTlsCertificateGnutls *gnutls, + GSocketConnectable *identity) +{ + GInetAddress *addr; + int i, ret = 0; + gsize addr_size; + const guint8 *addr_bytes; + + if (G_IS_INET_SOCKET_ADDRESS (identity)) + addr = g_object_ref (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity))); + else { + const char *hostname; + + if (G_IS_NETWORK_ADDRESS (identity)) + hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity)); + else if (G_IS_NETWORK_SERVICE (identity)) + hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity)); + else + return FALSE; + + addr = g_inet_address_new_from_string (hostname); + if (!addr) + return FALSE; + } + + addr_bytes = g_inet_address_to_bytes (addr); + addr_size = g_inet_address_get_native_size (addr); + + for (i = 0; ret >= 0; i++) { - if (gnutls_x509_crt_check_hostname (gnutls->priv->cert, hostname)) - return 0; + char san[500]; + size_t san_size; + + san_size = sizeof (san); + ret = gnutls_x509_crt_get_subject_alt_name (gnutls->cert, i, + san, &san_size, NULL); + + if ((ret == GNUTLS_SAN_IPADDRESS) && (addr_size == san_size)) + { + if (memcmp (addr_bytes, san, addr_size) == 0) + { + g_object_unref (addr); + return TRUE; + } + } } + g_object_unref (addr); + return FALSE; +} + +GTlsCertificateFlags +g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls, + GSocketConnectable *identity) +{ + if (verify_identity_hostname (gnutls, identity)) + return 0; + else if (verify_identity_ip (gnutls, identity)) + return 0; + /* FIXME: check sRVName and uniformResourceIdentifier * subjectAltNames, if appropriate for @identity. */ - return G_TLS_CERTIFICATE_BAD_IDENTITY; } @@ -554,8 +656,121 @@ g_tls_certificate_gnutls_set_issuer (GTlsCertificateGnutls *gnutls, if (issuer) g_object_ref (issuer); - if (gnutls->priv->issuer) - g_object_unref (gnutls->priv->issuer); - gnutls->priv->issuer = issuer; + if (gnutls->issuer) + g_object_unref (gnutls->issuer); + gnutls->issuer = issuer; g_object_notify (G_OBJECT (gnutls), "issuer"); } + +GBytes * +g_tls_certificate_gnutls_get_bytes (GTlsCertificateGnutls *gnutls) +{ + GByteArray *array; + + g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls), NULL); + + g_object_get (gnutls, "certificate", &array, NULL); + return g_byte_array_free_to_bytes (array); +} + +static gnutls_x509_crt_t * +convert_data_to_gnutls_certs (const gnutls_datum_t *certs, + guint num_certs, + gnutls_x509_crt_fmt_t format) +{ + gnutls_x509_crt_t *gnutls_certs; + guint i; + + gnutls_certs = g_new (gnutls_x509_crt_t, num_certs); + + for (i = 0; i < num_certs; i++) + { + if (gnutls_x509_crt_init (&gnutls_certs[i]) < 0) + { + i--; + goto error; + } + } + + for (i = 0; i < num_certs; i++) + { + if (gnutls_x509_crt_import (gnutls_certs[i], &certs[i], format) < 0) + { + i = num_certs - 1; + goto error; + } + } + + return gnutls_certs; + +error: + for (; i != G_MAXUINT; i--) + gnutls_x509_crt_deinit (gnutls_certs[i]); + g_free (gnutls_certs); + return NULL; +} + +GTlsCertificateGnutls * +g_tls_certificate_gnutls_build_chain (const gnutls_datum_t *certs, + guint num_certs, + gnutls_x509_crt_fmt_t format) +{ + GPtrArray *glib_certs; + gnutls_x509_crt_t *gnutls_certs; + GTlsCertificateGnutls *issuer; + GTlsCertificateGnutls *result; + guint i, j; + + g_return_val_if_fail (certs, NULL); + + gnutls_certs = convert_data_to_gnutls_certs (certs, num_certs, format); + if (!gnutls_certs) + return NULL; + + glib_certs = g_ptr_array_new_full (num_certs, g_object_unref); + for (i = 0; i < num_certs; i++) + g_ptr_array_add (glib_certs, g_tls_certificate_gnutls_new (&certs[i], NULL)); + + /* Some servers send certs out of order, or will send duplicate + * certs, so we need to be careful when assigning the issuer of + * our new GTlsCertificateGnutls. + */ + for (i = 0; i < num_certs; i++) + { + issuer = NULL; + + /* Check if the cert issued itself */ + if (gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[i])) + continue; + + if (i < num_certs - 1 && + gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[i + 1])) + { + issuer = glib_certs->pdata[i + 1]; + } + else + { + for (j = 0; j < num_certs; j++) + { + if (j != i && + gnutls_x509_crt_check_issuer (gnutls_certs[i], gnutls_certs[j])) + { + issuer = glib_certs->pdata[j]; + break; + } + } + } + + if (issuer) + g_tls_certificate_gnutls_set_issuer (glib_certs->pdata[i], issuer); + } + + result = g_object_ref (glib_certs->pdata[0]); + g_ptr_array_unref (glib_certs); + + for (i = 0; i < num_certs; i++) + gnutls_x509_crt_deinit (gnutls_certs[i]); + g_free (gnutls_certs); + + return result; +}