Allow blocking specific firmware releases by checksum

Fixes https://github.com/fwupd/fwupd/issues/2280
This commit is contained in:
Richard Hughes 2020-07-27 15:31:11 +01:00
parent 27fd95ae88
commit 3120683143
14 changed files with 688 additions and 11 deletions

View File

@ -1,5 +1,6 @@
_fwupdmgr_cmd_list=( _fwupdmgr_cmd_list=(
'activate' 'activate'
'block-firmware'
'clear-history' 'clear-history'
'clear-offline' 'clear-offline'
'clear-results' 'clear-results'
@ -7,6 +8,7 @@ _fwupdmgr_cmd_list=(
'downgrade' 'downgrade'
'enable-remote' 'enable-remote'
'get-approved-firmware' 'get-approved-firmware'
'get-blocked-firmware'
'get-details' 'get-details'
'get-devices' 'get-devices'
'get-history' 'get-history'
@ -25,6 +27,7 @@ _fwupdmgr_cmd_list=(
'security' 'security'
'set-approved-firmware' 'set-approved-firmware'
'unlock' 'unlock'
'unblock-firmware'
'update' 'update'
'upgrade' 'upgrade'
'verify' 'verify'

View File

@ -32,3 +32,7 @@ EnumerateAllDevices=false
# A list of firmware checksums that has been approved by the site admin # A list of firmware checksums that has been approved by the site admin
# If unset, all firmware is approved # If unset, all firmware is approved
ApprovedFirmware= ApprovedFirmware=
# Allow blocking specific devices by their checksum, either SHA1 or SHA256
# Uses semicolons as delimiter
BlockedFirmware=

View File

@ -2128,6 +2128,98 @@ fwupd_client_set_approved_firmware (FwupdClient *client,
return TRUE; return TRUE;
} }
/**
* fwupd_client_get_blocked_firmware:
* @client: A #FwupdClient
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Gets the list of blocked firmware.
*
* Returns: (transfer full): list of checksums, or %NULL
*
* Since: 1.4.6
**/
gchar **
fwupd_client_get_blocked_firmware (FwupdClient *client,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
gchar **retval = 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,
"GetBlockedFirmware",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return NULL;
}
g_variant_get (val, "(^as)", &retval);
return retval;
}
/**
* fwupd_client_set_blocked_firmware:
* @client: A #FwupdClient
* @checksums: Array of checksums
* @cancellable: the #GCancellable, or %NULL
* @error: the #GError, or %NULL
*
* Sets the list of blocked firmware.
*
* Returns: %TRUE for success
*
* Since: 1.4.6
**/
gboolean
fwupd_client_set_blocked_firmware (FwupdClient *client,
gchar **checksums,
GCancellable *cancellable,
GError **error)
{
FwupdClientPrivate *priv = GET_PRIVATE (client);
g_autoptr(GVariant) val = NULL;
g_return_val_if_fail (FWUPD_IS_CLIENT (client), FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
/* connect */
if (!fwupd_client_connect (client, cancellable, error))
return FALSE;
/* call into daemon */
val = g_dbus_proxy_call_sync (priv->proxy,
"SetBlockedFirmware",
g_variant_new ("(^as)", checksums),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
error);
if (val == NULL) {
if (error != NULL)
fwupd_client_fixup_dbus_error (*error);
return FALSE;
}
return TRUE;
}
/** /**
* fwupd_client_set_feature_flags: * fwupd_client_set_feature_flags:
* @client: A #FwupdClient * @client: A #FwupdClient

View File

@ -205,6 +205,13 @@ gboolean fwupd_client_set_approved_firmware (FwupdClient *client,
gchar **checksums, gchar **checksums,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
gchar **fwupd_client_get_blocked_firmware (FwupdClient *client,
GCancellable *cancellable,
GError **error);
gboolean fwupd_client_set_blocked_firmware (FwupdClient *client,
gchar **checksums,
GCancellable *cancellable,
GError **error);
gchar *fwupd_client_self_sign (FwupdClient *client, gchar *fwupd_client_self_sign (FwupdClient *client,
const gchar *value, const gchar *value,
FwupdSelfSignFlags flags, FwupdSelfSignFlags flags,

View File

@ -469,6 +469,13 @@ LIBFWUPD_1.4.5 {
local: *; local: *;
} LIBFWUPD_1.4.1; } LIBFWUPD_1.4.1;
LIBFWUPD_1.4.6 {
global:
fwupd_client_get_blocked_firmware;
fwupd_client_set_blocked_firmware;
local: *;
} LIBFWUPD_1.4.5;
LIBFWUPD_1.5.0 { LIBFWUPD_1.5.0 {
global: global:
fwupd_client_get_host_security_attrs; fwupd_client_get_host_security_attrs;
@ -508,4 +515,4 @@ LIBFWUPD_1.5.0 {
fwupd_security_attr_to_string; fwupd_security_attr_to_string;
fwupd_security_attr_to_variant; fwupd_security_attr_to_variant;
local: *; local: *;
} LIBFWUPD_1.4.5; } LIBFWUPD_1.4.6;

View File

@ -30,6 +30,7 @@ struct _FuConfig
GPtrArray *disabled_devices; /* (element-type utf-8) */ GPtrArray *disabled_devices; /* (element-type utf-8) */
GPtrArray *disabled_plugins; /* (element-type utf-8) */ GPtrArray *disabled_plugins; /* (element-type utf-8) */
GPtrArray *approved_firmware; /* (element-type utf-8) */ GPtrArray *approved_firmware; /* (element-type utf-8) */
GPtrArray *blocked_firmware; /* (element-type utf-8) */
guint64 archive_size_max; guint64 archive_size_max;
guint idle_timeout; guint idle_timeout;
gchar *config_file; gchar *config_file;
@ -52,6 +53,7 @@ fu_config_reload (FuConfig *self, GError **error)
guint64 archive_size_max; guint64 archive_size_max;
guint idle_timeout; guint idle_timeout;
g_auto(GStrv) approved_firmware = NULL; g_auto(GStrv) approved_firmware = NULL;
g_auto(GStrv) blocked_firmware = NULL;
g_auto(GStrv) devices = NULL; g_auto(GStrv) devices = NULL;
g_auto(GStrv) plugins = NULL; g_auto(GStrv) plugins = NULL;
g_autofree gchar *domains = NULL; g_autofree gchar *domains = NULL;
@ -106,6 +108,20 @@ fu_config_reload (FuConfig *self, GError **error)
} }
} }
/* get blocked firmware */
g_ptr_array_set_size (self->blocked_firmware, 0);
blocked_firmware = g_key_file_get_string_list (keyfile,
"fwupd",
"BlockedFirmware",
NULL, /* length */
NULL);
if (blocked_firmware != NULL) {
for (guint i = 0; blocked_firmware[i] != NULL; i++) {
g_ptr_array_add (self->blocked_firmware,
g_strdup (blocked_firmware[i]));
}
}
/* get maximum archive size, defaulting to something sane */ /* get maximum archive size, defaulting to something sane */
archive_size_max = g_key_file_get_uint64 (keyfile, archive_size_max = g_key_file_get_uint64 (keyfile,
"fwupd", "fwupd",
@ -227,6 +243,13 @@ fu_config_get_disabled_devices (FuConfig *self)
return self->disabled_devices; return self->disabled_devices;
} }
GPtrArray *
fu_config_get_blocked_firmware (FuConfig *self)
{
g_return_val_if_fail (FU_IS_CONFIG (self), NULL);
return self->blocked_firmware;
}
guint64 guint64
fu_config_get_archive_size_max (FuConfig *self) fu_config_get_archive_size_max (FuConfig *self)
{ {
@ -282,6 +305,7 @@ fu_config_init (FuConfig *self)
self->disabled_devices = g_ptr_array_new_with_free_func (g_free); self->disabled_devices = g_ptr_array_new_with_free_func (g_free);
self->disabled_plugins = g_ptr_array_new_with_free_func (g_free); self->disabled_plugins = g_ptr_array_new_with_free_func (g_free);
self->approved_firmware = g_ptr_array_new_with_free_func (g_free); self->approved_firmware = g_ptr_array_new_with_free_func (g_free);
self->blocked_firmware = g_ptr_array_new_with_free_func (g_free);
} }
static void static void
@ -294,6 +318,7 @@ fu_config_finalize (GObject *obj)
g_ptr_array_unref (self->disabled_devices); g_ptr_array_unref (self->disabled_devices);
g_ptr_array_unref (self->disabled_plugins); g_ptr_array_unref (self->disabled_plugins);
g_ptr_array_unref (self->approved_firmware); g_ptr_array_unref (self->approved_firmware);
g_ptr_array_unref (self->blocked_firmware);
g_free (self->config_file); g_free (self->config_file);
G_OBJECT_CLASS (fu_config_parent_class)->finalize (obj); G_OBJECT_CLASS (fu_config_parent_class)->finalize (obj);

View File

@ -26,5 +26,6 @@ guint fu_config_get_idle_timeout (FuConfig *self);
GPtrArray *fu_config_get_disabled_devices (FuConfig *self); GPtrArray *fu_config_get_disabled_devices (FuConfig *self);
GPtrArray *fu_config_get_disabled_plugins (FuConfig *self); GPtrArray *fu_config_get_disabled_plugins (FuConfig *self);
GPtrArray *fu_config_get_approved_firmware (FuConfig *self); GPtrArray *fu_config_get_approved_firmware (FuConfig *self);
GPtrArray *fu_config_get_blocked_firmware (FuConfig *self);
gboolean fu_config_get_update_motd (FuConfig *self); gboolean fu_config_get_update_motd (FuConfig *self);
gboolean fu_config_get_enumerate_all_devices (FuConfig *self); gboolean fu_config_get_enumerate_all_devices (FuConfig *self);

View File

@ -103,6 +103,7 @@ struct _FuEngine
GHashTable *runtime_versions; GHashTable *runtime_versions;
GHashTable *compile_versions; GHashTable *compile_versions;
GHashTable *approved_firmware; /* (nullable) */ GHashTable *approved_firmware; /* (nullable) */
GHashTable *blocked_firmware; /* (nullable) */
GHashTable *firmware_gtypes; GHashTable *firmware_gtypes;
gchar *host_machine_id; gchar *host_machine_id;
JcatContext *jcat_context; JcatContext *jcat_context;
@ -527,6 +528,7 @@ fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, G
const gchar *keys[] = { const gchar *keys[] = {
"ArchiveSizeMax", "ArchiveSizeMax",
"DisabledDevices", "DisabledDevices",
"BlockedFirmware",
"DisabledPlugins", "DisabledPlugins",
"IdleTimeout", "IdleTimeout",
"VerboseDomains", "VerboseDomains",
@ -4178,6 +4180,21 @@ fu_engine_check_release_is_approved (FuEngine *self, FwupdRelease *rel)
return FALSE; return FALSE;
} }
static gboolean
fu_engine_check_release_is_blocked (FuEngine *self, FwupdRelease *rel)
{
GPtrArray *csums = fwupd_release_get_checksums (rel);
if (self->blocked_firmware == NULL)
return FALSE;
for (guint i = 0; i < csums->len; i++) {
const gchar *csum = g_ptr_array_index (csums, i);
g_debug ("checking %s against blocked list", csum);
if (g_hash_table_lookup (self->blocked_firmware, csum) != NULL)
return TRUE;
}
return FALSE;
}
static gboolean static gboolean
fu_engine_add_releases_for_device_component (FuEngine *self, fu_engine_add_releases_for_device_component (FuEngine *self,
FuEngineRequest *request, FuEngineRequest *request,
@ -4258,6 +4275,10 @@ fu_engine_add_releases_for_device_component (FuEngine *self,
fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_VERSION); fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_VERSION);
} }
/* manually blocked */
if (fu_engine_check_release_is_blocked (self, rel))
fwupd_release_add_flag (rel, FWUPD_RELEASE_FLAG_BLOCKED_APPROVAL);
/* check if remote is filtering firmware */ /* check if remote is filtering firmware */
remote_id = fwupd_release_get_remote_id (rel); remote_id = fwupd_release_get_remote_id (rel);
if (remote_id != NULL) { if (remote_id != NULL) {
@ -4545,6 +4566,56 @@ fu_engine_add_approved_firmware (FuEngine *self, const gchar *checksum)
g_hash_table_add (self->approved_firmware, g_strdup (checksum)); g_hash_table_add (self->approved_firmware, g_strdup (checksum));
} }
GPtrArray *
fu_engine_get_blocked_firmware (FuEngine *self)
{
GPtrArray *checksums = g_ptr_array_new_with_free_func (g_free);
if (self->blocked_firmware != NULL) {
g_autoptr(GList) keys = g_hash_table_get_keys (self->blocked_firmware);
for (GList *l = keys; l != NULL; l = l->next) {
const gchar *csum = l->data;
g_ptr_array_add (checksums, g_strdup (csum));
}
}
return checksums;
}
void
fu_engine_add_blocked_firmware (FuEngine *self, const gchar *checksum)
{
if (self->blocked_firmware == NULL) {
self->blocked_firmware = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
NULL);
}
g_hash_table_add (self->blocked_firmware, g_strdup (checksum));
}
gboolean
fu_engine_set_blocked_firmware (FuEngine *self, GPtrArray *checksums, GError **error)
{
/* update in-memory hash */
if (self->blocked_firmware != NULL) {
g_hash_table_unref (self->blocked_firmware);
self->blocked_firmware = NULL;
}
for (guint i = 0; i < checksums->len; i++) {
const gchar *csum = g_ptr_array_index (checksums, i);
fu_engine_add_blocked_firmware (self, csum);
}
/* save database */
if (!fu_history_clear_blocked_firmware (self->history, error))
return FALSE;
for (guint i = 0; i < checksums->len; i++) {
const gchar *csum = g_ptr_array_index (checksums, i);
if (!fu_history_add_blocked_firmware (self->history, csum, error))
return FALSE;
}
return TRUE;
}
gchar * gchar *
fu_engine_self_sign (FuEngine *self, fu_engine_self_sign (FuEngine *self,
const gchar *value, const gchar *value,
@ -6123,7 +6194,8 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
{ {
FuRemoteListLoadFlags remote_list_flags = FU_REMOTE_LIST_LOAD_FLAG_NONE; FuRemoteListLoadFlags remote_list_flags = FU_REMOTE_LIST_LOAD_FLAG_NONE;
FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE; FuQuirksLoadFlags quirks_flags = FU_QUIRKS_LOAD_FLAG_NONE;
g_autoptr(GPtrArray) checksums = NULL; g_autoptr(GPtrArray) checksums_approved = NULL;
g_autoptr(GPtrArray) checksums_blocked = NULL;
#ifndef _WIN32 #ifndef _WIN32
g_autoptr(GError) error_local = NULL; g_autoptr(GError) error_local = NULL;
#endif #endif
@ -6159,21 +6231,33 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
/* create client certificate */ /* create client certificate */
fu_engine_ensure_client_certificate (self); fu_engine_ensure_client_certificate (self);
/* get hardcoded approved firmware */ /* get hardcoded approved and blocked firmware */
checksums = fu_config_get_approved_firmware (self->config); checksums_approved = fu_config_get_approved_firmware (self->config);
for (guint i = 0; i < checksums->len; i++) { for (guint i = 0; i < checksums_approved->len; i++) {
const gchar *csum = g_ptr_array_index (checksums, i); const gchar *csum = g_ptr_array_index (checksums_approved, i);
fu_engine_add_approved_firmware (self, csum); fu_engine_add_approved_firmware (self, csum);
} }
checksums_blocked = fu_config_get_blocked_firmware (self->config);
for (guint i = 0; i < checksums_blocked->len; i++) {
const gchar *csum = g_ptr_array_index (checksums_blocked, i);
fu_engine_add_blocked_firmware (self, csum);
}
/* get extra firmware saved to the database */ /* get extra firmware saved to the database */
checksums = fu_history_get_approved_firmware (self->history, error); checksums_approved = fu_history_get_approved_firmware (self->history, error);
if (checksums == NULL) if (checksums_approved == NULL)
return FALSE; return FALSE;
for (guint i = 0; i < checksums->len; i++) { for (guint i = 0; i < checksums_approved->len; i++) {
const gchar *csum = g_ptr_array_index (checksums, i); const gchar *csum = g_ptr_array_index (checksums_approved, i);
fu_engine_add_approved_firmware (self, csum); fu_engine_add_approved_firmware (self, csum);
} }
checksums_blocked = fu_history_get_blocked_firmware (self->history, error);
if (checksums_blocked == NULL)
return FALSE;
for (guint i = 0; i < checksums_blocked->len; i++) {
const gchar *csum = g_ptr_array_index (checksums_blocked, i);
fu_engine_add_blocked_firmware (self, csum);
}
/* set up idle exit */ /* set up idle exit */
if ((self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) == 0) if ((self->app_flags & FU_APP_FLAGS_NO_IDLE_SOURCES) == 0)
@ -6446,6 +6530,8 @@ fu_engine_finalize (GObject *obj)
g_source_remove (self->coldplug_id); g_source_remove (self->coldplug_id);
if (self->approved_firmware != NULL) if (self->approved_firmware != NULL)
g_hash_table_unref (self->approved_firmware); g_hash_table_unref (self->approved_firmware);
if (self->blocked_firmware != NULL)
g_hash_table_unref (self->blocked_firmware);
g_free (self->host_machine_id); g_free (self->host_machine_id);
g_free (self->host_security_id); g_free (self->host_security_id);

View File

@ -159,6 +159,14 @@ gboolean fu_engine_activate (FuEngine *self,
GPtrArray *fu_engine_get_approved_firmware (FuEngine *self); GPtrArray *fu_engine_get_approved_firmware (FuEngine *self);
void fu_engine_add_approved_firmware (FuEngine *self, void fu_engine_add_approved_firmware (FuEngine *self,
const gchar *checksum); const gchar *checksum);
void fu_engine_set_approved_firmware (FuEngine *self,
GPtrArray *checksums);
GPtrArray *fu_engine_get_blocked_firmware (FuEngine *self);
void fu_engine_add_blocked_firmware (FuEngine *self,
const gchar *checksum);
gboolean fu_engine_set_blocked_firmware (FuEngine *self,
GPtrArray *checksums,
GError **error);
gchar *fu_engine_self_sign (FuEngine *self, gchar *fu_engine_self_sign (FuEngine *self,
const gchar *value, const gchar *value,
JcatSignFlags flags, JcatSignFlags flags,

View File

@ -20,7 +20,7 @@
#include "fu-history.h" #include "fu-history.h"
#include "fu-mutex.h" #include "fu-mutex.h"
#define FU_HISTORY_CURRENT_SCHEMA_VERSION 5 #define FU_HISTORY_CURRENT_SCHEMA_VERSION 6
static void fu_history_finalize (GObject *object); static void fu_history_finalize (GObject *object);
@ -181,6 +181,8 @@ fu_history_create_database (FuHistory *self, GError **error)
"protocol TEXT DEFAULT NULL);" "protocol TEXT DEFAULT NULL);"
"CREATE TABLE IF NOT EXISTS approved_firmware (" "CREATE TABLE IF NOT EXISTS approved_firmware ("
"checksum TEXT);" "checksum TEXT);"
"CREATE TABLE IF NOT EXISTS blocked_firmware ("
"checksum TEXT);"
"COMMIT;", NULL, NULL, NULL); "COMMIT;", NULL, NULL, NULL);
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
@ -269,6 +271,22 @@ fu_history_migrate_database_v4 (FuHistory *self, GError **error)
return TRUE; return TRUE;
} }
static gboolean
fu_history_migrate_database_v5 (FuHistory *self, GError **error)
{
gint rc;
rc = sqlite3_exec (self->db,
"CREATE TABLE IF NOT EXISTS blocked_firmware (checksum TEXT);",
NULL, NULL, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
"Failed to create table: %s",
sqlite3_errmsg (self->db));
return FALSE;
}
return TRUE;
}
/* returns 0 if database is not initialised */ /* returns 0 if database is not initialised */
static guint static guint
fu_history_get_schema_version (FuHistory *self) fu_history_get_schema_version (FuHistory *self)
@ -315,16 +333,26 @@ fu_history_create_or_migrate (FuHistory *self, guint schema_ver, GError **error)
return FALSE; return FALSE;
if (!fu_history_migrate_database_v4 (self, error)) if (!fu_history_migrate_database_v4 (self, error))
return FALSE; return FALSE;
if (!fu_history_migrate_database_v5 (self, error))
return FALSE;
} else if (schema_ver == 3) { } else if (schema_ver == 3) {
g_debug ("migrating v%u database by altering", schema_ver); g_debug ("migrating v%u database by altering", schema_ver);
if (!fu_history_migrate_database_v3 (self, error)) if (!fu_history_migrate_database_v3 (self, error))
return FALSE; return FALSE;
if (!fu_history_migrate_database_v4 (self, error)) if (!fu_history_migrate_database_v4 (self, error))
return FALSE; return FALSE;
if (!fu_history_migrate_database_v5 (self, error))
return FALSE;
} else if (schema_ver == 4) { } else if (schema_ver == 4) {
g_debug ("migrating v%u database by altering", schema_ver); g_debug ("migrating v%u database by altering", schema_ver);
if (!fu_history_migrate_database_v4 (self, error)) if (!fu_history_migrate_database_v4 (self, error))
return FALSE; return FALSE;
if (!fu_history_migrate_database_v5 (self, error))
return FALSE;
} else if (schema_ver == 5) {
g_debug ("migrating v%u database by altering", schema_ver);
if (!fu_history_migrate_database_v5 (self, error))
return FALSE;
} else { } else {
/* this is probably okay, but return an error if we ever delete /* this is probably okay, but return an error if we ever delete
* or rename columns */ * or rename columns */
@ -1066,6 +1094,139 @@ fu_history_add_approved_firmware (FuHistory *self,
sqlite3_bind_text (stmt, 1, checksum, -1, SQLITE_STATIC); sqlite3_bind_text (stmt, 1, checksum, -1, SQLITE_STATIC);
return fu_history_stmt_exec (self, stmt, NULL, error); return fu_history_stmt_exec (self, stmt, NULL, error);
} }
/**
* fu_history_get_blocked_firmware:
* @self: A #FuHistory
* @error: A #GError or NULL
*
* Returns blocked firmware records.
*
* Returns: (transfer full) (element-type gchar *): records
*
* Since: 1.4.6
**/
GPtrArray *
fu_history_get_blocked_firmware (FuHistory *self, GError **error)
{
gint rc;
g_autoptr(GRWLockReaderLocker) locker = NULL;
g_autoptr(GPtrArray) array = NULL;
g_autoptr(sqlite3_stmt) stmt = NULL;
g_return_val_if_fail (FU_IS_HISTORY (self), NULL);
/* lazy load */
if (self->db == NULL) {
if (!fu_history_load (self, error))
return NULL;
}
/* get all the blocked firmware */
locker = g_rw_lock_reader_locker_new (&self->db_mutex);
g_return_val_if_fail (locker != NULL, NULL);
rc = sqlite3_prepare_v2 (self->db,
"SELECT checksum FROM blocked_firmware;",
-1, &stmt, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
"Failed to prepare SQL to get checksum: %s",
sqlite3_errmsg (self->db));
return NULL;
}
array = g_ptr_array_new_with_free_func (g_free);
while ((rc = sqlite3_step (stmt)) == SQLITE_ROW) {
const gchar *tmp = (const gchar *) sqlite3_column_text (stmt, 0);
g_ptr_array_add (array, g_strdup (tmp));
}
if (rc != SQLITE_DONE) {
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_WRITE,
"failed to execute prepared statement: %s",
sqlite3_errmsg (self->db));
return NULL;
}
return g_steal_pointer (&array);
}
/**
* fu_history_clear_blocked_firmware:
* @self: A #FuHistory
* @error: A #GError or NULL
*
* Clear all blocked firmware records
*
* Returns: #TRUE for success, #FALSE for failure
*
* Since: 1.4.6
**/
gboolean
fu_history_clear_blocked_firmware (FuHistory *self, GError **error)
{
gint rc;
g_autoptr(sqlite3_stmt) stmt = NULL;
g_autoptr(GRWLockWriterLocker) locker = NULL;
g_return_val_if_fail (FU_IS_HISTORY (self), FALSE);
/* lazy load */
if (!fu_history_load (self, error))
return FALSE;
/* remove entries */
locker = g_rw_lock_writer_locker_new (&self->db_mutex);
g_return_val_if_fail (locker != NULL, FALSE);
rc = sqlite3_prepare_v2 (self->db,
"DELETE FROM blocked_firmware;",
-1, &stmt, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
"Failed to prepare SQL to delete blocked firmware: %s",
sqlite3_errmsg (self->db));
return FALSE;
}
return fu_history_stmt_exec (self, stmt, NULL, error);
}
/**
* fu_history_add_blocked_firmware:
* @self: A #FuHistory
* @checksum: a string
* @error: A #GError or NULL
*
* Add an blocked firmware record to the database
*
* Returns: #TRUE for success, #FALSE for failure
*
* Since: 1.4.6
**/
gboolean
fu_history_add_blocked_firmware (FuHistory *self, const gchar *checksum, GError **error)
{
gint rc;
g_autoptr(sqlite3_stmt) stmt = NULL;
g_autoptr(GRWLockWriterLocker) locker = NULL;
g_return_val_if_fail (FU_IS_HISTORY (self), FALSE);
g_return_val_if_fail (checksum != NULL, FALSE);
/* lazy load */
if (!fu_history_load (self, error))
return FALSE;
/* add */
locker = g_rw_lock_writer_locker_new (&self->db_mutex);
g_return_val_if_fail (locker != NULL, FALSE);
rc = sqlite3_prepare_v2 (self->db,
"INSERT INTO blocked_firmware (checksum) "
"VALUES (?1)", -1, &stmt, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
"Failed to prepare SQL to insert checksum: %s",
sqlite3_errmsg (self->db));
return FALSE;
}
sqlite3_bind_text (stmt, 1, checksum, -1, SQLITE_STATIC);
return fu_history_stmt_exec (self, stmt, NULL, error);
}
static void static void
fu_history_class_init (FuHistoryClass *klass) fu_history_class_init (FuHistoryClass *klass)

View File

@ -47,3 +47,10 @@ gboolean fu_history_add_approved_firmware (FuHistory *self,
GError **error); GError **error);
GPtrArray *fu_history_get_approved_firmware (FuHistory *self, GPtrArray *fu_history_get_approved_firmware (FuHistory *self,
GError **error); GError **error);
gboolean fu_history_clear_blocked_firmware (FuHistory *self,
GError **error);
gboolean fu_history_add_blocked_firmware (FuHistory *self,
const gchar *checksum,
GError **error);
GPtrArray *fu_history_get_blocked_firmware (FuHistory *self,
GError **error);

View File

@ -430,6 +430,30 @@ fu_main_authorize_set_approved_firmware_cb (GObject *source, GAsyncResult *res,
g_dbus_method_invocation_return_value (helper->invocation, NULL); g_dbus_method_invocation_return_value (helper->invocation, NULL);
} }
static void
fu_main_authorize_set_blocked_firmware_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data;
g_autoptr(GError) error = NULL;
g_autoptr(PolkitAuthorizationResult) auth = NULL;
/* get result */
fu_main_set_status (helper->priv, FWUPD_STATUS_IDLE);
auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source),
res, &error);
if (!fu_main_authorization_is_valid (auth, &error)) {
g_dbus_method_invocation_return_gerror (helper->invocation, error);
return;
}
/* success */
if (!fu_engine_set_blocked_firmware (helper->priv->engine, helper->checksums, &error)) {
g_dbus_method_invocation_return_gerror (helper->invocation, error);
return;
}
g_dbus_method_invocation_return_value (helper->invocation, NULL);
}
static void static void
fu_main_authorize_self_sign_cb (GObject *source, GAsyncResult *res, gpointer user_data) fu_main_authorize_self_sign_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{ {
@ -878,6 +902,19 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
g_variant_new_tuple (&val, 1)); g_variant_new_tuple (&val, 1));
return; return;
} }
if (g_strcmp0 (method_name, "GetBlockedFirmware") == 0) {
GVariantBuilder builder;
GPtrArray *checksums = fu_engine_get_blocked_firmware (priv->engine);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
for (guint i = 0; i < checksums->len; i++) {
const gchar *checksum = g_ptr_array_index (checksums, i);
g_variant_builder_add_value (&builder, g_variant_new_string (checksum));
}
val = g_variant_builder_end (&builder);
g_dbus_method_invocation_return_value (invocation,
g_variant_new_tuple (&val, 1));
return;
}
if (g_strcmp0 (method_name, "GetReportMetadata") == 0) { if (g_strcmp0 (method_name, "GetReportMetadata") == 0) {
GHashTableIter iter; GHashTableIter iter;
GVariantBuilder builder; GVariantBuilder builder;
@ -932,6 +969,35 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
g_steal_pointer (&helper)); g_steal_pointer (&helper));
return; return;
} }
if (g_strcmp0 (method_name, "SetBlockedFirmware") == 0) {
g_autofree gchar *checksums_str = NULL;
g_auto(GStrv) checksums = NULL;
g_autoptr(FuMainAuthHelper) helper = NULL;
g_autoptr(PolkitSubject) subject = NULL;
g_variant_get (parameters, "(^as)", &checksums);
checksums_str = g_strjoinv (",", checksums);
g_debug ("Called %s(%s)", method_name, checksums_str);
/* authenticate */
fu_main_set_status (priv, FWUPD_STATUS_WAITING_FOR_AUTH);
helper = g_new0 (FuMainAuthHelper, 1);
helper->priv = priv;
helper->request = g_steal_pointer (&request);
helper->invocation = g_object_ref (invocation);
helper->checksums = g_ptr_array_new_with_free_func (g_free);
for (guint i = 0; checksums[i] != NULL; i++)
g_ptr_array_add (helper->checksums, g_strdup (checksums[i]));
subject = polkit_system_bus_name_new (sender);
polkit_authority_check_authorization (priv->authority, subject,
"org.freedesktop.fwupd.set-approved-firmware",
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
NULL,
fu_main_authorize_set_blocked_firmware_cb,
g_steal_pointer (&helper));
return;
}
if (g_strcmp0 (method_name, "SelfSign") == 0) { if (g_strcmp0 (method_name, "SelfSign") == 0) {
GVariant *prop_value; GVariant *prop_value;
gchar *prop_key; gchar *prop_key;

View File

@ -2311,6 +2311,162 @@ fu_util_check_polkit_actions (GError **error)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
#pragma clang diagnostic pop #pragma clang diagnostic pop
static gchar *
fu_util_get_history_checksum (FuUtilPrivate *priv, GError **error)
{
const gchar *csum;
g_autoptr(FwupdDevice) device = NULL;
g_autoptr(FwupdRelease) release = NULL;
g_autoptr(GPtrArray) devices = NULL;
devices = fwupd_client_get_history (priv->client, NULL, error);
if (devices == NULL)
return NULL;
device = fu_util_prompt_for_device (priv, devices, error);
if (device == NULL)
return NULL;
release = fu_util_prompt_for_release (priv, fwupd_device_get_releases (device), error);
if (release == NULL)
return NULL;
csum = fwupd_checksum_get_best (fwupd_release_get_checksums (release));
if (csum == NULL) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
"No suitable checksums");
return NULL;
}
return g_strdup (csum);
}
static gboolean
fu_util_block_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
guint idx = 0;
g_autofree gchar *csum = NULL;
g_auto(GStrv) csums_new = NULL;
g_auto(GStrv) csums = NULL;
/* get existing checksums */
csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
if (csums == NULL)
return FALSE;
/* get new value */
if (g_strv_length (values) == 0) {
csum = fu_util_get_history_checksum (priv, error);
if (csum == NULL)
return FALSE;
} else {
csum = g_strdup (values[0]);
}
/* ensure it's not already there */
if (g_strv_contains ((const gchar * const *) csums, csum)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: user selected something not possible */
_("Firmware is already blocked"));
return FALSE;
}
/* TRANSLATORS: we will not offer this firmware to the user */
g_print ("%s %s\n", _("Blocking firmware:"), csum);
/* remove it from the new list */
csums_new = g_new0 (gchar *, g_strv_length (csums) + 2);
for (guint i = 0; csums[i] != NULL; i++) {
if (g_strcmp0 (csums[i], csum) != 0)
csums_new[idx++] = g_strdup (csums[i]);
}
csums_new[idx] = g_strdup (csum);
return fwupd_client_set_blocked_firmware (priv->client, csums_new,
priv->cancellable, error);
}
static gboolean
fu_util_unblock_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
guint idx = 0;
g_auto(GStrv) csums = NULL;
g_auto(GStrv) csums_new = NULL;
g_autofree gchar *csum = NULL;
/* get existing checksums */
csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
if (csums == NULL)
return FALSE;
/* empty list */
if (g_strv_length (csums) == 0) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: nothing to show */
_("There are no blocked firmware files"));
return FALSE;
}
/* get new value */
if (g_strv_length (values) == 0) {
csum = fu_util_get_history_checksum (priv, error);
if (csum == NULL)
return FALSE;
} else {
csum = g_strdup (values[0]);
}
/* ensure it's there */
if (!g_strv_contains ((const gchar * const *) csums, csum)) {
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_NOTHING_TO_DO,
/* TRANSLATORS: user selected something not possible */
_("Firmware is not already blocked"));
return FALSE;
}
/* TRANSLATORS: we will not offer this firmware to the user */
g_print ("%s %s\n", _("Unblocking firmware:"), csum);
/* remove it from the new list */
csums_new = g_new0 (gchar *, g_strv_length (csums));
for (guint i = 0; csums[i] != NULL; i++) {
if (g_strcmp0 (csums[i], csum) != 0)
csums_new[idx++] = g_strdup (csums[i]);
}
return fwupd_client_set_blocked_firmware (priv->client, csums_new,
priv->cancellable, error);
}
static gboolean
fu_util_get_blocked_firmware (FuUtilPrivate *priv, gchar **values, GError **error)
{
g_auto(GStrv) csums = NULL;
/* get checksums */
csums = fwupd_client_get_blocked_firmware (priv->client, priv->cancellable, error);
if (csums == NULL)
return FALSE;
/* empty list */
if (g_strv_length (csums) == 0) {
/* TRANSLATORS: nothing to show */
g_print ("%s\n", _("There are no blocked firmware files"));
return TRUE;
}
/* TRANSLATORS: there follows a list of hashes */
g_print ("%s\n", _("Blocked firmware files:"));
for (guint i = 0; csums[i] != NULL; i++) {
g_print ("%u.\t%s\n", i + 1, csums[i]);
}
/* success */
return TRUE;
}
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
@ -2557,6 +2713,24 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */ /* TRANSLATORS: command description */
_("Gets the host security attributes."), _("Gets the host security attributes."),
fu_util_security); fu_util_security);
fu_util_cmd_array_add (cmd_array,
"block-firmware",
"[CHECKSUM]",
/* TRANSLATORS: command description */
_("Blocks a specific firmware from being installed."),
fu_util_block_firmware);
fu_util_cmd_array_add (cmd_array,
"unblock-firmware",
"[CHECKSUM]",
/* TRANSLATORS: command description */
_("Blocks a specific firmware from being installed."),
fu_util_unblock_firmware);
fu_util_cmd_array_add (cmd_array,
"get-blocked-firmware",
NULL,
/* TRANSLATORS: command description */
_("Gets the list of blocked firmware."),
fu_util_get_blocked_firmware);
/* do stuff on ctrl+c */ /* do stuff on ctrl+c */
priv->cancellable = g_cancellable_new (); priv->cancellable = g_cancellable_new ();

View File

@ -489,6 +489,42 @@
</arg> </arg>
</method> </method>
<!--***********************************************************-->
<method name='GetBlockedFirmware'>
<doc:doc>
<doc:description>
<doc:para>
Gets the list of blocked firmware.
</doc:para>
</doc:description>
</doc:doc>
<arg type='as' name='checksums' direction='out'>
<doc:doc>
<doc:summary>
<doc:para>The checksums of the archives</doc:para>
</doc:summary>
</doc:doc>
</arg>
</method>
<!--***********************************************************-->
<method name='SetBlockedFirmware'>
<doc:doc>
<doc:description>
<doc:para>
Sets the list of blocked firmware that can be applied to devices.
</doc:para>
</doc:description>
</doc:doc>
<arg type='as' name='checksums' direction='in'>
<doc:doc>
<doc:summary>
<doc:para>The checksums of the archives</doc:para>
</doc:summary>
</doc:doc>
</arg>
</method>
<!--***********************************************************--> <!--***********************************************************-->
<method name='SetFeatureFlags'> <method name='SetFeatureFlags'>
<doc:doc> <doc:doc>