/
githubmirror
/
fwupd
Обзор
Документация
Войти
/
githubmirror
/
fwupd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/fu-dbus-daemon.c
3 301 строка
103 KB
Richard Hughes
trivial: Drop an unused parameter to fu_engine_install_releases()
31 июл 2026, 08:31
31 июл 2026, 08:31
b0e8a55
Код
Авторство
О чём код?
/* * Copyright 2015 Richard Hughes <richard@hughsie.com> * * SPDX-License-Identifier: LGPL-2.1-or-later */ #define G_LOG_DOMAIN "FuMain" #include "config.h" #include <fwupdplugin.h> #ifdef HAVE_GIO_UNIX #include <gio/gunixfdlist.h> #include <gio/gunixoutputstream.h> #endif #include <glib/gstdio.h> #include "fwupd-enums-private.h" #include "fu-client-list.h" #include "fu-context-private.h" #include "fu-dbus-daemon.h" #include "fu-device-private.h" #include "fu-engine-helper.h" #include "fu-engine-requirements.h" #include "fu-polkit-authority.h" #include "fu-release.h" #include "fu-security-attrs-private.h" #ifdef HAVE_GIO_UNIX #include "fu-unix-seekable-input-stream.h" #endif struct _FuDbusDaemon { FuDaemon parent_instance; GDBusConnection *connection; GDBusNodeInfo *introspection_daemon; GDBusProxy *proxy_uid; FuClientList *client_list; guint32 clients_inhibit_id; FuPolkitAuthority *authority; guint owner_id; GPtrArray *system_inhibits; }; G_DEFINE_TYPE(FuDbusDaemon, fu_dbus_daemon, FU_TYPE_DAEMON) #define FU_DBUS_DAEMON_SYSTEM_INHIBIT_MAX_TOTAL 100 #define FU_DBUS_DAEMON_SYSTEM_INHIBIT_MAX_PER_SENDER 10 #define FU_DBUS_DAEMON_SET_HINTS_MAX 32 static void fu_dbus_daemon_engine_changed_cb(FuEngine *engine, FuDbusDaemon *self) { /* not yet connected */ if (self->connection == NULL) return; g_dbus_connection_emit_signal(self->connection, NULL, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, "Changed", NULL, NULL); fu_daemon_schedule_housekeeping(FU_DAEMON(self)); } static void fu_dbus_daemon_engine_device_added_cb(FuEngine *engine, FuDevice *device, FuDbusDaemon *self) { GVariant *val; /* not yet connected */ if (self->connection == NULL) return; val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_NONE); g_dbus_connection_emit_signal(self->connection, NULL, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, "DeviceAdded", g_variant_new_tuple(&val, 1), NULL); fu_daemon_schedule_housekeeping(FU_DAEMON(self)); } static void fu_dbus_daemon_engine_device_removed_cb(FuEngine *engine, FuDevice *device, FuDbusDaemon *self) { GVariant *val; /* not yet connected */ if (self->connection == NULL) return; val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_NONE); g_dbus_connection_emit_signal(self->connection, NULL, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, "DeviceRemoved", g_variant_new_tuple(&val, 1), NULL); fu_daemon_schedule_housekeeping(FU_DAEMON(self)); } static void fu_dbus_daemon_engine_device_changed_cb(FuEngine *engine, FuDevice *device, FuDbusDaemon *self) { GVariant *val; /* not yet connected */ if (self->connection == NULL) return; val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_NONE); g_dbus_connection_emit_signal(self->connection, NULL, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, "DeviceChanged", g_variant_new_tuple(&val, 1), NULL); fu_daemon_schedule_housekeeping(FU_DAEMON(self)); } static void fu_dbus_daemon_engine_device_request_cb(FuEngine *engine, FwupdRequest *request, FuDbusDaemon *self) { GVariant *val; /* not yet connected */ if (self->connection == NULL) return; val = fwupd_codec_to_variant(FWUPD_CODEC(request), FWUPD_CODEC_FLAG_NONE); g_dbus_connection_emit_signal(self->connection, NULL, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, "DeviceRequest", g_variant_new_tuple(&val, 1), NULL); } static void fu_dbus_daemon_emit_property_changed(FuDbusDaemon *self, const gchar *property_name, GVariant *property_value) { GVariantBuilder builder; GVariantBuilder invalidated_builder; /* not yet connected */ if (self->connection == NULL) { g_variant_unref(g_variant_ref_sink(property_value)); return; } /* build the dict */ g_variant_builder_init(&invalidated_builder, G_VARIANT_TYPE("as")); g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add(&builder, "{sv}", property_name, property_value); g_dbus_connection_emit_signal( self->connection, NULL, FWUPD_DBUS_PATH, "org.freedesktop.DBus.Properties", "PropertiesChanged", g_variant_new("(sa{sv}as)", FWUPD_DBUS_INTERFACE, &builder, &invalidated_builder), NULL); g_variant_builder_clear(&builder); g_variant_builder_clear(&invalidated_builder); } static void fu_dbus_daemon_engine_status_changed_cb(FuEngine *engine, FwupdStatus status, FuDbusDaemon *self) { fu_daemon_set_status(FU_DAEMON(self), status); /* engine has gone idle */ if (status == FWUPD_STATUS_SHUTDOWN) fu_daemon_stop(FU_DAEMON(self), NULL); } static FuEngineRequest * fu_dbus_daemon_create_request(FuDbusDaemon *self, const gchar *sender, GError **error) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); FwupdCodecFlags converter_flags = FWUPD_CODEC_FLAG_NONE; guint calling_uid = 0; g_autoptr(FuClient) client = NULL; g_autoptr(FuEngineRequest) request = fu_engine_request_new(sender); g_autoptr(GVariant) value = NULL; /* if using FWUPD_DBUS_SOCKET... */ if (sender == NULL) { #ifdef HAVE_GIO_UNIX GCredentials *credentials = g_dbus_connection_get_peer_credentials(self->connection); if (credentials != NULL) { calling_uid = g_credentials_get_unix_user(credentials, error); if (calling_uid == (guint)-1) return NULL; if (fu_engine_is_uid_trusted(engine, calling_uid)) converter_flags |= FWUPD_CODEC_FLAG_TRUSTED; } #endif fu_engine_request_set_converter_flags(request, converter_flags); return g_object_ref(request); } /* did the client set the list of supported features or any hints */ client = fu_client_list_get_by_sender(self->client_list, sender); if (client != NULL) { const gchar *locale = fu_client_lookup_hint(client, "locale"); if (locale != NULL) fu_engine_request_set_locale(request, locale); fu_engine_request_set_feature_flags(request, fu_client_get_feature_flags(client)); } /* are we root and therefore trusted? */ if (self->proxy_uid == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "org.freedesktop.DBus is not available"); return NULL; } value = g_dbus_proxy_call_sync(self->proxy_uid, "GetConnectionUnixUser", g_variant_new("(s)", sender), G_DBUS_CALL_FLAGS_NONE, 2000, NULL, error); if (value == NULL) { g_prefix_error_literal(error, "failed to read user id of caller: "); return NULL; } g_variant_get(value, "(u)", &calling_uid); if (fu_engine_is_uid_trusted(engine, calling_uid)) converter_flags |= FWUPD_CODEC_FLAG_TRUSTED; fu_engine_request_set_converter_flags(request, converter_flags); /* success */ return g_object_ref(request); } static GVariant * fu_dbus_daemon_device_array_to_variant(FuDbusDaemon *self, FuEngineRequest *request, GPtrArray *devices, GError **error) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); FuContext *ctx = fu_engine_get_context(engine); FwupdCodecFlags flags = fu_engine_request_get_converter_flags(request); if (fu_context_get_config_bool(ctx, "ShowDevicePrivate")) flags |= FWUPD_CODEC_FLAG_TRUSTED; return fwupd_codec_array_to_variant(devices, flags); } typedef struct { GDBusMethodInvocation *invocation; FuEngineRequest *request; FuProgress *progress; FuClient *client; gulong client_sender_changed_id; GPtrArray *releases; GPtrArray *action_ids; GPtrArray *checksums; GPtrArray *errors; guint64 flags; FuInputStream *stream; FuDbusDaemon *self; gchar *device_id; gchar *remote_id; gchar *section; gchar *key; gchar *value; gint32 handle; FuCabinet *cabinet; GHashTable *bios_settings; /* str:str */ gboolean is_fix; } FuMainAuthHelper; static void fu_dbus_daemon_auth_helper_free(FuMainAuthHelper *helper) { /* always return to IDLE even in event of an auth error */ fu_daemon_set_status(FU_DAEMON(helper->self), FWUPD_STATUS_IDLE); if (helper->cabinet != NULL) g_object_unref(helper->cabinet); if (helper->stream != NULL) g_object_unref(helper->stream); if (helper->request != NULL) g_object_unref(helper->request); if (helper->progress != NULL) g_object_unref(helper->progress); if (helper->releases != NULL) g_ptr_array_unref(helper->releases); if (helper->action_ids != NULL) g_ptr_array_unref(helper->action_ids); if (helper->checksums != NULL) g_ptr_array_unref(helper->checksums); if (helper->errors != NULL) g_ptr_array_unref(helper->errors); if (helper->client_sender_changed_id > 0) g_clear_signal_handler(&helper->client_sender_changed_id, helper->client); if (helper->client != NULL) g_object_unref(helper->client); g_free(helper->device_id); g_free(helper->remote_id); g_free(helper->section); g_free(helper->key); g_free(helper->value); g_object_unref(helper->invocation); if (helper->bios_settings != NULL) g_hash_table_unref(helper->bios_settings); g_free(helper); } static void fu_dbus_daemon_method_invocation_return_gerror(GDBusMethodInvocation *invocation, GError *error) { fwupd_error_convert(&error); g_dbus_method_invocation_return_gerror(invocation, error); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainAuthHelper, fu_dbus_daemon_auth_helper_free) #pragma clang diagnostic pop static void fu_dbus_daemon_authorize_unlock_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ if (!fu_engine_unlock(engine, helper->device_id, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_get_bios_settings_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; g_autoptr(FuBiosSettings) attrs = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); FuContext *ctx = fu_engine_get_context(engine); GVariant *val = NULL; /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ attrs = fu_context_get_bios_settings(ctx); val = fwupd_codec_to_variant(FWUPD_CODEC(attrs), FWUPD_CODEC_FLAG_TRUSTED); g_dbus_method_invocation_return_value(helper->invocation, val); } static void fu_dbus_daemon_authorize_set_bios_settings_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ if (!fu_engine_modify_bios_settings(engine, helper->bios_settings, FALSE, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_set_approved_firmware_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ for (guint i = 0; i < helper->checksums->len; i++) { const gchar *csum = g_ptr_array_index(helper->checksums, i); fu_engine_add_approved_firmware(engine, csum); } g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_fix_host_security_attr_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ if (!fu_engine_fix_host_security_attr(engine, helper->key, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_undo_host_security_attr_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ if (!fu_engine_undo_host_security_attr(engine, helper->key, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_self_sign_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autofree gchar *sig = NULL; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ sig = fu_engine_self_sign(engine, helper->value, helper->flags, &error); if (sig == NULL) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, g_variant_new("(s)", sig)); } static void fu_dbus_daemon_modify_config_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } if (!fu_engine_modify_config(engine, helper->section, helper->key, helper->value, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_reset_config_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } if (!fu_engine_reset_config(engine, helper->section, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_progress_percentage_changed_cb(FuProgress *progress, gdouble percentage, FuDbusDaemon *self) { fu_daemon_set_percentage(FU_DAEMON(self), percentage); } static void fu_dbus_daemon_progress_status_changed_cb(FuProgress *progress, FwupdStatus status, FuDbusDaemon *self) { fu_daemon_set_status(FU_DAEMON(self), status); } static void fu_dbus_daemon_authorize_activate_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* progress */ fu_progress_set_profile(progress, g_log_get_debug_enabled()); g_signal_connect(FU_PROGRESS(progress), "percentage-changed", G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb), helper->self); g_signal_connect(FU_PROGRESS(progress), "status-changed", G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb), helper->self); /* authenticated */ if (!fu_engine_activate(engine, helper->device_id, progress, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_verify_update_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* progress */ fu_progress_set_profile(progress, g_log_get_debug_enabled()); g_signal_connect(FU_PROGRESS(progress), "percentage-changed", G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb), helper->self); g_signal_connect(FU_PROGRESS(progress), "status-changed", G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb), helper->self); /* authenticated */ if (!fu_engine_verify_update(engine, helper->device_id, progress, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_modify_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ if (!fu_engine_modify_remote(engine, helper->remote_id, helper->key, helper->value, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_authorize_clean_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ if (!fu_engine_clean_remote(engine, helper->remote_id, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static FuPolkitAuthorityCheckFlags fu_dbus_daemon_engine_request_get_authority_check_flags(FuEngineRequest *request) { FuPolkitAuthorityCheckFlags auth_flags = FU_POLKIT_AUTHORITY_CHECK_FLAG_ALLOW_USER_INTERACTION; if (fu_engine_request_has_converter_flag(request, FWUPD_CODEC_FLAG_TRUSTED)) auth_flags |= FU_POLKIT_AUTHORITY_CHECK_FLAG_USER_IS_TRUSTED; return auth_flags; } #ifdef HAVE_GIO_UNIX static void fu_dbus_daemon_authorize_install_queue(FuMainAuthHelper *helper); static void fu_dbus_daemon_authorize_install_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* do the next authentication action ID */ fu_dbus_daemon_authorize_install_queue(g_steal_pointer(&helper)); } static void fu_dbus_daemon_authorize_install_queue(FuMainAuthHelper *helper_ref) { FuDbusDaemon *self = helper_ref->self; g_autoptr(FuMainAuthHelper) helper = helper_ref; g_autoptr(GError) error = NULL; gboolean ret; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* still more things to authenticate */ if (helper->action_ids->len > 0) { g_autofree gchar *action_id = g_strdup(g_ptr_array_index(helper->action_ids, 0)); g_autofree gchar *sender = g_strdup(fu_client_get_sender(helper->client)); g_autoptr(FuEngineRequest) request = g_object_ref(helper->request); g_ptr_array_remove_index(helper->action_ids, 0); fu_polkit_authority_check( self->authority, sender, action_id, fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_install_cb, g_steal_pointer(&helper)); return; } /* all authenticated, so install all the things */ fu_progress_set_profile(helper->progress, g_log_get_debug_enabled()); g_signal_connect(FU_PROGRESS(helper->progress), "percentage-changed", G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb), helper->self); g_signal_connect(FU_PROGRESS(helper->progress), "status-changed", G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb), helper->self); /* all authenticated, so install all the things */ fu_daemon_set_update_in_progress(FU_DAEMON(self), TRUE); ret = fu_engine_install_releases(engine, helper->request, helper->releases, helper->progress, helper->flags, &error); fu_daemon_set_update_in_progress(FU_DAEMON(self), FALSE); if (fu_daemon_get_pending_stop(FU_DAEMON(self))) { g_set_error_literal(&error, /* nocheck:error-false-return */ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "daemon was stopped"); fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } if (!ret) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } #endif /* HAVE_GIO_UNIX */ #ifdef HAVE_GIO_UNIX static gint fu_dbus_daemon_release_sort_cb(gconstpointer a, gconstpointer b) { FuRelease *release1 = *((FuRelease **)a); FuRelease *release2 = *((FuRelease **)b); return fu_release_compare(release1, release2); } static gboolean fu_dbus_daemon_install_with_helper_device(FuMainAuthHelper *helper, XbNode *component, FuDevice *device, GError **error) { FuDbusDaemon *self = helper->self; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) releases = NULL; /* is this component valid for the device */ fu_release_set_device(release, device); fu_release_set_request(release, helper->request); if (helper->remote_id != NULL) { fu_release_set_remote(release, fu_engine_get_remote_by_id(engine, helper->remote_id, NULL)); } if (!fu_release_load(release, helper->cabinet, component, NULL, helper->flags | FWUPD_INSTALL_FLAG_FORCE, &error_local)) { g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); return TRUE; } if (!fu_engine_requirements_check(engine, release, helper->flags | FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS, &error_local)) { if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("first pass requirement on %s:%s failed: %s", fu_device_get_id(device), xb_node_query_text(component, "id", NULL), error_local->message); } g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); return TRUE; } /* sync update message from CAB, but only if the metadata is trusted */ if (fu_release_has_flag(release, FWUPD_RELEASE_FLAG_TRUSTED_METADATA)) { fu_device_ensure_from_component(device, component); fu_device_incorporate_from_component(device, component); } else { g_debug("not using untrusted metadata"); } /* post-ensure checks */ if (!fu_release_check_version(release, component, helper->flags, &error_local)) { g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); return TRUE; } /* install each intermediate release */ releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)) { g_autoptr(GPtrArray) rels = NULL; g_autoptr(XbQuery) query = NULL; /* we get this one "for free" */ g_ptr_array_add(releases, g_object_ref(release)); query = xb_query_new_full(xb_node_get_silo(component), "releases/release", XB_QUERY_FLAG_FORCE_NODE_CACHE, error); if (query == NULL) return FALSE; rels = xb_node_query_full(component, query, NULL); /* add all but the first entry */ for (guint i = 1; rels != NULL && i < rels->len; i++) { XbNode *rel = g_ptr_array_index(rels, i); g_autoptr(FuRelease) release2 = fu_release_new(); g_autoptr(GError) error_loop = NULL; fu_release_set_device(release2, device); fu_release_set_request(release2, helper->request); if (!fu_release_load(release2, helper->cabinet, component, rel, helper->flags, &error_loop)) { g_ptr_array_add(helper->errors, g_steal_pointer(&error_loop)); continue; } g_ptr_array_add(releases, g_object_ref(release2)); } } else { g_ptr_array_add(releases, g_object_ref(release)); } /* make a second pass */ for (guint i = 0; i < releases->len; i++) { FuRelease *release_tmp = g_ptr_array_index(releases, i); const gchar *action_id; if (!fu_engine_requirements_check(engine, release_tmp, helper->flags, &error_local)) { g_debug("second pass requirement on %s:%s failed: %s", fu_device_get_id(device), xb_node_query_text(component, "id", NULL), error_local->message); g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); continue; } if (!fu_engine_check_trust(engine, release_tmp, &error_local)) { g_ptr_array_add(helper->errors, g_steal_pointer(&error_local)); continue; } /* get the action IDs for the valid device */ action_id = fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) ? "org.freedesktop.fwupd.device-emulate" : fu_release_get_action_id(release_tmp); if (!g_ptr_array_find(helper->action_ids, action_id, NULL)) g_ptr_array_add(helper->action_ids, g_strdup(action_id)); g_ptr_array_add(helper->releases, g_object_ref(release_tmp)); } /* success */ return TRUE; } static gboolean fu_dbus_daemon_install_with_helper(FuMainAuthHelper *helper_ref, GError **error) { FuDbusDaemon *self = helper_ref->self; g_autoptr(FuMainAuthHelper) helper = helper_ref; g_autoptr(GPtrArray) components = NULL; g_autoptr(GPtrArray) devices_possible = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get a list of devices that in some way match the device_id */ if (g_strcmp0(helper->device_id, FWUPD_DEVICE_ID_ANY) == 0) { devices_possible = fu_engine_get_devices(engine, error); if (devices_possible == NULL) return FALSE; } else { g_autoptr(FuDevice) device = NULL; device = fu_engine_get_device(engine, helper->device_id, error); if (device == NULL) return FALSE; devices_possible = fu_engine_get_devices_by_composite_id(engine, fu_device_get_composite_id(device), error); if (devices_possible == NULL) return FALSE; } /* parse silo */ helper->cabinet = fu_engine_build_cabinet_from_stream(engine, helper->stream, error); if (helper->cabinet == NULL) return FALSE; /* for each component in the silo */ components = fu_cabinet_get_components(helper->cabinet, error); if (components == NULL) return FALSE; helper->action_ids = g_ptr_array_new_with_free_func(g_free); helper->releases = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); helper->errors = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); helper->remote_id = fu_engine_get_remote_id_for_stream(engine, helper->stream); /* do any devices pass the requirements */ for (guint i = 0; i < components->len; i++) { XbNode *component = g_ptr_array_index(components, i); for (guint j = 0; j < devices_possible->len; j++) { FuDevice *device = g_ptr_array_index(devices_possible, j); /* emulating */ if ((helper->flags & FWUPD_INSTALL_FLAG_ONLY_EMULATED) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { g_debug("skipping non-emulated %s", fu_device_get_id(device)); continue; } g_debug("testing device %u [%s] with component %u", j, fu_device_get_id(device), i); if (!fu_dbus_daemon_install_with_helper_device(helper, component, device, error)) return FALSE; } } /* order the install tasks by the device priority */ g_ptr_array_sort(helper->releases, fu_dbus_daemon_release_sort_cb); /* nothing suitable */ if (helper->releases->len == 0) { GError *error_tmp = fu_engine_error_array_get_best(helper->errors); g_propagate_error(error, error_tmp); return FALSE; } /* authenticate all things in the action_ids */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); fu_dbus_daemon_authorize_install_queue(g_steal_pointer(&helper)); return TRUE; } #endif /* HAVE_GIO_UNIX */ typedef struct { gchar *id; gchar *sender; guint watcher_id; } FuDbusDaemonSystemInhibit; static void fu_dbus_daemon_system_inhibit_free(FuDbusDaemonSystemInhibit *inhibit) { g_bus_unwatch_name(inhibit->watcher_id); g_free(inhibit->id); g_free(inhibit->sender); g_free(inhibit); } static void fu_dbus_daemon_ensure_system_inhibit(FuDbusDaemon *self) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); FuContext *ctx = fu_engine_get_context(engine); if (self->system_inhibits->len > 0) { fu_context_add_flag(ctx, FU_CONTEXT_FLAG_SYSTEM_INHIBIT); return; } fu_context_remove_flag(ctx, FU_CONTEXT_FLAG_SYSTEM_INHIBIT); } static void fu_dbus_daemon_inhibit_name_vanished_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); g_autoptr(GPtrArray) inhibits = g_ptr_array_new(); for (guint i = 0; i < self->system_inhibits->len; i++) { FuDbusDaemonSystemInhibit *inhibit = g_ptr_array_index(self->system_inhibits, i); if (g_strcmp0(inhibit->sender, name) == 0) g_ptr_array_add(inhibits, inhibit); } if (inhibits->len == 0) return; for (guint i = 0; i < inhibits->len; i++) { FuDbusDaemonSystemInhibit *inhibit = g_ptr_array_index(inhibits, i); g_debug("removing %s as %s vanished without calling Uninhibit", inhibit->id, name); g_ptr_array_remove(self->system_inhibits, inhibit); } fu_dbus_daemon_ensure_system_inhibit(self); } #ifdef HAVE_GIO_UNIX static void fu_dbus_daemon_client_flags_notify_cb(FuClient *client, GParamSpec *pspec, FuMainAuthHelper *helper) { if (!fu_client_has_flag(client, FU_CLIENT_FLAG_ACTIVE)) { g_info("%s vanished before completion of install on %s", fu_client_get_sender(client), helper->device_id); fu_progress_add_flag(helper->progress, FU_PROGRESS_FLAG_NO_SENDER); } } #endif static FuInputStream * fu_dbus_daemon_invocation_get_input_stream(GDBusMethodInvocation *invocation, GError **error) { #ifdef HAVE_GIO_UNIX GDBusMessage *message; GUnixFDList *fd_list; g_autofd gint fd = -1; g_autoptr(FuInputStream) stream = NULL; g_autoptr(FuInputStream) stream_safe = NULL; g_autoptr(GError) error_local = NULL; /* get the fd */ message = g_dbus_method_invocation_get_message(invocation); fd_list = g_dbus_message_get_unix_fd_list(message); if (fd_list == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no file descriptors are associated"); return NULL; } if (g_unix_fd_list_get_length(fd_list) != 1) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "wrong number of file descriptors: %i", g_unix_fd_list_get_length(fd_list)); return NULL; } fd = g_unix_fd_list_get(fd_list, 0, error); if (fd < 0) return NULL; /* get details about the file (will close the fd when done) */ stream = fu_unix_seekable_input_stream_new(g_steal_fd(&fd), TRUE, error); if (stream == NULL) return NULL; if (!fu_unix_seekable_input_stream_require_seal(FU_UNIX_SEEKABLE_INPUT_STREAM(stream), &error_local)) { g_autoptr(GBytes) blob = NULL; g_debug("fd not sealed, falling back to copy: %s", error_local->message); blob = fu_input_stream_read_bytes(stream, 0, G_MAXSIZE, NULL, error); if (blob == NULL) return NULL; stream_safe = fu_memory_input_stream_new_from_bytes(blob); } else { stream_safe = g_object_ref(stream); } /* success */ return g_steal_pointer(&stream_safe); #else g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); return NULL; #endif } static GOutputStream * fu_dbus_daemon_invocation_get_output_stream(GDBusMethodInvocation *invocation, GError **error) { #ifdef HAVE_GIO_UNIX GDBusMessage *message; GUnixFDList *fd_list; g_autofd gint fd = -1; g_autoptr(GOutputStream) stream = NULL; /* get the fd */ message = g_dbus_method_invocation_get_message(invocation); fd_list = g_dbus_message_get_unix_fd_list(message); if (fd_list == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no file descriptors are associated"); return NULL; } if (g_unix_fd_list_get_length(fd_list) != 1) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "wrong number of file descriptors: %i", g_unix_fd_list_get_length(fd_list)); return NULL; } fd = g_unix_fd_list_get(fd_list, 0, error); if (fd < 0) return NULL; /* get details about the file (will close the fd when done) */ stream = g_unix_output_stream_new(g_steal_fd(&fd), TRUE); if (stream == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid stream"); return NULL; } return g_steal_pointer(&stream); #else g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); return NULL; #endif } static gboolean fu_dbus_daemon_hsi_supported(FuDbusDaemon *self, GError **error) { #ifdef HAVE_HSI FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); FuContext *ctx = fu_engine_get_context(engine); if (g_getenv("UMOCKDEV_DIR") != NULL) return TRUE; if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_IS_CONTAINER)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "HSI unavailable for container"); return FALSE; } if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_IS_HYPERVISOR) && !fu_context_has_flag(ctx, FU_CONTEXT_FLAG_IS_HYPERVISOR_PRIVILEGED)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "HSI unavailable for unprivileged hypervisor"); return FALSE; } /* success */ return TRUE; #else g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "HSI support not enabled"); return FALSE; #endif } static void fu_dbus_daemon_method_get_devices(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); GVariant *val; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; devices = fu_engine_get_devices(engine, &error); if (devices == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } val = fu_dbus_daemon_device_array_to_variant(self, request, devices, &error); if (val == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, val); } static void fu_dbus_daemon_method_get_plugins(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); GVariant *val; val = fwupd_codec_array_to_variant(fu_engine_get_plugins(engine), FWUPD_CODEC_FLAG_NONE); g_dbus_method_invocation_return_value(invocation, val); } static void fu_dbus_daemon_method_get_releases(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); const gchar *device_id; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) releases = NULL; g_variant_get(parameters, "(&s)", &device_id); releases = fu_engine_get_releases(engine, request, device_id, &error); if (releases == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value( invocation, fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE)); } static void fu_dbus_daemon_method_get_approved_firmware(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); GPtrArray *checksums = fu_engine_get_approved_firmware(engine); GVariantBuilder builder; GVariant *val; 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)); } static void fu_dbus_daemon_method_get_report_metadata(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); const gchar *key; const gchar *value; GHashTableIter iter; GVariantBuilder builder; GVariant *val; g_autoptr(GError) error = NULL; g_autoptr(GHashTable) metadata = NULL; metadata = fu_engine_get_report_metadata(engine, &error); if (metadata == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); g_hash_table_iter_init(&iter, metadata); while (g_hash_table_iter_next(&iter, (gpointer *)&key, (gpointer *)&value)) { g_variant_builder_add_value(&builder, g_variant_new("{ss}", key, value)); } val = g_variant_builder_end(&builder); g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&val, 1)); } static void fu_dbus_daemon_method_set_approved_firmware(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { g_auto(GStrv) checksums = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(^as)", &checksums); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->flags = FU_FIRMWARE_PARSE_FLAG_NO_SEARCH; helper->request = g_object_ref(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])); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.set-approved-firmware", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_set_approved_firmware_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_authorize_quit_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ fu_daemon_schedule_process_quit(FU_DAEMON(helper->self)); g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_method_quit(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { g_autoptr(FuMainAuthHelper) helper = NULL; /* is root */ if (fu_engine_request_has_converter_flag(request, FWUPD_CODEC_FLAG_TRUSTED)) { fu_daemon_schedule_process_quit(FU_DAEMON(self)); g_dbus_method_invocation_return_value(invocation, NULL); return; } /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.quit", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_quit_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_self_sign(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { GVariant *prop_value; const gchar *prop_key; g_autofree gchar *value = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_autoptr(GVariantIter) iter = NULL; g_variant_get(parameters, "(sa{sv})", &value, &iter); /* get flags */ helper = g_new0(FuMainAuthHelper, 1); while (g_variant_iter_next(iter, "{&sv}", &prop_key, &prop_value)) { g_debug("got option %s", prop_key); if (g_strcmp0(prop_key, "add-timestamp") == 0 && fwupd_variant_get_boolean(prop_value) == TRUE) helper->flags |= FU_JCAT_SIGN_FLAG_ADD_TIMESTAMP; if (g_strcmp0(prop_key, "add-cert") == 0 && fwupd_variant_get_boolean(prop_value) == TRUE) helper->flags |= FU_JCAT_SIGN_FLAG_ADD_CERT; g_variant_unref(prop_value); } /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper->self = self; helper->value = g_steal_pointer(&value); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.self-sign", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_self_sign_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_get_downgrades(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); const gchar *device_id; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) releases = NULL; g_variant_get(parameters, "(&s)", &device_id); releases = fu_engine_get_downgrades(engine, request, device_id, &error); if (releases == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value( invocation, fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE)); } static void fu_dbus_daemon_method_get_upgrades(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); const gchar *device_id; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) releases = NULL; g_variant_get(parameters, "(&s)", &device_id); releases = fu_engine_get_upgrades(engine, request, device_id, &error); if (releases == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value( invocation, fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE)); } static void fu_dbus_daemon_return_get_remotes(FuDbusDaemon *self, GDBusMethodInvocation *invocation, FwupdCodecFlags flag) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); g_autoptr(GPtrArray) remotes = NULL; g_autoptr(GError) error = NULL; remotes = fu_engine_get_remotes(engine, &error); if (remotes == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, fwupd_codec_array_to_variant(remotes, flag)); } static void fu_dbus_daemon_authorize_get_remotes_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; /* get result -- still return the remotes, but only the untrusted bits */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_AUTH_FAILED)) { g_debug("ignoring: %s", error->message); fu_dbus_daemon_return_get_remotes(helper->self, helper->invocation, FWUPD_CODEC_FLAG_NONE); return; } fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ fu_dbus_daemon_return_get_remotes(helper->self, helper->invocation, FWUPD_CODEC_FLAG_TRUSTED); } static void fu_dbus_daemon_method_get_remotes(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { g_autoptr(FuMainAuthHelper) helper = NULL; /* is root */ if (fu_engine_request_has_converter_flag(request, FWUPD_CODEC_FLAG_TRUSTED)) { fu_dbus_daemon_return_get_remotes(self, invocation, FWUPD_CODEC_FLAG_TRUSTED); return; } /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->self = self; /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.get-remotes", FU_POLKIT_AUTHORITY_CHECK_FLAG_NONE, /* non-interactive */ NULL, fu_dbus_daemon_authorize_get_remotes_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_get_history(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); GVariant *val; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; devices = fu_engine_get_history(engine, &error); if (devices == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } val = fu_dbus_daemon_device_array_to_variant(self, request, devices, &error); if (val == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, val); } static void fu_dbus_daemon_method_get_host_security_attrs(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); g_autoptr(GError) error = NULL; g_autoptr(FuSecurityAttrs) attrs = NULL; if (!fu_dbus_daemon_hsi_supported(self, &error)) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } attrs = fu_engine_get_host_security_attrs(engine); g_dbus_method_invocation_return_value(invocation, fu_security_attrs_to_variant(attrs)); } static void fu_dbus_daemon_method_get_host_security_events(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { #ifdef HAVE_HSI FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); guint limit = 0; g_autoptr(FuSecurityAttrs) attrs = NULL; g_autoptr(GError) error = NULL; g_variant_get(parameters, "(u)", &limit); attrs = fu_engine_get_host_security_events(engine, limit, &error); if (attrs == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, fu_security_attrs_to_variant(attrs)); #else g_dbus_method_invocation_return_error_literal(invocation, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "HSI support not enabled"); #endif } static void fu_dbus_daemon_authorize_clear_results_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* authenticated */ if (!fu_engine_clear_results(engine, helper->device_id, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_method_clear_results(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *device_id; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(&s)", &device_id); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->device_id = g_strdup(device_id); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.clear-results", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_clear_results_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_search(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); const gchar *token; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) releases = NULL; g_variant_get(parameters, "(&s)", &token); releases = fu_engine_search(engine, token, &error); if (releases == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value( invocation, fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE)); } static void fu_dbus_daemon_authorize_emulation_load_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; g_autoptr(FuInputStream) stream = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* get stream */ stream = fu_dbus_daemon_invocation_get_input_stream(helper->invocation, &error); if (stream == NULL) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* load data into engine */ if (!fu_engine_emulation_load(engine, stream, &error)) { g_dbus_method_invocation_return_error(helper->invocation, error->domain, error->code, "failed to load emulation data: %s", error->message); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_method_emulation_load(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { gint32 fd_handle = 0; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(h)", &fd_handle); fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->handle = fd_handle; fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.emulation-load", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_emulation_load_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_authorize_emulation_save_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; g_autoptr(GOutputStream) stream = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* get stream */ stream = fu_dbus_daemon_invocation_get_output_stream(helper->invocation, &error); if (stream == NULL) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* save data from engine */ if (!fu_engine_emulation_save(engine, stream, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_method_emulation_save(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { gint32 fd_handle = 0; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(h)", &fd_handle); fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->handle = fd_handle; fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.emulation-save", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_emulation_save_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_authorize_modify_device_internal(FuEngine *engine, const gchar *device_id, const gchar *key, const gchar *value, GDBusMethodInvocation *invocation) { g_autoptr(GError) error = NULL; if (!fu_engine_modify_device(engine, device_id, key, value, &error)) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value(invocation, NULL); } static void fu_dbus_daemon_authorize_modify_device_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } fu_dbus_daemon_authorize_modify_device_internal( fu_daemon_get_engine(FU_DAEMON(helper->self)), helper->device_id, helper->key, helper->value, helper->invocation); } static const gchar * fu_dbus_daemon_method_modify_device_flag_to_action_id(const gchar *key, const gchar *value, GError **error) { if (g_strcmp0(key, "Flags") == 0) { if (g_strcmp0(value, "emulation-tag") == 0 || g_strcmp0(value, "~emulation-tag") == 0) return "org.freedesktop.fwupd.emulation-tag"; return "org.freedesktop.fwupd.modify-device"; } g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "key value pair not supported"); return NULL; } static void fu_dbus_daemon_method_modify_device(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *device_id; const gchar *key; const gchar *value; const gchar *action_id; g_autoptr(FuMainAuthHelper) helper = NULL; g_autoptr(GError) error = NULL; g_variant_get(parameters, "(&s&s&s)", &device_id, &key, &value); /* use a specific action-id for flag key=value */ action_id = fu_dbus_daemon_method_modify_device_flag_to_action_id(key, value, &error); if (action_id == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } /* authenticate */ helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->device_id = g_strdup(device_id); helper->key = g_strdup(key); helper->value = g_strdup(value); fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), action_id, fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_modify_device_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_get_results(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); GVariant *val; const gchar *device_id = NULL; g_autoptr(FwupdDevice) device = NULL; g_autoptr(GError) error = NULL; g_variant_get(parameters, "(&s)", &device_id); device = fu_engine_get_results(engine, device_id, &error); if (device == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } val = fwupd_codec_to_variant(FWUPD_CODEC(device), fu_engine_request_get_converter_flags(request)); g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&val, 1)); } #ifdef HAVE_GIO_UNIX static void fu_dbus_daemon_authorize_update_metadata_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); GDBusMessage *message; GUnixFDList *fd_list; g_autofd gint fd_data = -1; g_autofd gint fd_sig = -1; /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* get the fds from the message */ message = g_dbus_method_invocation_get_message(helper->invocation); fd_list = g_dbus_message_get_unix_fd_list(message); if (fd_list == NULL) { g_set_error_literal(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no file descriptors are associated"); fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } if (g_unix_fd_list_get_length(fd_list) != 2) { g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "wrong number of file descriptors: %i", g_unix_fd_list_get_length(fd_list)); fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } fd_data = g_unix_fd_list_get(fd_list, 0, &error); if (fd_data < 0) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } fd_sig = g_unix_fd_list_get(fd_list, 1, &error); if (fd_sig < 0) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* store new metadata (will close the fds when done) */ if (!fu_engine_update_metadata(engine, helper->remote_id, g_steal_fd(&fd_data), g_steal_fd(&fd_sig), &error)) { g_prefix_error(&error, "Failed to update metadata for %s: ", helper->remote_id); fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } g_dbus_method_invocation_return_value(helper->invocation, NULL); } #endif /* HAVE_GIO_UNIX */ static void fu_dbus_daemon_method_update_metadata(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { #ifdef HAVE_GIO_UNIX const gchar *remote_id = NULL; gint fd_data; gint fd_sig; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(&shh)", &remote_id, &fd_data, &fd_sig); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->remote_id = g_strdup(remote_id); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.refresh-remote", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_update_metadata_cb, g_steal_pointer(&helper)); #else g_dbus_method_invocation_return_error_literal(invocation, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); #endif /* HAVE_GIO_UNIX */ } static void fu_dbus_daemon_method_unlock(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(&s)", &device_id); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->device_id = g_strdup(device_id); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.device-unlock", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_unlock_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_activate(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(&s)", &device_id); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->device_id = g_strdup(device_id); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.device-activate", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_activate_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_modify_config(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { g_autofree gchar *key = NULL; g_autofree gchar *section = NULL; g_autofree gchar *value = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(sss)", §ion, &key, &value); /* authenticate */ helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->section = g_steal_pointer(§ion); helper->key = g_steal_pointer(&key); helper->value = g_steal_pointer(&value); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.modify-config", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_modify_config_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_reset_config(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { g_autofree gchar *section = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(s)", §ion); /* authenticate */ helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->section = g_steal_pointer(§ion); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.reset-config", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_reset_config_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_modify_remote(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *action_id; const gchar *remote_id = NULL; const gchar *key = NULL; const gchar *value = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; const gchar *keys_enable[] = { "ApprovalRequired", "AutomaticReports", "AutomaticSecurityReports", "Enabled", NULL, }; const gchar *keys_modify[] = { "FirmwareBaseURI", "MetadataURI", "ReportURI", "Username", "Password", NULL, }; /* check the id exists */ g_variant_get(parameters, "(&s&s&s)", &remote_id, &key, &value); /* find the correct polkit authorization */ if (g_strv_contains(keys_enable, key)) { action_id = "org.freedesktop.fwupd.enable-remote"; } else if (g_strv_contains(keys_modify, key)) { action_id = "org.freedesktop.fwupd.modify-remote"; } else { g_dbus_method_invocation_return_error(invocation, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "setting %s=%s not supported", key, value); return; } /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->remote_id = g_strdup(remote_id); helper->key = g_strdup(key); helper->value = g_strdup(value); helper->self = self; /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), action_id, fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_modify_remote_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_clean_remote(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *remote_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; /* check the id exists */ g_variant_get(parameters, "(&s)", &remote_id); /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->remote_id = g_strdup(remote_id); helper->self = self; /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.clean-remote", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_clean_remote_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_verify_update(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; /* check the id exists */ g_variant_get(parameters, "(&s)", &device_id); /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->device_id = g_strdup(device_id); helper->self = self; /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.verify-update", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_verify_update_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_authorize_verify_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; g_autoptr(GError) error = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); /* get result */ if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* progress */ fu_progress_set_profile(progress, g_log_get_debug_enabled()); g_signal_connect(FU_PROGRESS(progress), "percentage-changed", G_CALLBACK(fu_dbus_daemon_progress_percentage_changed_cb), helper->self); g_signal_connect(FU_PROGRESS(progress), "status-changed", G_CALLBACK(fu_dbus_daemon_progress_status_changed_cb), helper->self); /* authenticated */ if (!fu_engine_verify(engine, helper->device_id, progress, &error)) { fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); return; } /* success */ g_dbus_method_invocation_return_value(helper->invocation, NULL); } static void fu_dbus_daemon_method_verify(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(&s)", &device_id); /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->device_id = g_strdup(device_id); helper->self = self; /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.verify", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_verify_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_set_feature_flags(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { guint64 feature_flags_u64 = 0; g_autoptr(FuClient) client = NULL; g_variant_get(parameters, "(t)", &feature_flags_u64); /* old flags for the same sender will be automatically destroyed */ client = fu_client_list_register(self->client_list, fu_engine_request_get_sender(request)); fu_client_set_feature_flags(client, feature_flags_u64); g_dbus_method_invocation_return_value(invocation, NULL); } static void fu_dbus_daemon_method_set_hints(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *prop_key; const gchar *prop_value; guint cnt = 0; g_autoptr(FuClient) client = NULL; g_autoptr(GVariantIter) iter = NULL; g_variant_get(parameters, "(a{ss})", &iter); client = fu_client_list_register(self->client_list, fu_engine_request_get_sender(request)); while (g_variant_iter_next(iter, "{&s&s}", &prop_key, &prop_value)) { if (++cnt > FU_DBUS_DAEMON_SET_HINTS_MAX) { g_dbus_method_invocation_return_error_literal(invocation, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "too many hints specified"); return; } g_debug("got hint %s=%s", prop_key, prop_value); fu_client_insert_hint(client, prop_key, prop_value); } g_dbus_method_invocation_return_value(invocation, NULL); } static void fu_dbus_daemon_method_inhibit(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuDbusDaemonSystemInhibit *inhibit; const gchar *reason = NULL; const gchar *sender = fu_engine_request_get_sender(request); guint sender_count = 0; g_autofree gchar *id = NULL; g_autoptr(GError) error_local = NULL; g_variant_get(parameters, "(&s)", &reason); /* limit total system inhibits */ if (self->system_inhibits->len >= FU_DBUS_DAEMON_SYSTEM_INHIBIT_MAX_TOTAL) { g_dbus_method_invocation_return_error_literal( invocation, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "maximum system inhibit limit reached"); return; } /* limit per-user inhibits */ for (guint i = 0; i < self->system_inhibits->len; i++) { FuDbusDaemonSystemInhibit *existing = g_ptr_array_index(self->system_inhibits, i); if (g_strcmp0(existing->sender, sender) == 0) sender_count++; } if (sender_count >= FU_DBUS_DAEMON_SYSTEM_INHIBIT_MAX_PER_SENDER) { g_dbus_method_invocation_return_error_literal( invocation, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "maximum inhibit limit per user reached"); return; } /* use a cryptographically secure RNG */ id = fu_common_get_random_string(6, &error_local); if (id == NULL) { g_dbus_method_invocation_return_gerror(invocation, error_local); return; } /* watch */ inhibit = g_new0(FuDbusDaemonSystemInhibit, 1); inhibit->sender = g_strdup(sender); inhibit->id = g_strdup_printf("dbus-%s", id); inhibit->watcher_id = g_bus_watch_name_on_connection(self->connection, sender, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, fu_dbus_daemon_inhibit_name_vanished_cb, self, NULL); g_ptr_array_add(self->system_inhibits, inhibit); fu_dbus_daemon_ensure_system_inhibit(self); g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", inhibit->id)); } static void fu_dbus_daemon_method_install(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { #ifdef HAVE_GIO_UNIX FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); FuContext *ctx = fu_engine_get_context(engine); GVariant *prop_value; const gchar *device_id = NULL; const gchar *prop_key; gint32 fd_handle = 0; g_autoptr(FuMainAuthHelper) helper = NULL; g_autoptr(GError) error = NULL; g_autoptr(GVariantIter) iter = NULL; /* check the id exists */ g_variant_get(parameters, "(&sha{sv})", &device_id, &fd_handle, &iter); /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); helper->request = g_object_ref(request); helper->progress = fu_progress_new(G_STRLOC); helper->invocation = g_object_ref(invocation); helper->device_id = g_strdup(device_id); helper->self = self; /* get flags */ while (g_variant_iter_next(iter, "{&sv}", &prop_key, &prop_value)) { g_debug("got option %s", prop_key); if (g_strcmp0(prop_key, "install-flags") == 0) { const guint64 allowed_mask = FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | FWUPD_INSTALL_FLAG_ALLOW_OLDER | FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_NO_HISTORY | FWUPD_INSTALL_FLAG_ONLY_EMULATED; helper->flags = fwupd_variant_get_uint64(prop_value) & allowed_mask; } /* these are all set by libfwupd < 2.0.x; parse for compatibility */ if (g_strcmp0(prop_key, "allow-older") == 0 && fwupd_variant_get_boolean(prop_value)) helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; if (g_strcmp0(prop_key, "allow-reinstall") == 0 && fwupd_variant_get_boolean(prop_value)) helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; if (g_strcmp0(prop_key, "allow-branch-switch") == 0 && fwupd_variant_get_boolean(prop_value)) helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; g_variant_unref(prop_value); } /* get stream */ helper->stream = fu_dbus_daemon_invocation_get_input_stream(invocation, &error); if (helper->stream == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } /* relax these */ if (fu_context_get_config_bool(ctx, "IgnoreRequirements")) helper->flags |= FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS; /* install all the things in the store */ helper->client = fu_client_list_register(self->client_list, fu_engine_request_get_sender(request)); helper->client_sender_changed_id = g_signal_connect(FU_CLIENT(helper->client), "notify::flags", G_CALLBACK(fu_dbus_daemon_client_flags_notify_cb), helper); if (!fu_dbus_daemon_install_with_helper(g_steal_pointer(&helper), &error)) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } #else g_dbus_method_invocation_return_error_literal(invocation, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); #endif /* HAVE_GIO_UNIX */ } static void fu_dbus_daemon_method_uninhibit(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *inhibit_id = NULL; const gchar *sender = fu_engine_request_get_sender(request); gboolean found = FALSE; g_variant_get(parameters, "(&s)", &inhibit_id); /* find by id, then verify ownership before uninhibiting */ for (guint i = 0; i < self->system_inhibits->len; i++) { FuDbusDaemonSystemInhibit *inhibit = g_ptr_array_index(self->system_inhibits, i); if (g_strcmp0(inhibit->id, inhibit_id) == 0) { /* verify caller is the owner of this inhibit */ if (g_strcmp0(inhibit->sender, sender) != 0) { g_dbus_method_invocation_return_error_literal( invocation, FWUPD_ERROR, FWUPD_ERROR_PERMISSION_DENIED, "cannot uninhibit: not the owner"); return; } g_ptr_array_remove_index(self->system_inhibits, i); fu_dbus_daemon_ensure_system_inhibit(self); found = TRUE; break; } } if (!found) { g_dbus_method_invocation_return_error_literal(invocation, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "Cannot find inhibit ID"); return; } g_dbus_method_invocation_return_value(invocation, NULL); } static void fu_dbus_daemon_method_get_details(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { #ifdef HAVE_GIO_UNIX FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); gint32 fd_handle = 0; g_autoptr(GError) error = NULL; g_autoptr(FuInputStream) stream = NULL; g_autoptr(GPtrArray) results = NULL; /* get parameters */ g_variant_get(parameters, "(h)", &fd_handle); /* get stream */ stream = fu_dbus_daemon_invocation_get_input_stream(invocation, &error); if (stream == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } /* get details about the file */ results = fu_engine_get_details(engine, request, stream, &error); if (results == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } g_dbus_method_invocation_return_value( invocation, fwupd_codec_array_to_variant(results, fu_engine_request_get_converter_flags(request))); #else g_dbus_method_invocation_return_error_literal(invocation, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); #endif /* HAVE_GIO_UNIX */ } static void fu_dbus_daemon_method_get_bios_settings(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); gboolean authenticate = fu_engine_request_get_feature_flags(request) & FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION; if (!authenticate) { /* if we cannot authenticate and the peer is not * inherently trusted, only return a non-sensitive * subset of the settings */ g_autoptr(FuBiosSettings) attrs = fu_context_get_bios_settings(fu_engine_get_context(engine)); g_dbus_method_invocation_return_value( invocation, fwupd_codec_to_variant(FWUPD_CODEC(attrs), fu_engine_request_get_converter_flags(request))); } else { g_autoptr(FuMainAuthHelper) helper = NULL; /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); fu_polkit_authority_check( self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.get-bios-settings", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_get_bios_settings_cb, g_steal_pointer(&helper)); } } static void fu_dbus_daemon_method_set_bios_settings(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *key; const gchar *value; g_autoptr(FuMainAuthHelper) helper = NULL; g_autoptr(GVariantIter) iter = NULL; g_variant_get(parameters, "(a{ss})", &iter); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->bios_settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); while (g_variant_iter_next(iter, "{&s&s}", &key, &value)) { g_debug("got setting %s=%s", key, value); g_hash_table_insert(helper->bios_settings, g_strdup(key), g_strdup(value)); } fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.set-bios-settings", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_set_bios_settings_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_fix_host_security_attr(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *appstream_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(&s)", &appstream_id); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->key = g_strdup(appstream_id); helper->is_fix = TRUE; fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.fix-host-security-attr", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_fix_host_security_attr_cb, g_steal_pointer(&helper)); } static void fu_dbus_daemon_method_undo_host_security_attr(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation) { const gchar *appstream_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; g_variant_get(parameters, "(&s)", &appstream_id); /* authenticate */ fu_daemon_set_status(FU_DAEMON(self), FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->key = g_strdup(appstream_id); helper->is_fix = FALSE; fu_polkit_authority_check(self->authority, fu_engine_request_get_sender(request), "org.freedesktop.fwupd.undo-host-security-attr", fu_dbus_daemon_engine_request_get_authority_check_flags(request), NULL, fu_dbus_daemon_authorize_undo_host_security_attr_cb, g_steal_pointer(&helper)); } typedef void (*FuDbusDaemonMethodFunc)(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, GDBusMethodInvocation *invocation); static void fu_dbus_daemon_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); g_autoptr(FuEngineRequest) request = NULL; g_autoptr(GError) error = NULL; g_autoptr(GString) parameters_str = NULL; struct { const gchar *name; FuDbusDaemonMethodFunc func; } method_funcs[] = { {"GetDevices", fu_dbus_daemon_method_get_devices}, {"GetPlugins", fu_dbus_daemon_method_get_plugins}, {"GetReleases", fu_dbus_daemon_method_get_releases}, {"GetApprovedFirmware", fu_dbus_daemon_method_get_approved_firmware}, {"GetReportMetadata", fu_dbus_daemon_method_get_report_metadata}, {"SetApprovedFirmware", fu_dbus_daemon_method_set_approved_firmware}, {"Quit", fu_dbus_daemon_method_quit}, {"SelfSign", fu_dbus_daemon_method_self_sign}, {"GetDowngrades", fu_dbus_daemon_method_get_downgrades}, {"GetUpgrades", fu_dbus_daemon_method_get_upgrades}, {"GetRemotes", fu_dbus_daemon_method_get_remotes}, {"GetHistory", fu_dbus_daemon_method_get_history}, {"GetHostSecurityAttrs", fu_dbus_daemon_method_get_host_security_attrs}, {"GetHostSecurityEvents", fu_dbus_daemon_method_get_host_security_events}, {"ClearResults", fu_dbus_daemon_method_clear_results}, {"EmulationLoad", fu_dbus_daemon_method_emulation_load}, {"EmulationSave", fu_dbus_daemon_method_emulation_save}, {"Search", fu_dbus_daemon_method_search}, {"ModifyDevice", fu_dbus_daemon_method_modify_device}, {"GetResults", fu_dbus_daemon_method_get_results}, {"UpdateMetadata", fu_dbus_daemon_method_update_metadata}, {"Unlock", fu_dbus_daemon_method_unlock}, {"Activate", fu_dbus_daemon_method_activate}, {"ModifyConfig", fu_dbus_daemon_method_modify_config}, {"ResetConfig", fu_dbus_daemon_method_reset_config}, {"ModifyRemote", fu_dbus_daemon_method_modify_remote}, {"CleanRemote", fu_dbus_daemon_method_clean_remote}, {"VerifyUpdate", fu_dbus_daemon_method_verify_update}, {"Verify", fu_dbus_daemon_method_verify}, {"SetFeatureFlags", fu_dbus_daemon_method_set_feature_flags}, {"SetHints", fu_dbus_daemon_method_set_hints}, {"Inhibit", fu_dbus_daemon_method_inhibit}, {"Uninhibit", fu_dbus_daemon_method_uninhibit}, {"Install", fu_dbus_daemon_method_install}, {"GetDetails", fu_dbus_daemon_method_get_details}, {"GetBiosSettings", fu_dbus_daemon_method_get_bios_settings}, {"SetBiosSettings", fu_dbus_daemon_method_set_bios_settings}, {"FixHostSecurityAttr", fu_dbus_daemon_method_fix_host_security_attr}, {"UndoHostSecurityAttr", fu_dbus_daemon_method_undo_host_security_attr}, }; /* build request */ request = fu_dbus_daemon_create_request(self, sender, &error); if (request == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } /* activity */ fu_engine_idle_reset(engine); /* be helpful */ parameters_str = g_variant_print_string(parameters, NULL, TRUE); g_debug("called %s%s", method_name, parameters_str->str); /* call the correct vfunc */ for (guint i = 0; i < G_N_ELEMENTS(method_funcs); i++) { if (g_strcmp0(method_name, method_funcs[i].name) == 0) { method_funcs[i].func(self, parameters, request, invocation); return; } } g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "no such method %s", method_name); } static GVariant * fu_dbus_daemon_get_property_hwids(FuDbusDaemon *self) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); FuContext *ctx = fu_engine_get_context(engine); FuHwids *hwids = fu_context_get_hwids(ctx); GVariantBuilder builder; g_autoptr(GPtrArray) chid_keys = fu_hwids_get_chid_keys(hwids); g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys(hwids); g_variant_builder_init(&builder, G_VARIANT_TYPE("a(ss)")); for (guint i = 0; i < hwid_keys->len; i++) { const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); const gchar *value = fu_hwids_get_value(hwids, hwid_key); if (value == NULL) continue; g_variant_builder_add(&builder, "(ss)", hwid_key, value); } for (guint i = 0; i < chid_keys->len; i++) { const gchar *key = g_ptr_array_index(chid_keys, i); const gchar *keys = NULL; g_autofree gchar *guid = NULL; /* get the GUID */ keys = fu_hwids_get_replace_keys(hwids, key); if (keys == NULL) continue; guid = fu_hwids_get_guid(hwids, key, NULL); if (guid == NULL) continue; g_variant_builder_add(&builder, "(ss)", keys, guid); } /* done */ return g_variant_builder_end(&builder); } static GVariant * fu_dbus_daemon_get_property(GDBusConnection *connection_, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); FuContext *ctx = fu_engine_get_context(engine); /* activity */ fu_engine_idle_reset(engine); if (g_strcmp0(property_name, "DaemonVersion") == 0) return g_variant_new_string(PACKAGE_VERSION); if (g_strcmp0(property_name, "HostBkc") == 0) { g_autofree gchar *host_bkc = fu_context_get_config_str(ctx, "HostBkc"); if (host_bkc == NULL) return g_variant_new_string(""); return g_variant_new_string(host_bkc); } if (g_strcmp0(property_name, "Tainted") == 0) return g_variant_new_boolean(FALSE); if (g_strcmp0(property_name, "Status") == 0) return g_variant_new_uint32(fu_daemon_get_status(FU_DAEMON(self))); if (g_strcmp0(property_name, "Percentage") == 0) { gdouble percentage = fu_daemon_get_percentage(FU_DAEMON(self)); return g_variant_new_uint32( fwupd_percentage_is_valid(percentage) ? (guint32)percentage : 0); } if (g_strcmp0(property_name, "PercentageFull") == 0) return g_variant_new_double(fu_daemon_get_percentage(FU_DAEMON(self))); if (g_strcmp0(property_name, FWUPD_RESULT_KEY_BATTERY_LEVEL) == 0) return g_variant_new_uint32(fu_context_get_battery_level(ctx)); if (g_strcmp0(property_name, FWUPD_RESULT_KEY_BATTERY_THRESHOLD) == 0) return g_variant_new_uint32(fu_context_get_battery_threshold(ctx)); if (g_strcmp0(property_name, "HostVendor") == 0) return g_variant_new_string(fu_engine_get_host_vendor(engine)); if (g_strcmp0(property_name, "HostProduct") == 0) return g_variant_new_string(fu_engine_get_host_product(engine)); if (g_strcmp0(property_name, "HostMachineId") == 0) { const gchar *tmp = fu_engine_get_host_machine_id(engine); if (tmp == NULL) { g_set_error(error, /* nocheck:error */ G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "failed to get daemon property %s", property_name); return NULL; } return g_variant_new_string(tmp); } if (g_strcmp0(property_name, "HostSecurityId") == 0) { #ifdef HAVE_HSI g_autofree gchar *tmp = fu_engine_get_host_security_id(engine, NULL); if (tmp == NULL) { g_set_error(error, /* nocheck:error */ G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "failed to get daemon property %s", property_name); return NULL; } return g_variant_new_string(tmp); #else g_set_error(error, /* nocheck:error */ G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "failed to get daemon property %s", property_name); return NULL; #endif } if (g_strcmp0(property_name, "Interactive") == 0) return g_variant_new_boolean(isatty(fileno(stdout)) != 0); if (g_strcmp0(property_name, "OnlyTrusted") == 0) { return g_variant_new_boolean(fu_context_get_config_bool(ctx, "OnlyTrusted")); } if (g_strcmp0(property_name, "Hwids") == 0) return fu_dbus_daemon_get_property_hwids(self); /* return an error */ g_set_error(error, /* nocheck:error */ G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "failed to get daemon property %s", property_name); return NULL; } static gboolean fu_dbus_daemon_register_object(FuDbusDaemon *self, GError **error) { guint registration_id; static const GDBusInterfaceVTable interface_vtable = {fu_dbus_daemon_method_call, fu_dbus_daemon_get_property, NULL}; registration_id = g_dbus_connection_register_object(self->connection, FWUPD_DBUS_PATH, self->introspection_daemon->interfaces[0], &interface_vtable, self, /* user_data */ NULL, /* user_data_free_func */ NULL); /* GError** */ if (registration_id == 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified failure"); return FALSE; } /* success */ return TRUE; } static void fu_dbus_daemon_client_list_ensure_inhibit(FuDbusDaemon *self) { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); g_autoptr(GPtrArray) clients = fu_client_list_get_all(self->client_list); g_debug("connected clients: %u", clients->len); if (clients->len > 0 && self->clients_inhibit_id == 0) { self->clients_inhibit_id = fu_engine_idle_inhibit(engine, FU_IDLE_INHIBIT_TIMEOUT, "connected-clients"); } else if (clients->len == 0 && self->clients_inhibit_id != 0) { fu_engine_idle_uninhibit(engine, self->clients_inhibit_id); self->clients_inhibit_id = 0; } } static void fu_dbus_daemon_client_list_added_cb(FuClientList *client_list, FuClient *client, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); fu_dbus_daemon_client_list_ensure_inhibit(self); } static void fu_dbus_daemon_client_list_removed_cb(FuClientList *client_list, FuClient *client, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); fu_dbus_daemon_client_list_ensure_inhibit(self); } static void fu_dbus_daemon_set_connection(FuDbusDaemon *self, GDBusConnection *connection) { g_set_object(&self->connection, connection); if (connection != NULL) { g_autoptr(FuClientList) client_list = fu_client_list_new(connection); g_signal_connect(client_list, "added", G_CALLBACK(fu_dbus_daemon_client_list_added_cb), self); g_signal_connect(client_list, "removed", G_CALLBACK(fu_dbus_daemon_client_list_removed_cb), self); g_set_object(&self->client_list, client_list); } } static void fu_dbus_daemon_dbus_bus_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); g_autoptr(GError) error = NULL; /* connect to D-Bus directly */ self->proxy_uid = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", NULL, &error); if (self->proxy_uid == NULL) { g_warning("cannot connect to DBus: %s", error->message); return; } fu_dbus_daemon_set_connection(self, connection); if (!fu_dbus_daemon_register_object(self, &error)) { g_warning("cannot register object: %s", error->message); return; } } static void fu_dbus_daemon_dbus_name_acquired_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) { g_debug("acquired name: %s", name); } static void fu_dbus_daemon_dbus_name_lost_cb(GDBusConnection *connection, const gchar *name, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); g_warning("another service has claimed the dbus name %s", name); fu_daemon_stop(FU_DAEMON(self), NULL); } static void fu_dbus_daemon_dbus_connection_closed_cb(GDBusConnection *connection, gboolean remote_peer_vanished, GError *error, gpointer user_data) { if (remote_peer_vanished) g_info("client connection closed: %s", error != NULL ? error->message : "unknown"); } static gboolean fu_dbus_daemon_dbus_new_connection_cb(GDBusServer *server, GDBusConnection *connection, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); fu_dbus_daemon_set_connection(self, connection); g_signal_connect(connection, "closed", G_CALLBACK(fu_dbus_daemon_dbus_connection_closed_cb), self); return fu_dbus_daemon_register_object(self, NULL); } static GDBusNodeInfo * fu_dbus_daemon_load_introspection(const gchar *filename, GError **error) { g_autoptr(GBytes) data = NULL; g_autofree gchar *path = NULL; /* lookup data */ path = g_build_filename("/org/freedesktop/fwupd", filename, NULL); data = g_resources_lookup_data(path, G_RESOURCE_LOOKUP_FLAGS_NONE, error); if (data == NULL) return NULL; /* build introspection from XML */ return g_dbus_node_info_new_for_xml(g_bytes_get_data(data, NULL), error); } static gboolean fu_dbus_daemon_setup(FuDaemon *daemon, const gchar *socket_address, FuProgress *progress, GError **error) { FuDbusDaemon *self = FU_DBUS_DAEMON(daemon); FuEngine *engine = fu_daemon_get_engine(daemon); /* progress */ fu_progress_set_id(progress, G_STRLOC); fu_progress_set_profile(progress, g_log_get_debug_enabled()); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 99, "load-engine"); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-introspection"); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "load-authority"); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "own-name"); /* load engine */ g_signal_connect(FU_ENGINE(engine), "changed", G_CALLBACK(fu_dbus_daemon_engine_changed_cb), self); g_signal_connect(FU_ENGINE(engine), "device-added", G_CALLBACK(fu_dbus_daemon_engine_device_added_cb), self); g_signal_connect(FU_ENGINE(engine), "device-removed", G_CALLBACK(fu_dbus_daemon_engine_device_removed_cb), self); g_signal_connect(FU_ENGINE(engine), "device-changed", G_CALLBACK(fu_dbus_daemon_engine_device_changed_cb), self); g_signal_connect(FU_ENGINE(engine), "device-request", G_CALLBACK(fu_dbus_daemon_engine_device_request_cb), self); g_signal_connect(FU_ENGINE(engine), "status-changed", G_CALLBACK(fu_dbus_daemon_engine_status_changed_cb), self); if (!fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | FU_ENGINE_LOAD_FLAG_ENSURE_CLIENT_CERT | FU_ENGINE_LOAD_FLAG_PATH_STORE_DEFAULTS | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_HISTORY, fu_progress_get_child(progress), error)) { g_prefix_error_literal(error, "failed to load engine: "); return FALSE; } fu_progress_step_done(progress); /* load introspection from file */ self->introspection_daemon = fu_dbus_daemon_load_introspection(FWUPD_DBUS_INTERFACE ".xml", error); if (self->introspection_daemon == NULL) { g_prefix_error_literal(error, "failed to load introspection: "); return FALSE; } fu_progress_step_done(progress); /* get authority */ self->authority = fu_polkit_authority_new(); if (!fu_polkit_authority_load(self->authority, error)) return FALSE; fu_progress_step_done(progress); /* own the object */ if (socket_address != NULL) { g_autofree gchar *guid = g_dbus_generate_guid(); g_autoptr(GDBusServer) server = NULL; server = g_dbus_server_new_sync(socket_address, #ifdef _WIN32 G_DBUS_SERVER_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS, #else G_DBUS_SERVER_FLAGS_AUTHENTICATION_REQUIRE_SAME_USER, #endif guid, NULL, NULL, error); if (server == NULL) { g_prefix_error_literal(error, "failed to create D-Bus server: "); return FALSE; } g_message("using socket address: %s", g_dbus_server_get_client_address(server)); g_dbus_server_start(server); g_signal_connect(server, "new-connection", G_CALLBACK(fu_dbus_daemon_dbus_new_connection_cb), self); } else { self->owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, FWUPD_DBUS_SERVICE, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | G_BUS_NAME_OWNER_FLAGS_REPLACE, fu_dbus_daemon_dbus_bus_acquired_cb, fu_dbus_daemon_dbus_name_acquired_cb, fu_dbus_daemon_dbus_name_lost_cb, self, NULL); } fu_progress_step_done(progress); /* success */ return TRUE; } static void fu_dbus_daemon_status_notify_cb(FuDaemon *daemon, GParamSpec *pspec, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(daemon); FwupdStatus status = fu_daemon_get_status(daemon); g_debug("emitting PropertyChanged('Status'='%s')", fwupd_status_to_string(status)); fu_dbus_daemon_emit_property_changed(self, "Status", g_variant_new_uint32(status)); } static void fu_dbus_daemon_percentage_notify_cb(FuDaemon *daemon, GParamSpec *pspec, gpointer user_data) { FuDbusDaemon *self = FU_DBUS_DAEMON(daemon); gdouble percentage = fu_daemon_get_percentage(daemon); g_debug("emitting PropertyChanged('Percentage'='%.1f%%')", percentage); fu_dbus_daemon_emit_property_changed( self, "Percentage", g_variant_new_uint32(fwupd_percentage_is_valid(percentage) ? (guint32)percentage : 0)); fu_dbus_daemon_emit_property_changed(self, "PercentageFull", g_variant_new_double(percentage)); } static void fu_dbus_daemon_init(FuDbusDaemon *self) { self->system_inhibits = g_ptr_array_new_with_free_func((GDestroyNotify)fu_dbus_daemon_system_inhibit_free); g_signal_connect(FU_DAEMON(self), "notify::status", G_CALLBACK(fu_dbus_daemon_status_notify_cb), NULL); g_signal_connect(FU_DAEMON(self), "notify::percentage", G_CALLBACK(fu_dbus_daemon_percentage_notify_cb), NULL); } static void fu_dbus_daemon_finalize(GObject *obj) { FuDbusDaemon *self = FU_DBUS_DAEMON(obj); g_ptr_array_unref(self->system_inhibits); if (self->client_list != NULL) g_object_unref(self->client_list); if (self->owner_id > 0) g_bus_unown_name(self->owner_id); if (self->proxy_uid != NULL) g_object_unref(self->proxy_uid); if (self->connection != NULL) g_object_unref(self->connection); if (self->authority != NULL) g_object_unref(self->authority); if (self->introspection_daemon != NULL) g_dbus_node_info_unref(self->introspection_daemon); G_OBJECT_CLASS(fu_dbus_daemon_parent_class)->finalize(obj); } static void fu_dbus_daemon_class_init(FuDbusDaemonClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); FuDaemonClass *daemon_class = FU_DAEMON_CLASS(klass); object_class->finalize = fu_dbus_daemon_finalize; daemon_class->setup = fu_dbus_daemon_setup; } FuDaemon * fu_daemon_new(void) { return FU_DAEMON(g_object_new(FU_TYPE_DBUS_DAEMON, NULL)); }