summaryrefslogtreecommitdiff
path: root/gcr
diff options
context:
space:
mode:
authorNiels De Graef <nielsdegraef@gmail.com>2021-09-01 23:15:13 +0200
committerNiels De Graef <nielsdegraef@gmail.com>2022-06-06 09:24:55 +0200
commitc61fd4fe606b8dfd5a5c0001ace848f192a53626 (patch)
tree26979a879fb418adf0a75063e4305eb9a11d809f /gcr
parent242f31eda7e5aed4948f078bb3d5fc3344df7281 (diff)
downloadgcr-c61fd4fe606b8dfd5a5c0001ace848f192a53626.tar.gz
Add lookup of trust assertions for distrusted certscert-distrusted
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.
Diffstat (limited to 'gcr')
-rw-r--r--gcr/gcr-trust.c186
-rw-r--r--gcr/gcr-trust.h18
-rw-r--r--gcr/test-trust.c86
3 files changed, 290 insertions, 0 deletions
diff --git a/gcr/gcr-trust.c b/gcr/gcr-trust.c
index c6ac4de..9fae5b5 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 88b407c..0f490d6 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 08e4a35..4646612 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 ();
}