From ae0efdc5a7a9a312622e2e679da109c87498a2ee Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 24 Jun 2015 16:18:29 +0100 Subject: [PATCH] Provide a way for clients to add new firmware metadata to the system cache This is only possible if the metadata has been signed by a key that we trust. --- contrib/fwupd.spec.in | 4 + .../pki/GPG-KEY-Linux-Vendor-Firmware-Service | 110 +++++++++++++++ data/pki/Makefile.am | 3 + src/Makefile.am | 2 + src/fu-keyring.c | 88 ++++++++++++ src/fu-keyring.h | 4 + src/fu-main.c | 133 ++++++++++++++++++ src/fu-util.c | 84 +++++++++++ src/org.freedesktop.fwupd.xml | 29 ++++ 9 files changed, 457 insertions(+) create mode 100644 data/pki/GPG-KEY-Linux-Vendor-Firmware-Service diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 611ba4290..17422af13 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -79,6 +79,7 @@ find %{buildroot} -name '*.la' -exec rm -f {} ';' %{_libexecdir}/fwupd %{_bindir}/fwupdmgr %{_sysconfdir}/pki/fwupd +%{_sysconfdir}/pki/fwupd-metadata %{_sysconfdir}/dbus-1/system.d/org.freedesktop.fwupd.conf %{_datadir}/dbus-1/interfaces/org.freedesktop.fwupd.xml %{_datadir}/polkit-1/actions/org.freedesktop.fwupd.policy @@ -90,6 +91,9 @@ find %{buildroot} -name '*.la' -exec rm -f {} ';' %dir %{_localstatedir}/lib/fwupd %{_libdir}/lib*.so.* %{_libdir}/girepository-1.0/*.typelib +%dir %{_localstatedir}/cache/app-info +%dir %{_localstatedir}/cache/app-info/icons +%dir %{_localstatedir}/cache/app-info/xmls %files devel %{_includedir}/fwupd-1 diff --git a/data/pki/GPG-KEY-Linux-Vendor-Firmware-Service b/data/pki/GPG-KEY-Linux-Vendor-Firmware-Service new file mode 100644 index 000000000..11ae995a0 --- /dev/null +++ b/data/pki/GPG-KEY-Linux-Vendor-Firmware-Service @@ -0,0 +1,110 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v1 + +mQENBEgu03ABCAC+4WHuoCNAN0G1jHRSF2DAphtTIuZkhKbd3C1BMzvU40O5cwhW +LWILM1IYa4YwYI77tJsLi1Hax9D59NNAKdESqsKGuN/QbeXuAD3qpKA6B51lZD9k +jPizCl+s3q+NxaJb8Rk92yocYyuolNii5qrkD1YazcbFCkuBX2wg7q1hqU8YaGNx +Q3SuKf4Rkkg9T/6mCz6hEE8z4sVLncFY7pqt6ch+ycGz4MWGo9Eh51HvYi2QmSf3 +6OjNrKHp1LwPF1V/LYI9dHPXfeWE3tgco8hhDsgYaG2W3yhk8Pn5BhnNGgmiCXQs +Allf7a3U/leXgOMTVJNvx+8yNrHRuI2YZMRhABEBAAG0JFJpY2hhcmQgSHVnaGVz +IDxyaWNoYXJkQGh1Z2hzaWUuY29tPokBNgQTAQIAIAUCSC7TcAIbLwYLCQgHAwIE +FQIIAwQWAgMBAh4BAheAAAoJEBesuo36lw4XBVIH/RKjtx2Xk1782CGX9PZnwLaC +krfPTDlcusAFwqtV9AiECenXGLS3A3Kcq6BOJ9wCh1FF80mRJMwRn2ONvHEkg1Dh +8amv4YD9y4r6mjA6tyk7MOPNCSc8ZYZHUl/RacHAOePnKjMWSsU7n6v+RTpjOWR5 +JjyMlIHv7K9h6KEx5VCLaDXLxluQvPc8uYBZJlMBa3K/pGS18RJKKrw0l5/8p5tY +uWPxL4Zay7SWdGiiy3EPcq3GJXu85I1x+LbMbq69BjwQt28B/5iMD0RCbYF0mHG2 +6iQNU1Gr8+BX2+CFXLSbo1rPExsrOO3Maz7OjX4VathnqS0h9I+Q3dFlnks6Ic/R +zxnPFwEQAAEBAAAAAAAAAAAAAAAA/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgG +BgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAx +NDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIy +MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCACQAH0DASIA +AhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgED +AwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2Jy +ggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1 +dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJ +ytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAA +AAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJB +UQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNE +RUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJma +oqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP0 +9fb3+Pn6/9oADAMBAAIRAxEAPwDtVhmb0FSLaOfvNV4LS4rmJKq2a9yakFrGO1WA +Kpapq1lo1o11fTrFGo6nv7CgLFkQoozgACud1nxx4d0HetzfRtMv/LGL5m/SvLvG +HxR1HVGe10gm1tPus4Hzv757CvOJS8h8yR9xPU55NUo3K5e57VL8bbBXYQaVM4/h +LSAZ/Sn23xptHb9/pMyJ6pICf6V4aGyflFWYmJGGxVOBSSPoTTvit4cvZFSVprVj +xmVOPzGa7OzvbW/gWe0uI5o25DRsCK+T9o525I/lVvTPEOqaBdiawupIWB5A6N9R +0NTyvoJxPq7FGK868G/FSw1xI7XVGjtb8naMcI59vSvRlIZQykEHoRUkNCVn6r/q +R9a0qztUH7taBDtLGLXPvV2q2nDFoPrVrFAFelxSCgnAJpDMXxH4kttAtCzkGZlJ +Re3414vrfiX+3b5pLr992CEkKPoBS/EPWJrzxDMjsQinaoBB4rQ8DeF47krf3ceR +1jVv51nNpLmkdlCF3ZbmRNoS3tiZbezaJ8epxXMzaTOrnzFOFznjpivpGLSoHi2G +NcemKyrvwZZzb1EQG7g4rGniGmdlTC3R4Pb6cDyFzxnmrtvpMk7BY0JbOMAV6W3w +9mguv3Kh4zkD1Ga7jwz4EtdPtxLdKHlJzjHSrdZyehl7FQV5Hitr4IvrxisULZAz +nHWs3VPBmq2BYS2zADvjg19Sx6fa23+qiVfwqC8s4LqMpLGrA+oqfaziPkpzPkGS +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 +vEI5YQ== +=/V/G +-----END PGP PUBLIC KEY BLOCK----- diff --git a/data/pki/Makefile.am b/data/pki/Makefile.am index 17dcca7ab..3ff9b71a3 100644 --- a/data/pki/Makefile.am +++ b/data/pki/Makefile.am @@ -1,4 +1,7 @@ pkidir = $(sysconfdir)/pki/fwupd dist_pki_DATA = GPG-KEY-Hughski-Limited +pkimetadatadir = $(sysconfdir)/pki/fwupd-metadata +dist_pkimetadata_DATA = GPG-KEY-Linux-Vendor-Firmware-Service + -include $(top_srcdir)/git.mk diff --git a/src/Makefile.am b/src/Makefile.am index 72abf4669..abb6f151a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -166,6 +166,8 @@ install-data-hook: if test -w $(DESTDIR)$(prefix)/; then \ mkdir -p $(DESTDIR)$(localstatedir)/lib/fwupd; \ chmod 0755 $(DESTDIR)$(localstatedir)/lib/fwupd; \ + mkdir -p $(DESTDIR)$(localstatedir)/cache/app-info/xmls; \ + mkdir -p $(DESTDIR)$(localstatedir)/cache/app-info/icons; \ fi TESTS = fu-self-test diff --git a/src/fu-keyring.c b/src/fu-keyring.c index 6e4855d95..9ae0c1f8c 100644 --- a/src/fu-keyring.c +++ b/src/fu-keyring.c @@ -321,6 +321,94 @@ out: return ret; } +/** + * fu_keyring_verify_data: + **/ +gboolean +fu_keyring_verify_data (FuKeyring *keyring, + GBytes *payload, + GBytes *payload_signature, + GError **error) +{ + gboolean ret = TRUE; + gpgme_data_t data = NULL; + gpgme_data_t sig = NULL; + gpgme_error_t rc; + gpgme_signature_t s; + gpgme_verify_result_t result; + + g_return_val_if_fail (FU_IS_KEYRING (keyring), FALSE); + g_return_val_if_fail (payload != NULL, FALSE); + g_return_val_if_fail (payload_signature != NULL, FALSE); + + /* setup context */ + if (!fu_keyring_setup (keyring, error)) + return FALSE; + + /* load file data */ + rc = gpgme_data_new_from_mem (&data, + g_bytes_get_data (payload, NULL), + g_bytes_get_size (payload), 0); + if (rc != GPG_ERR_NO_ERROR) { + ret = FALSE; + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to load data: %s", + gpgme_strerror (rc)); + goto out; + } + rc = gpgme_data_new_from_mem (&sig, + g_bytes_get_data (payload_signature, NULL), + g_bytes_get_size (payload_signature), 0); + if (rc != GPG_ERR_NO_ERROR) { + ret = FALSE; + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to load signature: %s", + gpgme_strerror (rc)); + goto out; + } + + /* verify */ + rc = gpgme_op_verify (keyring->priv->ctx, sig, data, NULL); + if (rc != GPG_ERR_NO_ERROR) { + ret = FALSE; + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to verify data: %s", + gpgme_strerror (rc)); + goto out; + } + + + /* verify the result */ + result = gpgme_op_verify_result (keyring->priv->ctx); + if (result == NULL) { + ret = FALSE; + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no result record from libgpgme"); + goto out; + } + + /* look at each signature */ + for (s = result->signatures; s != NULL ; s = s->next ) { + ret = fu_keyring_check_signature (s, error); + if (!ret) + goto out; + } +out: + if (data != NULL) + gpgme_data_release (data); + if (sig != NULL) + gpgme_data_release (sig); + return ret; +} + /** * fu_keyring_class_init: **/ diff --git a/src/fu-keyring.h b/src/fu-keyring.h index 75cd6fb78..091f3c12a 100644 --- a/src/fu-keyring.h +++ b/src/fu-keyring.h @@ -63,6 +63,10 @@ gboolean fu_keyring_verify_file (FuKeyring *keyring, const gchar *filename, const gchar *signature, GError **error); +gboolean fu_keyring_verify_data (FuKeyring *keyring, + GBytes *payload, + GBytes *payload_signature, + GError **error); G_END_DECLS diff --git a/src/fu-main.c b/src/fu-main.c index 5b2768890..ec9f286e0 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include "fu-cleanup.h" #include "fu-debug.h" #include "fu-device.h" +#include "fu-keyring.h" #include "fu-pending.h" #include "fu-provider.h" #include "fu-provider-usb.h" @@ -566,6 +568,101 @@ fu_main_get_action_id_for_device (FuMainAuthHelper *helper) return "org.freedesktop.fwupd.update-internal"; } +/** + * fu_main_daemon_update_metadata: + * + * Supports optionally GZipped AppStream files up to 1MiB in size. + **/ +static gboolean +fu_main_daemon_update_metadata (gint fd, gint fd_sig, GError **error) +{ + guint8 magic[2]; + _cleanup_bytes_unref_ GBytes *bytes = NULL; + _cleanup_bytes_unref_ GBytes *bytes_raw = NULL; + _cleanup_bytes_unref_ GBytes *bytes_sig = NULL; + _cleanup_object_unref_ AsStore *store = NULL; + _cleanup_object_unref_ FuKeyring *kr = NULL; + _cleanup_object_unref_ GConverter *converter = NULL; + _cleanup_object_unref_ GFile *file = NULL; + _cleanup_object_unref_ GInputStream *stream_buf = NULL; + _cleanup_object_unref_ GInputStream *stream_fd = NULL; + _cleanup_object_unref_ GInputStream *stream = NULL; + _cleanup_object_unref_ GInputStream *stream_sig = NULL; + + /* open existing file if it exists */ + store = as_store_new (); + file = g_file_new_for_path ("/var/cache/app-info/xmls/fwupd.xml"); + if (g_file_query_exists (file, NULL)) { + if (!as_store_from_file (store, file, NULL, NULL, error)) + return FALSE; + } + + /* read the entire file into memory */ + stream_fd = g_unix_input_stream_new (fd, TRUE); + bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error); + if (bytes_raw == NULL) + return FALSE; + stream_buf = g_memory_input_stream_new (); + g_memory_input_stream_add_bytes (G_MEMORY_INPUT_STREAM (stream_buf), bytes_raw); + + /* peek the file type and get data */ + if (g_input_stream_read (stream_buf, magic, 2, NULL, error) == -1) + return FALSE; + if (magic[0] == 0x1f && magic[1] == 0x8b) { + g_debug ("using GZip decompressor for data"); + if (!g_seekable_seek (G_SEEKABLE (stream_buf), 0, G_SEEK_SET, NULL, error)) + return FALSE; + converter = G_CONVERTER (g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP)); + stream = g_converter_input_stream_new (stream_buf, converter); + bytes = g_input_stream_read_bytes (stream, 0x100000, NULL, error); + if (bytes == NULL) + return FALSE; + } else if (magic[0] == '<' && magic[1] == '?') { + g_debug ("using no decompressor for data"); + bytes = g_bytes_ref (bytes_raw); + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "file type '0x%02x,0x%02x' not supported", + magic[0], magic[1]); + return FALSE; + } + + /* read signature */ + stream_sig = g_unix_input_stream_new (fd_sig, TRUE); + bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error); + if (bytes_sig == NULL) + return FALSE; + + /* verify file */ + kr = fu_keyring_new (); + if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error)) + return FALSE; + if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error)) + return FALSE; + + /* merge in the new contents */ + g_debug ("Store was %i size", as_store_get_size (store)); + if (!as_store_from_xml (store, + g_bytes_get_data (bytes, NULL), -1, + NULL, error)) + return FALSE; + g_debug ("Store now %i size", as_store_get_size (store)); + + /* save the new file */ + as_store_set_api_version (store, 0.9); + if (!as_store_to_file (store, file, + AS_NODE_TO_XML_FLAG_ADD_HEADER | + AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE | + AS_NODE_TO_XML_FLAG_FORMAT_INDENT, + NULL, error)) { + return FALSE; + } + + return TRUE; +} + /** * fu_main_daemon_method_call: **/ @@ -648,6 +745,42 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, return; } + /* return '' */ + if (g_strcmp0 (method_name, "UpdateMetadata") == 0) { + GDBusMessage *message; + GUnixFDList *fd_list; + gint fd_data; + gint fd_sig; + _cleanup_error_free_ GError *error = NULL; + + message = g_dbus_method_invocation_get_message (invocation); + fd_list = g_dbus_message_get_unix_fd_list (message); + if (fd_list == NULL || g_unix_fd_list_get_length (fd_list) != 2) { + g_dbus_method_invocation_return_error (invocation, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid handle"); + return; + } + fd_data = g_unix_fd_list_get (fd_list, 0, &error); + if (fd_data < 0) { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } + fd_sig = g_unix_fd_list_get (fd_list, 1, &error); + if (fd_sig < 0) { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } + if (!fu_main_daemon_update_metadata (fd_data, fd_sig, &error)) { + g_prefix_error (&error, "failed to update metadata: "); + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } + g_dbus_method_invocation_return_value (invocation, NULL); + return; + } + /* return '' */ if (g_strcmp0 (method_name, "Update") == 0) { FuDeviceItem *item = NULL; diff --git a/src/fu-util.c b/src/fu-util.c index c6d2163e7..a4fc7e2e6 100644 --- a/src/fu-util.c +++ b/src/fu-util.c @@ -776,6 +776,84 @@ fu_util_clear_results (FuUtilPrivate *priv, gchar **values, GError **error) return TRUE; } +/** + * fu_util_update_metadata: + **/ +static gboolean +fu_util_update_metadata (FuUtilPrivate *priv, gchar **values, GError **error) +{ + GVariant *body; + gint fd; + gint fd_sig; + _cleanup_object_unref_ GDBusMessage *request = NULL; + _cleanup_object_unref_ GUnixFDList *fd_list = NULL; + + if (g_strv_length (values) != 2) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Invalid arguments: expected 'filename.xml' 'filename.xml.asc'"); + return FALSE; + } + + /* open file */ + fd = open (values[0], O_RDONLY); + if (fd < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to open %s", + values[0]); + return FALSE; + } + fd_sig = open (values[1], O_RDONLY); + if (fd_sig < 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to open %s", + values[1]); + return FALSE; + } + + /* set out of band file descriptor */ + fd_list = g_unix_fd_list_new (); + g_unix_fd_list_append (fd_list, fd, NULL); + g_unix_fd_list_append (fd_list, fd_sig, NULL); + request = g_dbus_message_new_method_call (FWUPD_DBUS_SERVICE, + FWUPD_DBUS_PATH, + FWUPD_DBUS_INTERFACE, + "UpdateMetadata"); + g_dbus_message_set_unix_fd_list (request, fd_list); + + /* g_unix_fd_list_append did a dup() already */ + close (fd); + close (fd_sig); + + /* send message */ + body = g_variant_new ("(hh)", fd, fd_sig); + g_dbus_message_set_body (request, body); + g_dbus_connection_send_message_with_reply (priv->conn, + request, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + -1, + NULL, + NULL, + fu_util_update_cb, + priv); + g_main_loop_run (priv->loop); + if (priv->message == NULL) { + g_dbus_error_strip_remote_error (priv->error); + g_propagate_error (error, priv->error); + return FALSE; + } + if (g_dbus_message_to_gerror (priv->message, error)) { + g_dbus_error_strip_remote_error (*error); + return FALSE; + } + return TRUE; +} + /** * fu_util_get_results: **/ @@ -1061,6 +1139,12 @@ main (int argc, char *argv[]) /* TRANSLATORS: command description */ _("Gets the results from the last update"), fu_util_get_results); + fu_util_add (priv->cmd_array, + "update-metadata", + NULL, + /* TRANSLATORS: command description */ + _("Updates metadata"), + fu_util_update_metadata); /* sort by command name */ g_ptr_array_sort (priv->cmd_array, diff --git a/src/org.freedesktop.fwupd.xml b/src/org.freedesktop.fwupd.xml index 0bb9eb2b8..781f3e981 100644 --- a/src/org.freedesktop.fwupd.xml +++ b/src/org.freedesktop.fwupd.xml @@ -169,6 +169,35 @@ + + + + + + Adds AppStream resource information from a session client. + + + + + + + + File handle to AppStream metadata. + + + + + + + + + File handle to AppStream metadata GPG signature. + + + + + +