fwupd/libfwupdplugin/fu-security-attrs.c
Richard Hughes f58ac7316c hsi: Abstract out the list of FwupdSecurityAttr objects for plugins
This exports FuSecurityAttrs into libfwupdplugin so that we can pass the plugins
this object rather than a 'bare' GPtrArray. This greatly simplifies the object
ownership, and also allows us to check the object type before adding.

In the future we could also check for duplicate appstream IDs or missing
properties at insertion time.

This change also changes the fu_plugin_add_security_attrs() to not return an
error. This forces the plugin to handle the error, storing the failure in the
attribute itself.

Only the plugin know if a missing file it needs to read indicates a runtime
problem or a simple failure to obtain a specific HSI level.
2020-05-12 16:47:24 +01:00

242 lines
6.6 KiB
C

/*
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <glib/gi18n.h>
#include "fu-security-attrs-private.h"
struct _FuSecurityAttrs {
GObject parent_instance;
GPtrArray *attrs;
};
G_DEFINE_TYPE (FuSecurityAttrs, fu_security_attrs, G_TYPE_OBJECT)
static void
fu_security_attrs_finalize (GObject *obj)
{
FuSecurityAttrs *self = FU_SECURITY_ATTRS (obj);
g_ptr_array_unref (self->attrs);
G_OBJECT_CLASS (fu_security_attrs_parent_class)->finalize (obj);
}
static void
fu_security_attrs_class_init (FuSecurityAttrsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fu_security_attrs_finalize;
}
static void
fu_security_attrs_init (FuSecurityAttrs *self)
{
self->attrs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
}
/**
* fu_security_attrs_append:
* @self: A #FuSecurityAttrs
* @attr: a #FwupdSecurityAttr
*
* Adds a #FwupdSecurityAttr to the array.
*
* Since: 1.5.0
**/
void
fu_security_attrs_append (FuSecurityAttrs *self, FwupdSecurityAttr *attr)
{
g_return_if_fail (FU_IS_SECURITY_ATTRS (self));
g_return_if_fail (FWUPD_IS_SECURITY_ATTR (attr));
g_ptr_array_add (self->attrs, g_object_ref (attr));
}
/**
* fu_security_attrs_to_variant:
* @self: A #FuSecurityAttrs
*
* Converts the #FwupdSecurityAttr objects into a variant array.
*
* Returns: a #GVariant or %NULL
*
* Since: 1.5.0
**/
GVariant *
fu_security_attrs_to_variant (FuSecurityAttrs *self)
{
GVariantBuilder builder;
g_return_val_if_fail (FU_IS_SECURITY_ATTRS (self), NULL);
g_return_val_if_fail (self->attrs->len > 0, NULL);
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
for (guint i = 0; i < self->attrs->len; i++) {
FwupdSecurityAttr *security_attr = g_ptr_array_index (self->attrs, i);
GVariant *tmp = fwupd_security_attr_to_variant (security_attr);
g_variant_builder_add_value (&builder, tmp);
}
return g_variant_new ("(aa{sv})", &builder);
}
/**
* fu_security_attrs_get_all:
* @self: A #FuSecurityAttrs
*
* Gets all the attributes in the object.
*
* Returns: (transfer container) (element-type FwupdSecurityAttr): attributes
*
* Since: 1.5.0
**/
GPtrArray *
fu_security_attrs_get_all (FuSecurityAttrs *self)
{
g_return_val_if_fail (FU_IS_SECURITY_ATTRS (self), NULL);
return g_ptr_array_ref (self->attrs);
}
/**
* fu_security_attrs_calculate_hsi:
* @self: A #FuSecurityAttrs
*
* Calculates the HSI string from the appended attribues.
*
* Returns: (transfer full): a string or %NULL
*
* Since: 1.5.0
**/
gchar *
fu_security_attrs_calculate_hsi (FuSecurityAttrs *self)
{
guint hsi_number = 0;
FwupdSecurityAttrFlags flags = FWUPD_SECURITY_ATTR_FLAG_NONE;
GString *str = g_string_new ("HSI:");
const FwupdSecurityAttrFlags hpi_suffixes[] = {
FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES,
FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION,
FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE,
FWUPD_SECURITY_ATTR_FLAG_NONE,
};
g_return_val_if_fail (FU_IS_SECURITY_ATTRS (self), NULL);
/* find the highest HSI number where there are no failures and at least
* one success */
for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) {
gboolean success_cnt = 0;
gboolean failure_cnt = 0;
for (guint i = 0; i < self->attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i);
if (fwupd_security_attr_get_level (attr) != j)
continue;
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
success_cnt++;
else if (!fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
failure_cnt++;
}
/* abort */
if (failure_cnt > 0) {
hsi_number = j - 1;
break;
}
/* we matched at least one thing on this level */
if (success_cnt > 0)
hsi_number = j;
}
/* get a logical OR of the runtime flags */
for (guint i = 0; i < self->attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i);
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
continue;
/* positive things */
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES) ||
fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION)) {
if (!fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
continue;
}
/* negative things */
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE)) {
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS))
continue;
}
flags |= fwupd_security_attr_get_flags (attr);
}
g_string_append_printf (str, "%u", hsi_number);
if (flags & (FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES |
FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION |
FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE)) {
g_string_append (str, "+");
for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) {
if (flags & hpi_suffixes[j])
g_string_append (str, fwupd_security_attr_flag_to_suffix (hpi_suffixes[j]));
}
}
return g_string_free (str, FALSE);
}
/**
* fu_security_attrs_depsolve:
* @self: A #FuSecurityAttrs
*
* Marks any attributes with %FWUPD_SECURITY_ATTR_FLAG_OBSOLETED that have been
* defined as obsoleted by other attributes.
*
* It is only required to call this function once, and should be done when all
* attributes have been added.
*
* Since: 1.5.0
**/
void
fu_security_attrs_depsolve (FuSecurityAttrs *self)
{
g_autoptr(GHashTable) attrs_by_id = NULL;
g_return_if_fail (FU_IS_SECURITY_ATTRS (self));
/* make hash of ID -> object */
attrs_by_id = g_hash_table_new (g_str_hash, g_str_equal);
for (guint i = 0; i < self->attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i);
g_hash_table_insert (attrs_by_id,
(gpointer) fwupd_security_attr_get_appstream_id (attr),
(gpointer) attr);
}
/* set flat where required */
for (guint i = 0; i < self->attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (self->attrs, i);
GPtrArray *obsoletes = fwupd_security_attr_get_obsoletes (attr);
for (guint j = 0; j < obsoletes->len; j++) {
const gchar *obsolete = g_ptr_array_index (obsoletes, j);
FwupdSecurityAttr *attr_tmp = g_hash_table_lookup (attrs_by_id, obsolete);
if (attr_tmp != NULL) {
g_debug ("security attr %s obsoleted by %s", obsolete,
fwupd_security_attr_get_appstream_id (attr));
fwupd_security_attr_add_flag (attr_tmp,
FWUPD_SECURITY_ATTR_FLAG_OBSOLETED);
}
}
}
}
/**
* fu_security_attrs_new:
*
* Returns: a #FuSecurityAttrs
*
* Since: 1.5.0
**/
FuSecurityAttrs *
fu_security_attrs_new (void)
{
return g_object_new (FU_TYPE_SECURITY_ATTRS, NULL);
}