diff --git a/data/bash-completion/fwupdmgr.in b/data/bash-completion/fwupdmgr.in
index ccdd89c55..623a75917 100644
--- a/data/bash-completion/fwupdmgr.in
+++ b/data/bash-completion/fwupdmgr.in
@@ -1,5 +1,6 @@
_fwupdmgr_cmd_list=(
'activate'
+ 'block-firmware'
'clear-history'
'clear-offline'
'clear-results'
@@ -7,6 +8,7 @@ _fwupdmgr_cmd_list=(
'downgrade'
'enable-remote'
'get-approved-firmware'
+ 'get-blocked-firmware'
'get-details'
'get-devices'
'get-history'
@@ -25,6 +27,7 @@ _fwupdmgr_cmd_list=(
'security'
'set-approved-firmware'
'unlock'
+ 'unblock-firmware'
'update'
'upgrade'
'verify'
diff --git a/data/daemon.conf b/data/daemon.conf
index 93777595c..6a576cfd5 100644
--- a/data/daemon.conf
+++ b/data/daemon.conf
@@ -32,3 +32,7 @@ EnumerateAllDevices=false
# A list of firmware checksums that has been approved by the site admin
# If unset, all firmware is approved
ApprovedFirmware=
+
+# Allow blocking specific devices by their checksum, either SHA1 or SHA256
+# Uses semicolons as delimiter
+BlockedFirmware=
diff --git a/libfwupd/fwupd-client.c b/libfwupd/fwupd-client.c
index eef46d0d9..bf96ec381 100644
--- a/libfwupd/fwupd-client.c
+++ b/libfwupd/fwupd-client.c
@@ -2128,6 +2128,98 @@ fwupd_client_set_approved_firmware (FwupdClient *client,
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:
* @client: A #FwupdClient
diff --git a/libfwupd/fwupd-client.h b/libfwupd/fwupd-client.h
index 43011c190..235568e68 100644
--- a/libfwupd/fwupd-client.h
+++ b/libfwupd/fwupd-client.h
@@ -205,6 +205,13 @@ gboolean fwupd_client_set_approved_firmware (FwupdClient *client,
gchar **checksums,
GCancellable *cancellable,
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,
const gchar *value,
FwupdSelfSignFlags flags,
diff --git a/libfwupd/fwupd.map b/libfwupd/fwupd.map
index a70b12756..1b669fe8a 100644
--- a/libfwupd/fwupd.map
+++ b/libfwupd/fwupd.map
@@ -469,6 +469,13 @@ LIBFWUPD_1.4.5 {
local: *;
} 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 {
global:
fwupd_client_get_host_security_attrs;
@@ -508,4 +515,4 @@ LIBFWUPD_1.5.0 {
fwupd_security_attr_to_string;
fwupd_security_attr_to_variant;
local: *;
-} LIBFWUPD_1.4.5;
+} LIBFWUPD_1.4.6;
diff --git a/src/fu-config.c b/src/fu-config.c
index b5bdf24ed..646940c03 100644
--- a/src/fu-config.c
+++ b/src/fu-config.c
@@ -30,6 +30,7 @@ struct _FuConfig
GPtrArray *disabled_devices; /* (element-type utf-8) */
GPtrArray *disabled_plugins; /* (element-type utf-8) */
GPtrArray *approved_firmware; /* (element-type utf-8) */
+ GPtrArray *blocked_firmware; /* (element-type utf-8) */
guint64 archive_size_max;
guint idle_timeout;
gchar *config_file;
@@ -52,6 +53,7 @@ fu_config_reload (FuConfig *self, GError **error)
guint64 archive_size_max;
guint idle_timeout;
g_auto(GStrv) approved_firmware = NULL;
+ g_auto(GStrv) blocked_firmware = NULL;
g_auto(GStrv) devices = NULL;
g_auto(GStrv) plugins = 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 */
archive_size_max = g_key_file_get_uint64 (keyfile,
"fwupd",
@@ -227,6 +243,13 @@ fu_config_get_disabled_devices (FuConfig *self)
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
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_plugins = 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
@@ -294,6 +318,7 @@ fu_config_finalize (GObject *obj)
g_ptr_array_unref (self->disabled_devices);
g_ptr_array_unref (self->disabled_plugins);
g_ptr_array_unref (self->approved_firmware);
+ g_ptr_array_unref (self->blocked_firmware);
g_free (self->config_file);
G_OBJECT_CLASS (fu_config_parent_class)->finalize (obj);
diff --git a/src/fu-config.h b/src/fu-config.h
index 72b2c28ab..6c8d6e340 100644
--- a/src/fu-config.h
+++ b/src/fu-config.h
@@ -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_plugins (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_enumerate_all_devices (FuConfig *self);
diff --git a/src/fu-engine.c b/src/fu-engine.c
index d0f7c5975..a87a9ce22 100644
--- a/src/fu-engine.c
+++ b/src/fu-engine.c
@@ -103,6 +103,7 @@ struct _FuEngine
GHashTable *runtime_versions;
GHashTable *compile_versions;
GHashTable *approved_firmware; /* (nullable) */
+ GHashTable *blocked_firmware; /* (nullable) */
GHashTable *firmware_gtypes;
gchar *host_machine_id;
JcatContext *jcat_context;
@@ -527,6 +528,7 @@ fu_engine_modify_config (FuEngine *self, const gchar *key, const gchar *value, G
const gchar *keys[] = {
"ArchiveSizeMax",
"DisabledDevices",
+ "BlockedFirmware",
"DisabledPlugins",
"IdleTimeout",
"VerboseDomains",
@@ -4178,6 +4180,21 @@ fu_engine_check_release_is_approved (FuEngine *self, FwupdRelease *rel)
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
fu_engine_add_releases_for_device_component (FuEngine *self,
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);
}
+ /* 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 */
remote_id = fwupd_release_get_remote_id (rel);
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));
}
+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 *
fu_engine_self_sign (FuEngine *self,
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;
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
g_autoptr(GError) error_local = NULL;
#endif
@@ -6159,21 +6231,33 @@ fu_engine_load (FuEngine *self, FuEngineLoadFlags flags, GError **error)
/* create client certificate */
fu_engine_ensure_client_certificate (self);
- /* get hardcoded approved firmware */
- checksums = fu_config_get_approved_firmware (self->config);
- for (guint i = 0; i < checksums->len; i++) {
- const gchar *csum = g_ptr_array_index (checksums, i);
+ /* get hardcoded approved and blocked firmware */
+ checksums_approved = fu_config_get_approved_firmware (self->config);
+ for (guint i = 0; i < checksums_approved->len; i++) {
+ const gchar *csum = g_ptr_array_index (checksums_approved, i);
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 */
- checksums = fu_history_get_approved_firmware (self->history, error);
- if (checksums == NULL)
+ checksums_approved = fu_history_get_approved_firmware (self->history, error);
+ if (checksums_approved == NULL)
return FALSE;
- for (guint i = 0; i < checksums->len; i++) {
- const gchar *csum = g_ptr_array_index (checksums, i);
+ for (guint i = 0; i < checksums_approved->len; i++) {
+ const gchar *csum = g_ptr_array_index (checksums_approved, i);
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 */
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);
if (self->approved_firmware != NULL)
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_security_id);
diff --git a/src/fu-engine.h b/src/fu-engine.h
index eaad096e8..08462198b 100644
--- a/src/fu-engine.h
+++ b/src/fu-engine.h
@@ -159,6 +159,14 @@ gboolean fu_engine_activate (FuEngine *self,
GPtrArray *fu_engine_get_approved_firmware (FuEngine *self);
void fu_engine_add_approved_firmware (FuEngine *self,
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,
const gchar *value,
JcatSignFlags flags,
diff --git a/src/fu-history.c b/src/fu-history.c
index 96db6bd91..6de1970d5 100644
--- a/src/fu-history.c
+++ b/src/fu-history.c
@@ -20,7 +20,7 @@
#include "fu-history.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);
@@ -181,6 +181,8 @@ fu_history_create_database (FuHistory *self, GError **error)
"protocol TEXT DEFAULT NULL);"
"CREATE TABLE IF NOT EXISTS approved_firmware ("
"checksum TEXT);"
+ "CREATE TABLE IF NOT EXISTS blocked_firmware ("
+ "checksum TEXT);"
"COMMIT;", NULL, NULL, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL,
@@ -269,6 +271,22 @@ fu_history_migrate_database_v4 (FuHistory *self, GError **error)
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 */
static guint
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;
if (!fu_history_migrate_database_v4 (self, error))
return FALSE;
+ if (!fu_history_migrate_database_v5 (self, error))
+ return FALSE;
} else if (schema_ver == 3) {
g_debug ("migrating v%u database by altering", schema_ver);
if (!fu_history_migrate_database_v3 (self, error))
return FALSE;
if (!fu_history_migrate_database_v4 (self, error))
return FALSE;
+ if (!fu_history_migrate_database_v5 (self, error))
+ return FALSE;
} else if (schema_ver == 4) {
g_debug ("migrating v%u database by altering", schema_ver);
if (!fu_history_migrate_database_v4 (self, error))
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 {
/* this is probably okay, but return an error if we ever delete
* or rename columns */
@@ -1066,6 +1094,139 @@ fu_history_add_approved_firmware (FuHistory *self,
sqlite3_bind_text (stmt, 1, checksum, -1, SQLITE_STATIC);
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
fu_history_class_init (FuHistoryClass *klass)
diff --git a/src/fu-history.h b/src/fu-history.h
index f5b92fa12..84e4ae935 100644
--- a/src/fu-history.h
+++ b/src/fu-history.h
@@ -47,3 +47,10 @@ gboolean fu_history_add_approved_firmware (FuHistory *self,
GError **error);
GPtrArray *fu_history_get_approved_firmware (FuHistory *self,
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);
diff --git a/src/fu-main.c b/src/fu-main.c
index fe4e7316c..3f5cce94b 100644
--- a/src/fu-main.c
+++ b/src/fu-main.c
@@ -430,6 +430,30 @@ fu_main_authorize_set_approved_firmware_cb (GObject *source, GAsyncResult *res,
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
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));
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) {
GHashTableIter iter;
GVariantBuilder builder;
@@ -932,6 +969,35 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender,
g_steal_pointer (&helper));
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) {
GVariant *prop_value;
gchar *prop_key;
diff --git a/src/fu-util.c b/src/fu-util.c
index 11ae78fc2..bff5163a1 100644
--- a/src/fu-util.c
+++ b/src/fu-util.c
@@ -2311,6 +2311,162 @@ fu_util_check_polkit_actions (GError **error)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free)
#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
main (int argc, char *argv[])
{
@@ -2557,6 +2713,24 @@ main (int argc, char *argv[])
/* TRANSLATORS: command description */
_("Gets the host security attributes."),
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 */
priv->cancellable = g_cancellable_new ();
diff --git a/src/org.freedesktop.fwupd.xml b/src/org.freedesktop.fwupd.xml
index 79904c793..6b0f7448e 100644
--- a/src/org.freedesktop.fwupd.xml
+++ b/src/org.freedesktop.fwupd.xml
@@ -489,6 +489,42 @@
+
+
+
+
+
+ Gets the list of blocked firmware.
+
+
+
+
+
+
+ The checksums of the archives
+
+
+
+
+
+
+
+
+
+
+ Sets the list of blocked firmware that can be applied to devices.
+
+
+
+
+
+
+ The checksums of the archives
+
+
+
+
+