[gcr/cert-distrusted: 107/107] Add lookup of trust assertions for distrusted certs




commit c61fd4fe606b8dfd5a5c0001ace848f192a53626
Author: Niels De Graef <nielsdegraef gmail com>
Date:   Wed Sep 1 23:15:13 2021 +0200

    Add lookup of trust assertions for distrusted certs
    
    There is one notable type of trust assertions that is not implemented in
    gcr: those where `CK_X_ASSERTION_TYPE` equals
    `CKT_X_DISTRUSTED_CERTIFICATE`.
    
    This is actually also something needed by Seahorse, as that is showing
    the infamous "null" certificates due to distrusted certificates being
    present on the machine, but not having any DER data stored with them.

 docs/gcr/trust-store.md |   8 +++
 gcr/gcr-trust.c         | 186 ++++++++++++++++++++++++++++++++++++++++++++++++
 gcr/gcr-trust.h         |  18 +++++
 gcr/test-trust.c        |  86 ++++++++++++++++++++++
 4 files changed, 298 insertions(+)
---
diff --git a/docs/gcr/trust-store.md b/docs/gcr/trust-store.md
index 1c97ec23..baeabe7e 100644
--- a/docs/gcr/trust-store.md
+++ b/docs/gcr/trust-store.md
@@ -36,3 +36,11 @@ After the user has requested to override the trust decision about a given
 certificate then a pinned certificates can be added by using the
 [func@Gcr.trust_add_pinned_certificate] function, or
 [func@Gcr.trust_add_pinned_certificate_async] for the asynchronous version.
+
+Distrusted certificates
+------------------------
+Certificates can be marked _distrusted_, either by manual action of the user, or
+by Certificate Authorities (CAs) that add them in a Certificate Revocation List
+(CRL) or other means. To check if a certificate is distrusted, one can use
+[func@Gcr.trust_is_certificate_distrusted], or
+[func@Gcr.trust_is_certificate_distrusted_async] for the asynchronous version.
diff --git a/gcr/gcr-trust.c b/gcr/gcr-trust.c
index c6ac4dee..9fae5b5f 100644
--- a/gcr/gcr-trust.c
+++ b/gcr/gcr-trust.c
@@ -783,3 +783,189 @@ gcr_trust_is_certificate_anchored_finish (GAsyncResult *result, GError **error)
 
        return g_task_propagate_boolean (G_TASK (result), error);
 }
+
+/* ----------------------------------------------------------------------------------
+ * DISTRUSTED CERTIFICATE
+ */
+
+static GckAttributes *
+prepare_is_certificate_distrusted (unsigned char *serial_nr,
+                                   size_t         serial_nr_len,
+                                   unsigned char *issuer,
+                                   size_t         issuer_len)
+{
+    GckBuilder builder = GCK_BUILDER_INIT;
+
+    gck_builder_add_ulong (&builder, CKA_CLASS, CKO_X_TRUST_ASSERTION);
+    gck_builder_add_ulong (&builder, CKA_X_ASSERTION_TYPE, CKT_X_DISTRUSTED_CERTIFICATE);
+    gck_builder_add_data (&builder, CKA_SERIAL_NUMBER, serial_nr, serial_nr_len);
+    gck_builder_add_data (&builder, CKA_ISSUER, issuer, issuer_len);
+
+    return gck_builder_end (&builder);
+}
+
+static gboolean
+perform_is_certificate_distrusted (GckAttributes *attrs,
+                                   GCancellable  *cancellable,
+                                   GError       **error)
+{
+    GckEnumerator *en;
+    GList *slots;
+    GckObject *object;
+
+    if (!gcr_pkcs11_initialize (cancellable, error))
+        return FALSE;
+
+    slots = gcr_pkcs11_get_trust_lookup_slots ();
+    g_debug ("searching for certificate distrust assertion in %d slots",
+             g_list_length (slots));
+    en = gck_slots_enumerate_objects (slots, attrs, 0);
+    gck_list_unref_free (slots);
+
+    object = gck_enumerator_next (en, cancellable, error);
+    g_object_unref (en);
+
+    if (object != NULL)
+        g_object_unref (object);
+
+    g_debug ("%s certificate distrust", object ? "found" : "did not find");
+    return (object != NULL);
+}
+
+/**
+ * gcr_trust_is_certificate_distrusted:
+ * @serial_nr: (array length=serial_nr_len): The serial number of the certificate
+ * @serial_nr_len: The nr of bytes in @serial_nr
+ * @issuer: (array length=issuer_len): The raw issuer
+ * @issuer_len: The nr of bytes in @issuer
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Checks whether the certificate that can be uniquely identified with the
+ * given @serial_nr and @issuer is marked as distrusted (for example by the
+ * user, or because it's part of a CRL).
+ *
+ * Since we can't directly use [iface@Certificate] to fetch these values, you
+ * need to call these with the raw serial number and issuer as provided by the
+ * PKCS#11 fields `CKA_SERIAL_NR` and `CKA_ISSUER`.
+ *
+ * Returns: %TRUE if the certificate is marked as distrusted
+ */
+gboolean
+gcr_trust_is_certificate_distrusted (unsigned char *serial_nr,
+                                     size_t         serial_nr_len,
+                                     unsigned char *issuer,
+                                     size_t         issuer_len,
+                                     GCancellable  *cancellable,
+                                     GError       **error)
+{
+    GckAttributes *search;
+    gboolean ret;
+
+    g_return_val_if_fail (serial_nr, FALSE);
+    g_return_val_if_fail (serial_nr_len > 0, FALSE);
+    g_return_val_if_fail (issuer, FALSE);
+    g_return_val_if_fail (issuer_len > 0, FALSE);
+    g_return_val_if_fail (G_IS_CANCELLABLE (cancellable) || !cancellable, FALSE);
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+    search = prepare_is_certificate_distrusted (serial_nr, serial_nr_len, issuer, issuer_len);
+    g_return_val_if_fail (search, FALSE);
+
+    ret = perform_is_certificate_distrusted (search, cancellable, error);
+    gck_attributes_unref (search);
+
+    return ret;
+}
+
+static void
+thread_is_certificate_distrusted (GTask        *task,
+                                  void         *object,
+                                  void         *task_data,
+                                  GCancellable *cancellable)
+{
+    GckAttributes *attrs = task_data;
+    GError *error = NULL;
+    gboolean found;
+
+    found = perform_is_certificate_distrusted (attrs, cancellable, &error);
+    if (error == NULL)
+        g_task_return_boolean (task, found);
+    else
+        g_task_return_error (task, g_steal_pointer (&error));
+}
+
+/**
+ * gcr_trust_is_certificate_distrusted_async:
+ * @serial_nr: (array length=serial_nr_len): The serial number of the certificate
+ * @serial_nr_len: The nr of bytes in @serial_nr
+ * @issuer: (array length=issuer_len): The raw issuer
+ * @issuer_len: The number of bytes in @issuer
+ * @cancellable: (nullable): a #GCancellable or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the operation completes
+ * @user_data: the data to pass to callback function
+ *
+ * Asynchronously checks whether the certificate that can be uniquely
+ * identified with the given @serial_nr and @issuer is marked as distrusted
+ * (for example by the user, or because it's part of a CRL).
+ *
+ * Since we can't directly use [iface@Certificate] to fetch these values, you
+ * need to call these with the raw serial number and issuer as provided by the
+ * PKCS#11 fields `CKA_SERIAL_NR` and `CKA_ISSUER`.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * [func@trust_is_certificate_distrusted_finish] to get the result of the
+ * operation.
+ */
+void
+gcr_trust_is_certificate_distrusted_async (unsigned char      *serial_nr,
+                                           size_t              serial_nr_len,
+                                           unsigned char      *issuer,
+                                           size_t              issuer_len,
+                                           GCancellable       *cancellable,
+                                           GAsyncReadyCallback callback,
+                                           void               *user_data)
+{
+    GTask *task;
+    GckAttributes *attrs;
+
+    g_return_if_fail (serial_nr);
+    g_return_if_fail (serial_nr_len > 0);
+    g_return_if_fail (issuer);
+    g_return_if_fail (issuer_len > 0);
+    g_return_if_fail (G_IS_CANCELLABLE (cancellable) || !cancellable);
+
+    task = g_task_new (NULL, cancellable, callback, user_data);
+    g_task_set_source_tag (task, gcr_trust_is_certificate_distrusted_async);
+
+    attrs = prepare_is_certificate_distrusted (serial_nr, serial_nr_len, issuer, issuer_len);
+    g_return_if_fail (attrs);
+    g_task_set_task_data (task, attrs, gck_attributes_unref);
+
+    g_task_run_in_thread (task, thread_is_certificate_distrusted);
+
+    g_clear_object (&task);
+}
+
+/**
+ * gcr_trust_is_certificate_distrusted_finish:
+ * @result: the #GAsyncResult passed to the callback
+ * @error: a #GError, or %NULL
+ *
+ * Finishes an asynchronous operation started by
+ * [func@trust_is_certificate_distrusted_async].
+ *
+ * In the case of an error, %FALSE is also returned. Check @error to detect
+ * if an error occurred.
+ *
+ * Returns: %TRUE if the certificate is a trust anchor
+ */
+gboolean
+gcr_trust_is_certificate_distrusted_finish (GAsyncResult *result,
+                                            GError      **error)
+{
+    g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+    g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
+
+    return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/gcr/gcr-trust.h b/gcr/gcr-trust.h
index 88b407c6..0f490d6a 100644
--- a/gcr/gcr-trust.h
+++ b/gcr/gcr-trust.h
@@ -96,6 +96,24 @@ void           gcr_trust_is_certificate_anchored_async         (GcrCertificate *
 gboolean       gcr_trust_is_certificate_anchored_finish        (GAsyncResult *result,
                                                                 GError **error);
 
+gboolean       gcr_trust_is_certificate_distrusted             (unsigned char *serial_nr,
+                                                                size_t         serial_nr_len,
+                                                                unsigned char *issuer,
+                                                                size_t         issuer_len,
+                                                                GCancellable  *cancellable,
+                                                                GError       **error);
+
+void           gcr_trust_is_certificate_distrusted_async      (unsigned char      *serial_nr,
+                                                               size_t              serial_nr_len,
+                                                               unsigned char      *issuer,
+                                                               size_t              issuer_len,
+                                                               GCancellable       *cancellable,
+                                                               GAsyncReadyCallback callback,
+                                                               void               *user_data);
+
+gboolean       gcr_trust_is_certificate_distrusted_finish     (GAsyncResult *result,
+                                                               GError      **error);
+
 G_END_DECLS
 
 #endif /* __GCR_TOKEN_MANAGER_H__ */
diff --git a/gcr/test-trust.c b/gcr/test-trust.c
index 08e4a359..46466122 100644
--- a/gcr/test-trust.c
+++ b/gcr/test-trust.c
@@ -305,6 +305,89 @@ test_is_certificate_anchored_async (Test *test, gconstpointer unused)
        g_object_unref (result);
 }
 
+static void
+test_is_certificate_distrusted_not (Test *test, gconstpointer unused)
+{
+       guchar *serial_nr = NULL;
+       size_t serial_nr_len;
+       guchar *issuer = NULL;
+       size_t issuer_len;
+       GError *error = NULL;
+       gboolean ret;
+
+       serial_nr = gcr_certificate_get_serial_number (test->certificate, &serial_nr_len);
+       issuer = gcr_certificate_get_issuer_raw (test->certificate, &issuer_len);
+
+       ret = gcr_trust_is_certificate_distrusted (serial_nr, serial_nr_len,
+                                                  issuer, issuer_len,
+                                                  NULL, &error);
+       g_assert_false (ret);
+       g_assert_no_error (error);
+
+       g_free (issuer);
+       g_free (serial_nr);
+}
+
+static void
+test_is_certificate_distrusted_yes (Test *test, gconstpointer unused)
+{
+       GckBuilder builder = GCK_BUILDER_INIT;
+       guchar *serial_nr = NULL;
+       size_t serial_nr_len;
+       guchar *issuer = NULL;
+       size_t issuer_len;
+       GError *error = NULL;
+       gboolean ret;
+
+       serial_nr = gcr_certificate_get_serial_number (test->certificate, &serial_nr_len);
+       issuer = gcr_certificate_get_issuer_raw (test->certificate, &issuer_len);
+
+       /* Create "distrusted certificate" trust assertion */
+       gck_builder_add_ulong (&builder, CKA_CLASS, CKO_X_TRUST_ASSERTION);
+       gck_builder_add_ulong (&builder, CKA_X_ASSERTION_TYPE, CKT_X_DISTRUSTED_CERTIFICATE);
+        gck_builder_add_data (&builder, CKA_SERIAL_NUMBER, serial_nr, serial_nr_len);
+        gck_builder_add_data (&builder, CKA_ISSUER, issuer, issuer_len);
+       gck_mock_module_add_object (gck_builder_end (&builder));
+
+       ret = gcr_trust_is_certificate_distrusted (serial_nr, serial_nr_len,
+                                                  issuer, issuer_len,
+                                                  NULL, &error);
+       g_assert_true (ret);
+       g_assert_no_error (error);
+
+       g_free (issuer);
+       g_free (serial_nr);
+}
+
+static void
+test_is_certificate_distrusted_async (Test *test, gconstpointer unused)
+{
+       guchar *serial_nr = NULL;
+       size_t serial_nr_len;
+       guchar *issuer = NULL;
+       size_t issuer_len;
+       GAsyncResult *result = NULL;
+       GError *error = NULL;
+       gboolean ret;
+
+       serial_nr = gcr_certificate_get_serial_number (test->certificate, &serial_nr_len);
+       issuer = gcr_certificate_get_issuer_raw (test->certificate, &issuer_len);
+
+       gcr_trust_is_certificate_distrusted_async (serial_nr, serial_nr_len,
+                                                  issuer, issuer_len,
+                                                  NULL, fetch_async_result, &result);
+       egg_test_wait_until (500);
+       g_assert_nonnull (result);
+
+       ret = gcr_trust_is_certificate_distrusted_finish (result, &error);
+       g_assert_false (ret);
+       g_assert_no_error (error);
+
+       g_free (issuer);
+       g_free (serial_nr);
+       g_object_unref (result);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -320,6 +403,9 @@ main (int argc, char **argv)
        g_test_add ("/gcr/trust/is_certificate_anchored_not", Test, NULL, setup, 
test_is_certificate_anchored_not, teardown);
        g_test_add ("/gcr/trust/is_certificate_anchored_yes", Test, NULL, setup, 
test_is_certificate_anchored_yes, teardown);
        g_test_add ("/gcr/trust/is_certificate_anchored_async", Test, NULL, setup, 
test_is_certificate_anchored_async, teardown);
+       g_test_add ("/gcr/trust/is_certificate_distrusted_not", Test, NULL, setup, 
test_is_certificate_distrusted_not, teardown);
+       g_test_add ("/gcr/trust/is_certificate_distrusted_yes", Test, NULL, setup, 
test_is_certificate_distrusted_yes, teardown);
+       g_test_add ("/gcr/trust/is_certificate_distrusted_async", Test, NULL, setup, 
test_is_certificate_distrusted_async, teardown);
 
        return egg_tests_run_with_loop ();
 }


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