mirror of
https://git.proxmox.com/git/fwupd
synced 2025-08-05 19:48:47 +00:00
Allow blocking specific firmware releases by checksum
Fixes https://github.com/fwupd/fwupd/issues/2280
This commit is contained in:
parent
27fd95ae88
commit
3120683143
@ -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'
|
||||
|
@ -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=
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
104
src/fu-engine.c
104
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);
|
||||
|
@ -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,
|
||||
|
163
src/fu-history.c
163
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)
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
174
src/fu-util.c
174
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 ();
|
||||
|
@ -489,6 +489,42 @@
|
||||
</arg>
|
||||
</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'>
|
||||
<doc:doc>
|
||||
|
Loading…
Reference in New Issue
Block a user