Add support for the Host Security ID

The HSI specification assigns a simple text ID to the current state of firmware
security. As new vulnerabilities are found, and as protection measures are
updated, new requirements will be added to the required firmware behaviours for
each HSI value.

The HSI specification is currently incomplete and in active development, and
so the --force flag is required in all command line tools. The current ID value
will probably change on a given platform so please do not start using the result
for any kind of compliance requirements.
This commit is contained in:
Richard Hughes 2020-05-11 19:42:47 +01:00
parent c1407ed14f
commit 196c6c69db
23 changed files with 689 additions and 1 deletions

View File

@ -2,6 +2,7 @@ _fwupdagent_cmd_list=(
'get-devices'
'get-updates'
'get-upgrades'
'security'
)
_fwupdagent_opts=(

View File

@ -22,6 +22,7 @@ _fwupdmgr_cmd_list=(
'reinstall'
'refresh'
'report-history'
'security'
'set-approved-firmware'
'unlock'
'update'

View File

@ -20,6 +20,7 @@ _fwupdtool_cmd_list=(
'install-blob'
'monitor'
'reinstall'
'security'
'self-sign'
'smbios-dump'
'attach'

View File

@ -23,6 +23,7 @@
#include "fwupd-enums.h"
#include "fwupd-error.h"
#include "fwupd-device-private.h"
#include "fwupd-security-attr-private.h"
#include "fwupd-release-private.h"
#include "fwupd-remote-private.h"
@ -45,6 +46,7 @@ typedef struct {
gchar *daemon_version;
gchar *host_product;
gchar *host_machine_id;
gchar *host_security_id;
GDBusConnection *conn;
GDBusProxy *proxy;
} FwupdClientPrivate;
@ -66,6 +68,7 @@ enum {
PROP_TAINTED,
PROP_HOST_PRODUCT,
PROP_HOST_MACHINE_ID,
PROP_HOST_SECURITY_ID,
PROP_INTERACTIVE,
PROP_LAST
};
@ -128,6 +131,15 @@ fwupd_client_set_host_machine_id (FwupdClient *client, const gchar *host_machine
g_object_notify (G_OBJECT (client), "host-machine-id");
}
static void
fwupd_client_set_host_security_id (FwupdClient *client, const gchar *host_security_id)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_free (priv->host_security_id);
priv->host_security_id = g_strdup (host_security_id);
g_object_notify (G_OBJECT (client), "host-security-id");
}
static void
fwupd_client_set_daemon_version (FwupdClient *client, const gchar *daemon_version)
{
@ -201,6 +213,12 @@ fwupd_client_properties_changed_cb (GDBusProxy *proxy,
if (val != NULL)
fwupd_client_set_host_machine_id (client, g_variant_get_string (val, NULL));
}
if (g_variant_dict_contains (dict, "HostSecurityId")) {
g_autoptr(GVariant) val = NULL;
val = g_dbus_proxy_get_cached_property (proxy, "HostSecurityId");
if (val != NULL)
fwupd_client_set_host_security_id (client, g_variant_get_string (val, NULL));
}
}
static void
@ -304,6 +322,9 @@ fwupd_client_connect (FwupdClient *client, GCancellable *cancellable, GError **e
val = g_dbus_proxy_get_cached_property (priv->proxy, "HostMachineId");
if (val != NULL)
fwupd_client_set_host_machine_id (client, g_variant_get_string (val, NULL));
val = g_dbus_proxy_get_cached_property (priv->proxy, "HostSecurityId");
if (val != NULL)
fwupd_client_set_host_security_id (client, g_variant_get_string (val, NULL));
return TRUE;
}
@ -341,6 +362,48 @@ fwupd_client_fixup_dbus_error (GError *error)
g_dbus_error_strip_remote_error (error);
}
/**
* fwupd_client_get_host_security_attrs:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets all the host security attributes from the daemon.
*
* Returns: (element-type FwupdSecurityAttr) (transfer container): attributes
*
* Since: 1.5.0
**/
GPtrArray *
fwupd_client_get_host_security_attrs (FwupdClient *client, GCancellable *cancellable, GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return NULL;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"GetHostSecurityAttrs",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
return fwupd_security_attr_array_from_variant (val);
}
/**
* fwupd_client_get_devices:
* @client: A #FwupdClient
@ -1314,6 +1377,24 @@ fwupd_client_get_host_machine_id (FwupdClient *client)
return priv->host_machine_id;
}
/**
* fwupd_client_get_host_security_id:
* @client: A #FwupdClient
*
* Gets the string that represents the host machine ID
*
* Returns: a string, or %NULL for unknown.
*
* Since: 1.5.0
**/
const gchar *
fwupd_client_get_host_security_id (FwupdClient *client)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_return_val_if_fail (FWUPD_IS_CLIENT (client), NULL);
return priv->host_security_id;
}
/**
* fwupd_client_get_status:
* @client: A #FwupdClient
@ -1869,6 +1950,9 @@ fwupd_client_get_property (GObject *object, guint prop_id,
case PROP_HOST_MACHINE_ID:
g_value_set_string (value, priv->host_machine_id);
break;
case PROP_HOST_SECURITY_ID:
g_value_set_string (value, priv->host_security_id);
break;
case PROP_INTERACTIVE:
g_value_set_boolean (value, priv->interactive);
break;
@ -2069,6 +2153,17 @@ fwupd_client_class_init (FwupdClientClass *klass)
pspec = g_param_spec_string ("host-machine-id", NULL, NULL,
NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_HOST_MACHINE_ID, pspec);
/**
* FwupdClient:host-security-id:
*
* The host machine-id string
*
* Since: 1.5.0
*/
pspec = g_param_spec_string ("host-security-id", NULL, NULL,
NULL, G_PARAM_READABLE | G_PARAM_STATIC_NAME);
g_object_class_install_property (object_class, PROP_HOST_SECURITY_ID, pspec);
}
static void
@ -2085,6 +2180,7 @@ fwupd_client_finalize (GObject *object)
g_free (priv->daemon_version);
g_free (priv->host_product);
g_free (priv->host_machine_id);
g_free (priv->host_security_id);
if (priv->conn != NULL)
g_object_unref (priv->conn);
if (priv->proxy != NULL)

View File

@ -95,6 +95,9 @@ FwupdDevice *fwupd_client_get_results (FwupdClient *client,
const gchar *device_id,
GCancellable *cancellable,
GError **error);
GPtrArray *fwupd_client_get_host_security_attrs (FwupdClient *client,
GCancellable *cancellable,
GError **error);
FwupdDevice *fwupd_client_get_device_by_id (FwupdClient *client,
const gchar *device_id,
GCancellable *cancellable,
@ -134,6 +137,7 @@ guint fwupd_client_get_percentage (FwupdClient *client);
const gchar *fwupd_client_get_daemon_version (FwupdClient *client);
const gchar *fwupd_client_get_host_product (FwupdClient *client);
const gchar *fwupd_client_get_host_machine_id (FwupdClient *client);
const gchar *fwupd_client_get_host_security_id (FwupdClient *client);
GPtrArray *fwupd_client_get_remotes (FwupdClient *client,
GCancellable *cancellable,

View File

@ -449,6 +449,8 @@ LIBFWUPD_1.4.1 {
LIBFWUPD_1.5.0 {
global:
fwupd_client_get_host_security_attrs;
fwupd_client_get_host_security_id;
fwupd_security_attr_add_flag;
fwupd_security_attr_add_obsolete;
fwupd_security_attr_array_from_variant;

View File

@ -116,6 +116,9 @@ gboolean fu_plugin_runner_clear_results (FuPlugin *self,
gboolean fu_plugin_runner_get_results (FuPlugin *self,
FuDevice *device,
GError **error);
gboolean fu_plugin_runner_add_security_attrs (FuPlugin *self,
GPtrArray *attrs,
GError **error);
gint fu_plugin_name_compare (FuPlugin *plugin1,
FuPlugin *plugin2);
gint fu_plugin_order_compare (FuPlugin *plugin1,

View File

@ -359,3 +359,16 @@ gboolean fu_plugin_device_created (FuPlugin *plugin,
**/
void fu_plugin_device_registered (FuPlugin *plugin,
FuDevice *dev);
/**
* fu_plugin_add_security_attrs
* @plugin: A #FuPlugin
* @attrs: A #GPtrArray of #FwupdSecurityAttr
* @error: A #GError or NULL
*
* Function that asks plugins to add Host Security Attributes.
*
* Since: 1.5.0
**/
gboolean fu_plugin_add_security_attrs (FuPlugin *plugin,
GPtrArray *attrs,
GError **error);

View File

@ -1579,6 +1579,26 @@ fu_plugin_runner_update_reload (FuPlugin *self, FuDevice *device, GError **error
return fu_device_reload (device, error);
}
/**
* fu_plugin_runner_add_security_attrs:
* @self: a #FuPlugin
* @attrs: (element-type FwupdSecurityAttr): a #GPtrArray of attributes
* @error: a #GError or NULL
*
* Runs the composite_prepare routine for the plugin
*
* Returns: #TRUE for success, #FALSE for failure
*
* Since: 1.5.0
**/
gboolean
fu_plugin_runner_add_security_attrs (FuPlugin *self, GPtrArray *attrs, GError **error)
{
return fu_plugin_runner_device_array_generic (self, attrs,
"fu_plugin_add_security_attrs",
error);
}
/**
* fu_plugin_add_udev_subsystem:
* @self: a #FuPlugin

View File

@ -585,6 +585,7 @@ LIBFWUPDPLUGIN_1.5.0 {
global:
fu_common_filename_glob;
fu_common_is_cpu_intel;
fu_plugin_runner_add_security_attrs;
fu_plugin_runner_device_added;
fu_udev_device_get_parent_name;
fu_udev_device_get_sysfs_attr;

View File

@ -21,6 +21,7 @@
#include "fu-util-common.h"
#include "fwupd-device-private.h"
#include "fwupd-enums-private.h"
#include "fwupd-security-attr-private.h"
struct FuUtilPrivate {
GCancellable *cancellable;
@ -113,6 +114,37 @@ fu_util_add_updates_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **er
return TRUE;
}
static gboolean
fu_util_add_security_attributes_json (FuUtilPrivate *priv, JsonBuilder *builder, GError **error)
{
g_autoptr(GPtrArray) attrs = NULL;
/* not ready yet */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"The HSI specification is not yet complete. "
"To ignore this warning, use --force");
return FALSE;
}
/* get attrs from daemon */
attrs = fwupd_client_get_host_security_attrs (priv->client, NULL, error);
if (attrs == NULL)
return FALSE;
json_builder_set_member_name (builder, "HostSecurityAttributes");
json_builder_begin_array (builder);
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
json_builder_begin_object (builder);
fwupd_security_attr_to_json (attr, builder);
json_builder_end_object (builder);
}
json_builder_end_array (builder);
return TRUE;
}
static gboolean
fu_util_get_devices (FuUtilPrivate *priv, gchar **values, GError **error)
{
@ -199,6 +231,49 @@ fu_util_get_updates (FuUtilPrivate *priv, gchar **values, GError **error)
return TRUE;
}
static gboolean
fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autofree gchar *data = NULL;
g_autoptr(JsonBuilder) builder = NULL;
g_autoptr(JsonGenerator) json_generator = NULL;
g_autoptr(JsonNode) json_root = NULL;
/* check args */
if (g_strv_length (values) != 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_ARGS,
"Invalid arguments");
return FALSE;
}
/* create header */
builder = json_builder_new ();
json_builder_begin_object (builder);
if (!fu_util_add_security_attributes_json (priv, builder, error))
return FALSE;
json_builder_end_object (builder);
/* export as a string */
json_root = json_builder_get_root (builder);
json_generator = json_generator_new ();
json_generator_set_pretty (json_generator, TRUE);
json_generator_set_root (json_generator, json_root);
data = json_generator_to_data (json_generator, NULL);
if (data == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"Failed to convert to JSON string");
return FALSE;
}
/* just print */
g_print ("%s\n", data);
return TRUE;
}
static void
fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
@ -280,6 +355,11 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */
_("Gets the list of updates for connected hardware"),
fu_util_get_updates);
fu_util_cmd_array_add (cmd_array,
"security", NULL,
/* TRANSLATORS: command description */
_("Gets the host security attributes"),
fu_util_security);
/* sort by command name */
fu_util_cmd_array_sort (cmd_array);

View File

@ -49,6 +49,7 @@
#include "fu-plugin-private.h"
#include "fu-quirks.h"
#include "fu-remote-list.h"
#include "fu-security-attrs.h"
#include "fu-smbios-private.h"
#include "fu-udev-device-private.h"
#include "fu-usb-device-private.h"
@ -99,6 +100,8 @@ struct _FuEngine
gchar *host_machine_id;
JcatContext *jcat_context;
gboolean loaded;
gchar *host_security_id;
gboolean host_security_id_valid;
};
enum {
@ -133,6 +136,7 @@ fu_engine_emit_changed (FuEngine *self)
static void
fu_engine_emit_device_changed (FuEngine *self, FuDevice *device)
{
self->host_security_id_valid = FALSE;
g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device);
}
@ -5026,6 +5030,56 @@ fu_engine_get_host_machine_id (FuEngine *self)
return self->host_machine_id;
}
GPtrArray *
fu_engine_get_host_security_attrs (FuEngine *self, GError **error)
{
GPtrArray *plugins = fu_plugin_list_get_all (self->plugin_list);
g_autoptr(GPtrArray) attrs = NULL;
attrs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
for (guint j = 0; j < plugins->len; j++) {
FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j);
g_autoptr(GError) error_local = NULL;
if (!fu_plugin_runner_add_security_attrs (plugin_tmp,
attrs,
&error_local)) {
FwupdSecurityAttr *attr;
g_autofree gchar *appstream_id = NULL;
g_autofree gchar *msg = NULL;
appstream_id = g_strdup_printf ("org.fwupd.plugin.%s",
fu_plugin_get_name (plugin_tmp));
msg = g_strdup_printf ("Failed to add HSI attribute: %s",
error_local->message);
attr = fwupd_security_attr_new (appstream_id);
fwupd_security_attr_set_name (attr, "fwupd");
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE);
fwupd_security_attr_set_result (attr, msg);
g_ptr_array_add (attrs, attr);
continue;
}
}
/* set the obsoletes flag for each attr */
fu_security_attrs_depsolve (attrs);
return g_steal_pointer (&attrs);
}
const gchar *
fu_engine_get_host_security_id (FuEngine *self)
{
g_return_val_if_fail (FU_IS_ENGINE (self), NULL);
/* rebuild */
if (!self->host_security_id_valid) {
g_autoptr(GPtrArray) attrs = fu_engine_get_host_security_attrs (self, NULL);
g_free (self->host_security_id);
self->host_security_id = fu_security_attrs_calculate_hsi (attrs);
self->host_security_id_valid = TRUE;
}
return self->host_security_id;
}
gboolean
fu_engine_load_plugins (FuEngine *self, GError **error)
{
@ -5745,6 +5799,7 @@ fu_engine_finalize (GObject *obj)
g_source_remove (self->coldplug_id);
g_free (self->host_machine_id);
g_free (self->host_security_id);
g_object_unref (self->idle);
g_object_unref (self->config);
g_object_unref (self->remote_list);

View File

@ -49,6 +49,7 @@ gboolean fu_engine_load_plugins (FuEngine *self,
gboolean fu_engine_get_tainted (FuEngine *self);
const gchar *fu_engine_get_host_product (FuEngine *self);
const gchar *fu_engine_get_host_machine_id (FuEngine *self);
const gchar *fu_engine_get_host_security_id (FuEngine *self);
FwupdStatus fu_engine_get_status (FuEngine *self);
XbSilo *fu_engine_get_silo_from_blob (FuEngine *self,
GBytes *blob_cab,
@ -82,6 +83,8 @@ GPtrArray *fu_engine_get_upgrades (FuEngine *self,
FwupdDevice *fu_engine_get_results (FuEngine *self,
const gchar *device_id,
GError **error);
GPtrArray *fu_engine_get_host_security_attrs (FuEngine *self,
GError **error);
gboolean fu_engine_clear_results (FuEngine *self,
const gchar *device_id,
GError **error);

View File

@ -20,6 +20,7 @@
#include <jcat.h>
#include "fwupd-device-private.h"
#include "fwupd-security-attr-private.h"
#include "fwupd-release-private.h"
#include "fwupd-remote-private.h"
#include "fwupd-resources.h"
@ -254,6 +255,22 @@ fu_main_device_array_to_variant (FuMainPrivate *priv, const gchar *sender,
return g_variant_new ("(aa{sv})", &builder);
}
static GVariant *
fu_main_security_attr_array_to_variant (FuMainPrivate *priv, GPtrArray *attrs)
{
GVariantBuilder builder;
g_return_val_if_fail (attrs->len > 0, NULL);
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *security_attr = g_ptr_array_index (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);
}
static GVariant *
fu_main_release_array_to_variant (GPtrArray *results)
{
@ -987,6 +1004,18 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
g_dbus_method_invocation_return_value (invocation, val);
return;
}
if (g_strcmp0 (method_name, "GetHostSecurityAttrs") == 0) {
g_autoptr(GPtrArray) attrs = NULL;
g_debug ("Called %s()", method_name);
attrs = fu_engine_get_host_security_attrs (priv->engine, &error);
if (attrs == NULL) {
g_dbus_method_invocation_return_gerror (invocation, error);
return;
}
val = fu_main_security_attr_array_to_variant (priv, attrs);
g_dbus_method_invocation_return_value (invocation, val);
return;
}
if (g_strcmp0 (method_name, "ClearResults") == 0) {
const gchar *device_id;
g_variant_get (parameters, "(&s)", &device_id);
@ -1388,6 +1417,9 @@ fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender,
if (g_strcmp0 (property_name, "HostMachineId") == 0)
return g_variant_new_string (fu_engine_get_host_machine_id (priv->engine));
if (g_strcmp0 (property_name, "HostSecurityId") == 0)
return g_variant_new_string (fu_engine_get_host_security_id (priv->engine));
if (g_strcmp0 (property_name, "Interactive") == 0)
return g_variant_new_boolean (isatty (fileno (stdout)) != 0);

115
src/fu-security-attrs.c Normal file
View File

@ -0,0 +1,115 @@
/*
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <glib/gi18n.h>
#include "fwupd-security-attr.h"
#include "fu-security-attrs.h"
gchar *
fu_security_attrs_calculate_hsi (GPtrArray *attrs)
{
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,
};
/* 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 < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (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 < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (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);
}
void
fu_security_attrs_depsolve (GPtrArray *attrs)
{
g_autoptr(GHashTable) attrs_by_id = NULL;
/* make hash of ID -> object */
attrs_by_id = g_hash_table_new (g_str_hash, g_str_equal);
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (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 < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (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);
}
}
}
}

12
src/fu-security-attrs.h Normal file
View File

@ -0,0 +1,12 @@
/*
* Copyright (C) 2020 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#pragma once
#include <glib-object.h>
gchar *fu_security_attrs_calculate_hsi (GPtrArray *attrs);
void fu_security_attrs_depsolve (GPtrArray *attrs);

View File

@ -25,6 +25,7 @@
#include "fu-plugin-list.h"
#include "fu-progressbar.h"
#include "fu-hash.h"
#include "fu-security-attrs.h"
#include "fu-smbios-private.h"
typedef struct {
@ -151,6 +152,75 @@ fu_engine_generate_md_func (gconstpointer user_data)
g_assert_cmpstr (tmp, ==, NULL);
}
static void
fu_plugin_hsi_func (gconstpointer user_data)
{
FwupdSecurityAttr *attr;
g_autofree gchar *hsi1 = NULL;
g_autofree gchar *hsi2 = NULL;
g_autofree gchar *hsi3 = NULL;
g_autofree gchar *hsi4 = NULL;
g_autofree gchar *hsi5 = NULL;
g_autofree gchar *hsi6 = NULL;
g_autofree gchar *hsi7 = NULL;
g_autoptr(FuEngine) engine = fu_engine_new (FU_APP_FLAGS_NONE);
g_autoptr(GPtrArray) attrs = NULL;
/* no attrs */
attrs = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
hsi1 = fu_security_attrs_calculate_hsi (attrs);
g_assert_cmpstr (hsi1, ==, "HSI:0");
/* just success from HSI:1 */
attr = fwupd_security_attr_new ("org.fwupd.Hsi.BIOSWE");
fwupd_security_attr_set_level (attr, 1);
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
g_ptr_array_add (attrs, attr);
hsi2 = fu_security_attrs_calculate_hsi (attrs);
g_assert_cmpstr (hsi2, ==, "HSI:1");
/* add failed from HSI:2, so still HSI:1 */
attr = fwupd_security_attr_new ("org.fwupd.Hsi.PRX");
fwupd_security_attr_set_level (attr, 2);
g_ptr_array_add (attrs, attr);
hsi3 = fu_security_attrs_calculate_hsi (attrs);
g_assert_cmpstr (hsi3, ==, "HSI:1");
/* add attr from HSI:3, obsoleting the failure */
attr = fwupd_security_attr_new ("org.fwupd.Hsi.BIOSGuard");
fwupd_security_attr_set_level (attr, 3);
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
fwupd_security_attr_add_obsolete (attr, "org.fwupd.Hsi.PRX");
g_ptr_array_add (attrs, attr);
fu_security_attrs_depsolve (attrs);
hsi4 = fu_security_attrs_calculate_hsi (attrs);
g_assert_cmpstr (hsi4, ==, "HSI:3");
/* add taint that was fine */
attr = fwupd_security_attr_new ("org.fwupd.Hsi.PluginsTainted");
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE);
g_ptr_array_add (attrs, attr);
hsi5 = fu_security_attrs_calculate_hsi (attrs);
g_assert_cmpstr (hsi5, ==, "HSI:3");
/* add updates and attestation */
attr = fwupd_security_attr_new ("org.fwupd.Hsi.LVFS");
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_UPDATES);
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ATTESTATION);
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS);
g_ptr_array_add (attrs, attr);
hsi6 = fu_security_attrs_calculate_hsi (attrs);
g_assert_cmpstr (hsi6, ==, "HSI:3+UA");
/* add issue that was uncool */
attr = fwupd_security_attr_new ("org.fwupd.Hsi.Swap");
fwupd_security_attr_add_flag (attr, FWUPD_SECURITY_ATTR_FLAG_RUNTIME_ISSUE);
g_ptr_array_add (attrs, attr);
hsi7 = fu_security_attrs_calculate_hsi (attrs);
g_assert_cmpstr (hsi7, ==, "HSI:3+UA!");
}
static void
fu_plugin_hash_func (gconstpointer user_data)
{
@ -2980,6 +3050,8 @@ main (int argc, char **argv)
g_test_add_data_func ("/fwupd/progressbar", self,
fu_progressbar_func);
}
g_test_add_data_func ("/fwupd/plugin{hsi}", self,
fu_plugin_hsi_func);
g_test_add_data_func ("/fwupd/plugin{build-hash}", self,
fu_plugin_hash_func);
g_test_add_data_func ("/fwupd/plugin{module}", self,

View File

@ -1937,6 +1937,38 @@ fu_util_get_remotes (FuUtilPrivate *priv, gchar **values, GError **error)
return TRUE;
}
static gboolean
fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GPtrArray) attrs = NULL;
g_autofree gchar *str = NULL;
/* not ready yet */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"The HSI specification is not yet complete. "
"To ignore this warning, use --force");
return FALSE;
}
if (!fu_util_start_engine (priv, FU_ENGINE_LOAD_FLAG_NONE, error))
return FALSE;
/* TRANSLATORS: this is a string like 'HSI:2-U' */
g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
fu_engine_get_host_security_id (priv->engine));
/* print the "why" */
attrs = fu_engine_get_host_security_attrs (priv->engine, error);
if (attrs == NULL)
return FALSE;
str = fu_util_security_attrs_to_string (attrs);
g_print ("%s\n", str);
return TRUE;
}
int
main (int argc, char *argv[])
{
@ -2176,6 +2208,12 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */
_("Refresh metadata from remote server"),
fu_util_refresh);
fu_util_cmd_array_add (cmd_array,
"security",
NULL,
/* TRANSLATORS: command description */
_("Gets the host security attributes."),
fu_util_security);
/* do stuff on ctrl+c */
priv->cancellable = g_cancellable_new ();

View File

@ -16,6 +16,7 @@
#include "fu-common.h"
#include "fu-util-common.h"
#include "fu-device.h"
#include "fu-security-attrs.h"
#ifdef HAVE_SYSTEMD
#include "fu-systemd.h"
@ -1547,3 +1548,71 @@ fu_util_remote_to_string (FwupdRemote *remote, guint idt)
return g_string_free (str, FALSE);
}
static void
fu_security_attr_append_str (FwupdSecurityAttr *attr, GString *str)
{
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) {
g_string_append_printf (str, "\033[32m✔\033[0m ");
} else {
g_string_append_printf (str, "\033[31m✘\033[0m ");
}
g_string_append_printf (str, "%s", fwupd_security_attr_get_name (attr));
if (fwupd_security_attr_get_result (attr) != NULL) {
g_string_append_printf (str, ": %s",
fwupd_security_attr_get_result (attr));
} else {
g_string_append_printf (str, ": %s",
fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)
? "OK" : "Failed");
}
if (fwupd_security_attr_has_flag (attr, FWUPD_SECURITY_ATTR_FLAG_OBSOLETED))
g_string_append (str, " (obsoleted)");
g_string_append_printf (str, "\n");
}
gchar *
fu_util_security_attrs_to_string (GPtrArray *attrs)
{
FwupdSecurityAttrFlags flags = FWUPD_SECURITY_ATTR_FLAG_NONE;
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,
};
GString *str = g_string_new (NULL);
for (guint j = 1; j <= FWUPD_SECURITY_ATTR_LEVEL_LAST; j++) {
gboolean has_header = FALSE;
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
if (fwupd_security_attr_get_level (attr) != j)
continue;
if (!has_header) {
g_string_append_printf (str, "\n\033[1mHSI-%u\033[0m\n", j);
has_header = TRUE;
}
fu_security_attr_append_str (attr, str);
}
}
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
flags |= fwupd_security_attr_get_flags (attr);
}
for (guint j = 0; hpi_suffixes[j] != FWUPD_SECURITY_ATTR_FLAG_NONE; j++) {
if (flags & hpi_suffixes[j]) {
g_string_append_printf (str, "\n\033[1m%s -%s\033[0m\n",
/* TRANSLATORS: this is the HSI suffix */
_("Runtime Suffix"),
fwupd_security_attr_flag_to_suffix (hpi_suffixes[j]));
for (guint i = 0; i < attrs->len; i++) {
FwupdSecurityAttr *attr = g_ptr_array_index (attrs, i);
if (!fwupd_security_attr_has_flag (attr, hpi_suffixes[j]))
continue;
fu_security_attr_append_str (attr, str);
}
}
}
return g_string_free (str, FALSE);
}

View File

@ -77,3 +77,4 @@ gchar *fu_util_release_to_string (FwupdRelease *rel,
guint idt);
gchar *fu_util_remote_to_string (FwupdRemote *remote,
guint idt);
gchar *fu_util_security_attrs_to_string (GPtrArray *attrs);

View File

@ -26,6 +26,7 @@
#include "fu-history.h"
#include "fu-plugin-private.h"
#include "fu-progressbar.h"
#include "fu-security-attrs.h"
#include "fu-util-common.h"
#include "fwupd-common-private.h"
@ -2364,6 +2365,37 @@ fu_util_modify_config (FuUtilPrivate *priv, gchar **values, GError **error)
return TRUE;
}
static gboolean
fu_util_security (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_autoptr(GPtrArray) attrs = NULL;
g_autofree gchar *str = NULL;
/* not ready yet */
if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOT_SUPPORTED,
"The HSI specification is not yet complete. "
"To ignore this warning, use --force");
return FALSE;
}
/* TRANSLATORS: this is a string like 'HSI:2-U' */
g_print ("%s \033[1m%s\033[0m\n", _("Host Security ID:"),
fwupd_client_get_host_security_id (priv->client));
/* print the "why" */
attrs = fwupd_client_get_host_security_attrs (priv->client,
priv->cancellable,
error);
if (attrs == NULL)
return FALSE;
str = fu_util_security_attrs_to_string (attrs);
g_print ("%s\n", str);
return TRUE;
}
static void
fu_util_ignore_cb (const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
@ -2678,7 +2710,12 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */
_("Reinstall current firmware on the device."),
fu_util_reinstall);
fu_util_cmd_array_add (cmd_array,
"security",
NULL,
/* TRANSLATORS: command description */
_("Gets the host security attributes."),
fu_util_security);
/* do stuff on ctrl+c */
priv->cancellable = g_cancellable_new ();

View File

@ -131,6 +131,7 @@ fwupdtool = executable(
'fu-plugin-list.c',
'fu-progressbar.c',
'fu-remote-list.c',
'fu-security-attrs.c',
'fu-util-common.c',
systemd_src
],
@ -229,6 +230,7 @@ executable(
'fu-main.c',
'fu-plugin-list.c',
'fu-remote-list.c',
'fu-security-attrs.c',
systemd_src
],
include_directories : [
@ -285,6 +287,7 @@ if get_option('tests')
'fu-plugin-list.c',
'fu-progressbar.c',
'fu-remote-list.c',
'fu-security-attrs.c',
'fu-self-test.c',
systemd_src
],

View File

@ -44,6 +44,17 @@
</doc:doc>
</property>
<!--***********************************************************-->
<property name='HostSecurityId' type='s' access='read'>
<doc:doc>
<doc:description>
<doc:para>
The Host Security ID, for instance <doc:tt>HSI:2UA</doc:tt>
</doc:para>
</doc:description>
</doc:doc>
</property>
<!--***********************************************************-->
<property name='Tainted' type='b' access='read'>
<doc:doc>
@ -242,6 +253,24 @@
</arg>
</method>
<!--***********************************************************-->
<method name='GetHostSecurityAttrs'>
<doc:doc>
<doc:description>
<doc:para>
Gets a list of all the Host Security ID attributes.
</doc:para>
</doc:description>
</doc:doc>
<arg type='aa{sv}' name='attrs' direction='out'>
<doc:doc>
<doc:summary>
<doc:para>An array of HSI attributes, with any properties set on each.</doc:para>
</doc:summary>
</doc:doc>
</arg>
</method>
<!--***********************************************************-->
<method name='Install'>
<doc:doc>