Allow running fwsignd as the non-root user

...however, most users will still want to use the root user so that the owner
and group are set correctly on the destination files.
This commit is contained in:
Richard Hughes 2015-07-21 13:05:44 +01:00
parent 0bdd5cbcb8
commit ebaf7ba035
4 changed files with 158 additions and 27 deletions

View File

@ -73,6 +73,12 @@ find %{buildroot} -name '*.la' -exec rm -f {} ';'
%find_lang %{name}
%pre
getent group fwsignd >/dev/null || groupadd -r fwsignd
getent passwd fwsignd >/dev/null || \
useradd -r -g fwsignd -d /var/lib/fwsignd -s /sbin/nologin \
-c "User for fwsignd" fwsignd
%post
/sbin/ldconfig
%systemd_post fwupd.service
@ -112,6 +118,7 @@ find %{buildroot} -name '*.la' -exec rm -f {} ';'
%config(noreplace)%{_sysconfdir}/fwsignd.conf
%{_unitdir}/fwsignd.service
%{_libexecdir}/fwsignd
%attr(755,fwsignd,fwsignd) %dir %{_localstatedir}/lib/fwsignd
%files devel
%{_includedir}/fwupd-1

View File

@ -4,11 +4,36 @@
#
# IMPORTANT: the files we could process are deleted from this location when
# they have been signed successfully.
SourceDirectory=/mnt/source
#
# You will need to modify the permissions on this directory so that the signd
# user can read from this location, e.g.
# # chown fwsignd:fwsignd /srv/fwsignd/source
#
# If you are running as non-root, you probably also want to do something like:
# # chmod g+s /srv/fwsignd/source
SourceDirectory=/mnt/fwsignd/source
# The path where we write new signed .cab files
DestinationDirectory=/mnt/destination
#
# You will need to modify the permissions on this directory so that the signd
# user can write to this location, e.g.
# # chown fwsignd:fwsignd /srv/fwsignd/destination -R
DestinationDirectory=/mnt/fwsignd/destination
# The GPG key ID used for signing -- you probably want to create a key without
# a password, e.g. gpg2 --gen-key && gpg2 --edit-key; passwd; quit
# a password, e.g.
# # useradd temp
# # su -l temp
# $ gpg2 --full-gen-key
# $ gpg2 --list-secret-keys
# $ gpg2 --edit-key 12345678
# gpg> passwd
# gpg> quit
# # cp /home/temp/.gnupg /var/lib/fwsignd/ -Rv
# # chown fwsignd:fwsignd /var/lib/fwsignd/.gnupg -R
KeyID=12345678
# Set the destination files to the same user and group as the source files
#
# This requires that the fwsignd binary is being run as the root user.
SetDestinationOwner=true

View File

@ -210,6 +210,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)/lib/fwsignd; \
chmod 0755 $(DESTDIR)$(localstatedir)/lib/fwsignd; \
mkdir -p $(DESTDIR)$(localstatedir)/cache/app-info/xmls; \
mkdir -p $(DESTDIR)$(localstatedir)/cache/app-info/icons; \
fi

View File

@ -21,11 +21,13 @@
#include "config.h"
#include <appstream-glib.h>
#include <fwupd.h>
#include <gio/gio.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "fu-cleanup.h"
#include "fu-cab.h"
@ -36,16 +38,18 @@ typedef struct {
gchar *source;
gchar *destination;
gchar *key_id;
gboolean set_owner;
FuKeyring *keyring;
} FuSignPrivate;
/**
* fu_sign_process_file:
* fu_sign_process_file_cab:
**/
static gboolean
fu_sign_process_file (FuSignPrivate *priv,
const gchar *fn_src,
const gchar *fn_dst,
GError **error)
fu_sign_process_file_cab (FuSignPrivate *priv,
const gchar *fn_src,
const gchar *fn_dst,
GError **error)
{
gsize fw_len = 0;
_cleanup_bytes_unref_ GBytes *fw = NULL;
@ -53,7 +57,6 @@ fu_sign_process_file (FuSignPrivate *priv,
_cleanup_free_ gchar *fw_data = NULL;
_cleanup_free_ gchar *fn_bin_asc = NULL;
_cleanup_object_unref_ FuCab *cab = NULL;
_cleanup_object_unref_ FuKeyring *kr = NULL;
_cleanup_object_unref_ GFile *src = NULL;
_cleanup_object_unref_ GFile *dst = NULL;
const gchar *fn_bin;
@ -71,15 +74,10 @@ fu_sign_process_file (FuSignPrivate *priv,
/* sign the .bin */
fn_bin = fu_cab_get_filename_firmware (cab);
g_info ("signing %s with key %s", fn_bin, priv->key_id);
kr = fu_keyring_new ();
if (priv->key_id != NULL) {
if (!fu_keyring_set_signing_key (kr, priv->key_id, error))
return FALSE;
}
if (!g_file_get_contents (fn_bin, &fw_data, &fw_len, error))
return FALSE;
fw = g_bytes_new_static (fw_data, fw_len);
sig = fu_keyring_sign_data (kr, fw, error);
sig = fu_keyring_sign_data (priv->keyring, fw, error);
if (sig == NULL)
return FALSE;
@ -101,10 +99,6 @@ fu_sign_process_file (FuSignPrivate *priv,
if (!fu_cab_save_file (cab, dst, NULL, error))
return FALSE;
/* delete the source file */
g_debug ("deleting %s", fn_src);
g_unlink (fn_src);
/* delete the working space */
g_unlink (fn_bin_asc);
if (!fu_cab_delete_temp_files (cab, error))
@ -112,6 +106,105 @@ fu_sign_process_file (FuSignPrivate *priv,
return TRUE;
}
/**
* fu_sign_process_file_xml:
**/
static gboolean
fu_sign_process_file_xml (FuSignPrivate *priv,
const gchar *fn_src,
const gchar *fn_dst,
GError **error)
{
gsize xml_len = 0;
_cleanup_bytes_unref_ GBytes *xml = NULL;
_cleanup_bytes_unref_ GBytes *sig = NULL;
_cleanup_free_ gchar *xml_data = NULL;
/* sign the firmware */
g_info ("signing %s with key %s", fn_src, priv->key_id);
if (!g_file_get_contents (fn_src, &xml_data, &xml_len, error))
return FALSE;
xml = g_bytes_new_static (xml_data, xml_len);
sig = fu_keyring_sign_data (priv->keyring, xml, error);
if (sig == NULL)
return FALSE;
/* write new detached signature */
g_debug ("writing to %s", fn_dst);
if (!g_file_set_contents (fn_dst,
g_bytes_get_data (sig, NULL),
g_bytes_get_size (sig),
error))
return FALSE;
return TRUE;
}
/**
* fu_sign_process_file:
**/
static gboolean
fu_sign_process_file (FuSignPrivate *priv,
const gchar *fn_src,
GError **error)
{
guint32 uid;
guint32 gid;
gboolean ret = FALSE;
_cleanup_free_ gchar *fn_dst = NULL;
_cleanup_free_ gchar *basename = NULL;
_cleanup_object_unref_ GFileInfo *info = NULL;
_cleanup_object_unref_ GFile *src = NULL;
/* get the file owner */
src = g_file_new_for_path (fn_src);
info = g_file_query_info (src,
G_FILE_ATTRIBUTE_UNIX_UID ","
G_FILE_ATTRIBUTE_UNIX_GID ","
G_FILE_ATTRIBUTE_OWNER_USER ","
G_FILE_ATTRIBUTE_OWNER_GROUP,
G_FILE_QUERY_INFO_NONE,
NULL, error);
if (info == NULL)
return FALSE;
/* process these in different ways */
if (g_str_has_suffix (fn_src, ".cab")) {
/* cab file */
basename = g_path_get_basename (fn_src);
fn_dst = g_build_filename (priv->destination, basename, NULL);
ret = fu_sign_process_file_cab (priv, fn_src, fn_dst, error);
} else if (as_app_guess_source_kind (fn_src) == AS_APP_SOURCE_KIND_APPSTREAM) {
/* AppStream metadata */
basename = g_path_get_basename (fn_src);
fn_dst = g_strdup_printf ("%s/%s.asc", priv->destination, basename);
ret = fu_sign_process_file_xml (priv, fn_src, fn_dst, error);
} else {
/* unknown */
g_set_error_literal (error,
FWUPD_ERROR,
FWUPD_ERROR_INVALID_FILE,
"unknown file type");
}
if (!ret)
return FALSE;
/* set the owner:group on the new file */
if (priv->set_owner) {
uid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID);
gid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID);
g_debug ("Attempting to set unix owner to %s:%s",
g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER),
g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP));
chown (fn_dst, uid, gid);
}
/* only delete the source file if *everything* worked */
g_debug ("deleting %s", fn_src);
g_unlink (fn_src);
return TRUE;
}
/**
* fu_sign_coldplug:
**/
@ -126,13 +219,11 @@ fu_sign_coldplug (FuSignPrivate *priv, GError **error)
while (TRUE) {
const gchar *fn;
_cleanup_free_ gchar *fn_src = NULL;
_cleanup_free_ gchar *fn_dst = NULL;
fn = g_dir_read_name (dir);
if (fn == NULL)
break;
fn_src = g_build_filename (priv->source, fn, NULL);
fn_dst = g_build_filename (priv->destination, fn, NULL);
if (!fu_sign_process_file (priv, fn_src, fn_dst, error))
if (!fu_sign_process_file (priv, fn_src, error))
return FALSE;
}
return TRUE;
@ -148,8 +239,6 @@ fu_sign_monitor_changed_cb (GFileMonitor *monitor,
FuSignPrivate *priv)
{
_cleanup_error_free_ GError *error = NULL;
_cleanup_free_ gchar *basename = NULL;
_cleanup_free_ gchar *fn_dst = NULL;
_cleanup_free_ gchar *fn_src = NULL;
/* only new files */
@ -160,9 +249,7 @@ fu_sign_monitor_changed_cb (GFileMonitor *monitor,
return;
/* process file */
basename = g_file_get_basename (file);
fn_dst = g_build_filename (priv->destination, basename, NULL);
if (!fu_sign_process_file (priv, fn_src, fn_dst, &error)) {
if (!fu_sign_process_file (priv, fn_src, &error)) {
g_warning ("failed to process %s: %s", fn_src, error->message);
return;
}
@ -226,6 +313,7 @@ main (int argc, char *argv[])
/* fall back to keyfile */
config = g_key_file_new ();
config_file = g_build_filename (SYSCONFDIR, "fwsignd.conf", NULL);
g_debug ("Loading fallback values from %s", config_file);
if (!g_key_file_load_from_file (config, config_file,
G_KEY_FILE_NONE, &error)) {
g_print ("failed to load config file %s: %s\n",
@ -248,6 +336,14 @@ main (int argc, char *argv[])
priv->source = g_strdup (source);
priv->destination = g_strdup (destination);
priv->key_id = g_strdup (key_id);
priv->keyring = fu_keyring_new ();
priv->set_owner = g_key_file_get_boolean (config, "fwupd", "SetDestinationOwner", NULL);
if (priv->key_id != NULL &&
!fu_keyring_set_signing_key (priv->keyring, priv->key_id, &error)) {
g_print ("valid GPG key required: %s\n", error->message);
retval = EXIT_FAILURE;
goto out;
}
/* process */
g_debug ("clearing queue");
@ -277,6 +373,7 @@ main (int argc, char *argv[])
retval = 0;
out:
if (priv != NULL) {
g_object_unref (priv->keyring);
g_free (priv->source);
g_free (priv->destination);
g_free (priv->key_id);