From: Dan Winship Date: Wed, 1 Dec 2010 00:45:39 +0000 (-0500) Subject: gnutls: Update for GTlsCertificate changes, implement verify X-Git-Tag: 2.27.5~12 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=4264b02ac1e2e30aacb8511ccc2a99a60be14062;p=platform%2Fupstream%2Fglib-networking.git gnutls: Update for GTlsCertificate changes, implement verify --- diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c index 93580c2..29e992f 100644 --- a/tls/gnutls/gtlscertificate-gnutls.c +++ b/tls/gnutls/gtlscertificate-gnutls.c @@ -27,20 +27,7 @@ #include "gtlscertificate-gnutls.h" #include -static void g_tls_certificate_gnutls_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void g_tls_certificate_gnutls_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void g_tls_certificate_gnutls_finalize (GObject *object); - static void g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface); -static gboolean g_tls_certificate_gnutls_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error); G_DEFINE_TYPE_WITH_CODE (GTlsCertificateGnutls, g_tls_certificate_gnutls, G_TYPE_TLS_CERTIFICATE, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, @@ -53,7 +40,8 @@ enum PROP_CERTIFICATE, PROP_CERTIFICATE_PEM, PROP_PRIVATE_KEY, - PROP_PRIVATE_KEY_PEM + PROP_PRIVATE_KEY_PEM, + PROP_ISSUER }; struct _GTlsCertificateGnutlsPrivate @@ -61,6 +49,8 @@ struct _GTlsCertificateGnutlsPrivate gnutls_x509_crt_t cert; gnutls_x509_privkey_t key; + GTlsCertificateGnutls *issuer; + GError *construct_error; guint have_cert : 1; @@ -68,23 +58,6 @@ struct _GTlsCertificateGnutlsPrivate }; static void -g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_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; - - 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"); - g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem"); -} - -static void g_tls_certificate_gnutls_finalize (GObject *object) { GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object); @@ -92,6 +65,9 @@ g_tls_certificate_gnutls_finalize (GObject *object) gnutls_x509_crt_deinit (gnutls->priv->cert); gnutls_x509_privkey_deinit (gnutls->priv->key); + if (gnutls->priv->issuer) + g_object_unref (gnutls->priv->issuer); + g_clear_error (&gnutls->priv->construct_error); G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object); @@ -156,6 +132,10 @@ g_tls_certificate_gnutls_get_property (GObject *object, g_value_take_string (value, certificate_pem); break; + case PROP_ISSUER: + g_value_set_object (value, gnutls->priv->issuer); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -256,6 +236,10 @@ g_tls_certificate_gnutls_set_property (GObject *object, } break; + case PROP_ISSUER: + gnutls->priv->issuer = g_value_dup_object (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -272,12 +256,6 @@ g_tls_certificate_gnutls_init (GTlsCertificateGnutls *gnutls) gnutls_x509_privkey_init (&gnutls->priv->key); } -static void -g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface) -{ - iface->init = g_tls_certificate_gnutls_initable_init; -} - static gboolean g_tls_certificate_gnutls_initable_init (GInitable *initable, GCancellable *cancellable, @@ -301,6 +279,82 @@ g_tls_certificate_gnutls_initable_init (GInitable *initable, return TRUE; } +static GTlsCertificateFlags +g_tls_certificate_gnutls_verify (GTlsCertificate *cert, + GSocketConnectable *identity, + GTlsCertificate *trusted_ca) +{ + GTlsCertificateGnutls *cert_gnutls; + int status; + guint gnutls_flags, num_certs, i, num_cas; + gnutls_x509_crt_t *chain, ca; + GTlsCertificateFlags gtls_flags; + + cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert); + for (num_certs = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer) + num_certs++; + 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; + + if (trusted_ca) + { + cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (trusted_ca); + ca = cert_gnutls->priv->cert; + num_cas = 1; + } + else + { + ca = NULL; + num_cas = 0; + } + + status = gnutls_x509_crt_list_verify (chain, num_certs, + &ca, num_cas, + NULL, 0, + GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT, + &gnutls_flags); + g_free (chain); + + if (status != 0) + return G_TLS_CERTIFICATE_GENERIC_ERROR; + + gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags); + + if (identity) + gtls_flags |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (cert), identity); + + return gtls_flags; +} + +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; + + 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"); + g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem"); + g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer"); +} + +static void +g_tls_certificate_gnutls_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_tls_certificate_gnutls_initable_init; +} + GTlsCertificate * g_tls_certificate_gnutls_new (const gnutls_datum *datum, GTlsCertificate *issuer) @@ -360,3 +414,71 @@ g_tls_certificate_gnutls_copy_key (GTlsCertificateGnutls *gnutls) gnutls_x509_privkey_cpy (key, gnutls->priv->key); return key; } + +static const struct { + int gnutls_flag; + GTlsCertificateFlags gtls_flag; +} flags_map[] = { + { GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA, G_TLS_CERTIFICATE_UNKNOWN_CA }, + { 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 } +}; +static const int flags_map_size = G_N_ELEMENTS (flags_map); + +GTlsCertificateFlags +g_tls_certificate_gnutls_convert_flags (guint gnutls_flags) +{ + int i; + GTlsCertificateFlags gtls_flags; + + /* Convert GNUTLS status to GTlsCertificateFlags. GNUTLS sets + * GNUTLS_CERT_INVALID if it sets any other flag, so we want to + * strip that out unless it's the only flag set. Then we convert + * specific flags we recognize, and if there are any flags left over + * at the end, we add G_TLS_CERTIFICATE_GENERIC_ERROR. + */ + gtls_flags = 0; + + if (gnutls_flags != GNUTLS_CERT_INVALID) + gnutls_flags = gnutls_flags & ~GNUTLS_CERT_INVALID; + 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; + } + } + if (gnutls_flags) + gtls_flags |= G_TLS_CERTIFICATE_GENERIC_ERROR; + + return gtls_flags; +} + +GTlsCertificateFlags +g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls, + GSocketConnectable *identity) +{ + 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 + hostname = NULL; + + if (hostname) + { + if (gnutls_x509_crt_check_hostname (gnutls->priv->cert, hostname)) + return 0; + } + + /* FIXME: check sRVName and uniformResourceIdentifier + * subjectAltNames, if appropriate for @identity. + */ + + return G_TLS_CERTIFICATE_BAD_IDENTITY; +} diff --git a/tls/gnutls/gtlscertificate-gnutls.h b/tls/gnutls/gtlscertificate-gnutls.h index 32f9058..5e2cc70 100644 --- a/tls/gnutls/gtlscertificate-gnutls.h +++ b/tls/gnutls/gtlscertificate-gnutls.h @@ -41,14 +41,20 @@ struct _GTlsCertificateGnutls GType g_tls_certificate_gnutls_get_type (void) G_GNUC_CONST; -GTlsCertificate * g_tls_certificate_gnutls_new (const gnutls_datum *datum, - GTlsCertificate *issuer); +GTlsCertificate * g_tls_certificate_gnutls_new (const gnutls_datum *datum, + GTlsCertificate *issuer); -const gnutls_x509_crt_t g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls); -const gnutls_x509_privkey_t g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls); +const gnutls_x509_crt_t g_tls_certificate_gnutls_get_cert (GTlsCertificateGnutls *gnutls); +const gnutls_x509_privkey_t g_tls_certificate_gnutls_get_key (GTlsCertificateGnutls *gnutls); + +gnutls_x509_crt_t g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls); +gnutls_x509_privkey_t g_tls_certificate_gnutls_copy_key (GTlsCertificateGnutls *gnutls); + +GTlsCertificateFlags g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls, + GSocketConnectable *identity); + +GTlsCertificateFlags g_tls_certificate_gnutls_convert_flags (guint gnutls_flags); -gnutls_x509_crt_t g_tls_certificate_gnutls_copy_cert (GTlsCertificateGnutls *gnutls); -gnutls_x509_privkey_t g_tls_certificate_gnutls_copy_key (GTlsCertificateGnutls *gnutls); G_END_DECLS diff --git a/tls/gnutls/gtlsclientconnection-gnutls.c b/tls/gnutls/gtlsclientconnection-gnutls.c index ebe871f..b4f1b38 100644 --- a/tls/gnutls/gtlsclientconnection-gnutls.c +++ b/tls/gnutls/gtlsclientconnection-gnutls.c @@ -27,6 +27,7 @@ #include #include "gtlsclientconnection-gnutls.h" +#include "gtlscertificate-gnutls.h" #include enum @@ -164,6 +165,7 @@ g_tls_client_connection_gnutls_set_property (GObject *object, GParamSpec *pspec) { GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (object); + const char *hostname; switch (prop_id) { @@ -177,11 +179,16 @@ g_tls_client_connection_gnutls_set_property (GObject *object, gnutls->priv->server_identity = g_value_dup_object (value); if (G_IS_NETWORK_ADDRESS (gnutls->priv->server_identity)) + hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (gnutls->priv->server_identity)); + else if (G_IS_NETWORK_SERVICE (gnutls->priv->server_identity)) + hostname = g_network_service_get_domain (G_NETWORK_SERVICE (gnutls->priv->server_identity)); + else + hostname = NULL; + + if (hostname) { - const char *hostname; gnutls_session_t session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (gnutls)); - hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (gnutls->priv->server_identity)); gnutls_server_name_set (session, GNUTLS_NAME_DNS, hostname, strlen (hostname)); } @@ -233,41 +240,24 @@ g_tls_client_connection_gnutls_retrieve_function (gnutls_session_t s static gboolean validate_handshake (GTlsClientConnectionGnutls *gnutls) { + GTlsCertificate *peer; GTlsCertificateFlags errors; gboolean accepted; + peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (gnutls)); + errors = g_tls_connection_gnutls_validate_peer (G_TLS_CONNECTION_GNUTLS (gnutls)); - /* FIXME: implement the full hostname/servicename/URI check - * according to draft-saintandre-tls-server-id-check - */ if ((gnutls->priv->validation_flags & G_TLS_CERTIFICATE_BAD_IDENTITY) && - gnutls->priv->server_identity && - G_IS_NETWORK_ADDRESS (gnutls->priv->server_identity)) + gnutls->priv->server_identity) { - gnutls_session session; - gnutls_x509_crt x509_cert; - const gnutls_datum_t *certs; - const char *hostname; - unsigned int num_certs; - - session = g_tls_connection_gnutls_get_session (G_TLS_CONNECTION_GNUTLS (gnutls)); - hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (gnutls->priv->server_identity)); - - gnutls_x509_crt_init (&x509_cert); - certs = gnutls_certificate_get_peers (session, &num_certs); - gnutls_x509_crt_import (x509_cert, &certs[0], GNUTLS_X509_FMT_DER); - if (!gnutls_x509_crt_check_hostname (x509_cert, hostname)) - errors |= G_TLS_CERTIFICATE_BAD_IDENTITY; - gnutls_x509_crt_deinit (x509_cert); + errors |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (peer), + gnutls->priv->server_identity); } errors &= gnutls->priv->validation_flags; if (errors) - { - GTlsCertificate *peer = g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (gnutls)); - accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls), peer, errors); - } + accepted = g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls), peer, errors); else accepted = TRUE; diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c index 22bf34c..75685f8 100644 --- a/tls/gnutls/gtlsconnection-gnutls.c +++ b/tls/gnutls/gtlsconnection-gnutls.c @@ -397,48 +397,13 @@ g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls, st->ncerts = 0; } -static const struct { - int gnutls_flag; - GTlsCertificateFlags gtls_flag; -} flags_map[] = { - { GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA, G_TLS_CERTIFICATE_UNKNOWN_CA }, - { 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 } -}; -static const int flags_map_size = G_N_ELEMENTS (flags_map); - GTlsCertificateFlags g_tls_connection_gnutls_validate_peer (GTlsConnectionGnutls *gnutls) { - int status, i; - GTlsCertificateFlags gtls_errors; + int status; status = gnutls_certificate_verify_peers (gnutls->priv->session); - - /* Convert GNUTLS status to GTlsCertificateFlags. GNUTLS sets - * GNUTLS_CERT_INVALID if it sets any other flag, so we want to - * strip that out unless it's the only flag set. Then we convert - * specific flags we recognize, and if there are any flags left over - * at the end, we add G_TLS_CERTIFICATE_GENERIC_ERROR. - */ - gtls_errors = 0; - - if (status != GNUTLS_CERT_INVALID) - status = status & ~GNUTLS_CERT_INVALID; - for (i = 0; i < flags_map_size && status != 0; i++) - { - if (status & flags_map[i].gnutls_flag) - { - status &= ~flags_map[i].gnutls_flag; - gtls_errors |= flags_map[i].gtls_flag; - } - } - if (status) - gtls_errors |= G_TLS_CERTIFICATE_GENERIC_ERROR; - - return gtls_errors; + return g_tls_certificate_gnutls_convert_flags (status); } static void