[glib-networking] tls: expose SAN details on GTlsCertificate



commit e12c82f74464ae098f7279e895f84d6769d3a48d
Author: Ross A. Wollman <ross wollman gmail com>
Date:   Tue Jun 8 19:25:29 2021 +0000

    tls: expose SAN details on GTlsCertificate
    
    This changeset exposes
    
    * `dns-names`
    * `ip-addresses`
    
    For more information, see related glib MR:
    https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2142.

 tls/gnutls/gtlscertificate-gnutls.c   | 66 +++++++++++++++++++++++++++++++++++
 tls/openssl/gtlscertificate-openssl.c | 61 ++++++++++++++++++++++++++++++++
 tls/tests/certificate.c               | 47 +++++++++++++++++++++++++
 3 files changed, 174 insertions(+)
---
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index 4c27587..5de6eb5 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -46,6 +46,8 @@ enum
   PROP_NOT_VALID_AFTER,
   PROP_SUBJECT_NAME,
   PROP_ISSUER_NAME,
+  PROP_DNS_NAMES,
+  PROP_IP_ADDRESSES,
 };
 
 struct _GTlsCertificateGnutls
@@ -90,6 +92,60 @@ g_tls_certificate_gnutls_finalize (GObject *object)
   G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object);
 }
 
+static GPtrArray *
+get_subject_alt_names (GTlsCertificateGnutls          *cert,
+                       gnutls_x509_subject_alt_name_t  type)
+{
+  GPtrArray *data = NULL;
+  guint8 *san = NULL;
+  size_t san_size;
+  guint san_type;
+  guint critical;
+  guint i;
+  guint status;
+
+  if (type == GNUTLS_SAN_IPADDRESS)
+    data = g_ptr_array_new_with_free_func (g_object_unref);
+  else
+    data = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
+
+  for (i = 0; ; i++)
+  {
+    san_size = 0;
+    san = NULL;
+    status = gnutls_x509_crt_get_subject_alt_name2 (cert->cert, i, san, &san_size, &san_type, &critical);
+    if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+      return data;
+    else if (san_type != (guint)type)
+      continue;
+
+    if (san_size == 0)
+      continue;
+
+    san = g_malloc (san_size);
+    status = gnutls_x509_crt_get_subject_alt_name2 (cert->cert, i, san, &san_size, &san_type, &critical);
+    if (status == (guint)type)
+      {
+        if (status == (guint)GNUTLS_SAN_IPADDRESS)
+          {
+            if (san_size == 4)
+              g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV4));
+            else if (san_size == 16)
+              g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV6));
+          }
+        else
+          {
+            g_assert (status == (guint)GNUTLS_SAN_DNSNAME);
+            g_ptr_array_add (data, g_bytes_new (san, san_size));
+          }
+      }
+
+    g_free (san);
+  }
+
+  return data;
+}
+
 static void
 g_tls_certificate_gnutls_get_property (GObject    *object,
                                        guint       prop_id,
@@ -202,6 +258,14 @@ g_tls_certificate_gnutls_get_property (GObject    *object,
       gnutls_free (data.data);
       break;
 
+    case PROP_DNS_NAMES:
+      g_value_take_boxed (value, get_subject_alt_names (gnutls, GNUTLS_SAN_DNSNAME));
+      break;
+
+    case PROP_IP_ADDRESSES:
+      g_value_take_boxed (value, get_subject_alt_names (gnutls, GNUTLS_SAN_IPADDRESS));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -460,6 +524,8 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
   g_object_class_override_property (gobject_class, PROP_NOT_VALID_AFTER, "not-valid-after");
   g_object_class_override_property (gobject_class, PROP_SUBJECT_NAME, "subject-name");
   g_object_class_override_property (gobject_class, PROP_ISSUER_NAME, "issuer-name");
+  g_object_class_override_property (gobject_class, PROP_DNS_NAMES, "dns-names");
+  g_object_class_override_property (gobject_class, PROP_IP_ADDRESSES, "ip-addresses");
 }
 
 static void
diff --git a/tls/openssl/gtlscertificate-openssl.c b/tls/openssl/gtlscertificate-openssl.c
index cbb8f82..2ed753f 100644
--- a/tls/openssl/gtlscertificate-openssl.c
+++ b/tls/openssl/gtlscertificate-openssl.c
@@ -60,6 +60,8 @@ enum
   PROP_NOT_VALID_AFTER,
   PROP_SUBJECT_NAME,
   PROP_ISSUER_NAME,
+  PROP_DNS_NAMES,
+  PROP_IP_ADDRESSES,
 };
 
 static void     g_tls_certificate_openssl_initable_iface_init (GInitableIface  *iface);
@@ -85,6 +87,55 @@ g_tls_certificate_openssl_finalize (GObject *object)
   G_OBJECT_CLASS (g_tls_certificate_openssl_parent_class)->finalize (object);
 }
 
+static GPtrArray *
+get_subject_alt_names (GTlsCertificateOpenssl *cert,
+                       guint                   type)
+{
+  GPtrArray *data = NULL;
+  STACK_OF (GENERAL_NAME) *sans;
+  const guint8 *san = NULL;
+  size_t san_size;
+  guint alt_occurrences;
+  guint i;
+
+  if (type == GEN_IPADD)
+    data = g_ptr_array_new_with_free_func (g_object_unref);
+  else
+    data = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
+
+  sans = X509_get_ext_d2i (cert->cert, NID_subject_alt_name, NULL, NULL);
+  if (sans)
+    {
+      alt_occurrences = sk_GENERAL_NAME_num (sans);
+      for (i = 0; i < alt_occurrences; i++)
+        {
+          const GENERAL_NAME *value = sk_GENERAL_NAME_value (sans, i);
+          if (value->type != type)
+            continue;
+
+          if (type == GEN_IPADD)
+            {
+              g_assert (value->type == GEN_IPADD);
+              san = ASN1_STRING_get0_data (value->d.ip);
+              san_size = ASN1_STRING_length (value->d.ip);
+              if (san_size == 4)
+                g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV4));
+              else if (san_size == 16)
+                g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV6));
+            }
+          else
+            {
+              g_assert (value->type == GEN_DNS);
+              san = ASN1_STRING_get0_data (value->d.ia5);
+              san_size = ASN1_STRING_length (value->d.ia5);
+              g_ptr_array_add (data, g_bytes_new (san, san_size));
+            }
+          }
+    }
+
+  return data;
+}
+
 static void
 g_tls_certificate_openssl_get_property (GObject    *object,
                                         guint       prop_id,
@@ -183,6 +234,14 @@ g_tls_certificate_openssl_get_property (GObject    *object,
       BIO_free_all (bio);
       break;
 
+    case PROP_DNS_NAMES:
+      g_value_take_boxed (value, get_subject_alt_names (openssl, GEN_DNS));
+      break;
+
+    case PROP_IP_ADDRESSES:
+      g_value_take_boxed (value, get_subject_alt_names (openssl, GEN_IPADD));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -412,6 +471,8 @@ g_tls_certificate_openssl_class_init (GTlsCertificateOpensslClass *klass)
   g_object_class_override_property (gobject_class, PROP_NOT_VALID_AFTER, "not-valid-after");
   g_object_class_override_property (gobject_class, PROP_SUBJECT_NAME, "subject-name");
   g_object_class_override_property (gobject_class, PROP_ISSUER_NAME, "issuer-name");
+  g_object_class_override_property (gobject_class, PROP_DNS_NAMES, "dns-names");
+  g_object_class_override_property (gobject_class, PROP_IP_ADDRESSES, "ip-addresses");
 }
 
 static void
diff --git a/tls/tests/certificate.c b/tls/tests/certificate.c
index 50b7dc3..32ba66c 100644
--- a/tls/tests/certificate.c
+++ b/tls/tests/certificate.c
@@ -658,6 +658,51 @@ test_certificate_issuer_name (void)
   g_object_unref (cert);
 }
 
+static void
+test_certificate_dns_names (void)
+{
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  GPtrArray *actual;
+  const gchar *dns_name = "server.example.com";
+  GBytes *expected = g_bytes_new_static (dns_name, strlen (dns_name));
+
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("server.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert);
+
+  actual = g_tls_certificate_get_dns_names (cert);
+  g_assert_nonnull (actual);
+  g_assert_cmpuint (actual->len, ==, 1);
+  g_assert_true (g_ptr_array_find_with_equal_func (actual, expected, (GEqualFunc)g_bytes_equal, NULL));
+
+  g_ptr_array_free (actual, FALSE);
+  g_bytes_unref (expected);
+  g_object_unref (cert);
+}
+
+static void
+test_certificate_ip_addresses (void)
+{
+  GTlsCertificate *cert;
+  GError *error = NULL;
+  GPtrArray *actual;
+  GInetAddress *expected = g_inet_address_new_from_string ("192.168.1.10");
+
+  cert = g_tls_certificate_new_from_file (tls_test_file_path ("server.pem"), &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (cert);
+
+  actual = g_tls_certificate_get_ip_addresses (cert);
+  g_assert_nonnull (actual);
+  g_assert_cmpuint (actual->len, ==, 1);
+  g_assert_true (g_ptr_array_find_with_equal_func (actual, expected, (GEqualFunc)g_inet_address_equal, 
NULL));
+
+  g_ptr_array_free (actual, TRUE);
+  g_object_unref (expected);
+  g_object_unref (cert);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -720,6 +765,8 @@ main (int   argc,
   g_test_add_func ("/tls/" BACKEND "/certificate/not-valid-after", test_certificate_not_valid_after);
   g_test_add_func ("/tls/" BACKEND "/certificate/subject-name", test_certificate_subject_name);
   g_test_add_func ("/tls/" BACKEND "/certificate/issuer-name", test_certificate_issuer_name);
+  g_test_add_func ("/tls/" BACKEND "/certificate/dns-names", test_certificate_dns_names);
+  g_test_add_func ("/tls/" BACKEND "/certificate/ip-addresses", test_certificate_ip_addresses);
 
   return g_test_run();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]