diff options
author | David Zeuthen <davidz@redhat.com> | 2011-08-11 15:46:12 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2011-08-11 15:46:12 -0400 |
commit | ff5d150f8b20a97f02dccd7897f8df0c65136930 (patch) | |
tree | 07b58060e74063cc2781858abff8bde2e4415b4b | |
parent | 0ffe041523bd8eac3765dc4ef74c3a679870dcd6 (diff) |
Add support for /etc/crypttab
Signed-off-by: David Zeuthen <davidz@redhat.com>
-rw-r--r-- | data/org.freedesktop.UDisks2.xml | 107 | ||||
-rw-r--r-- | doc/udisks2-docs.xml | 2 | ||||
-rw-r--r-- | doc/udisks2-sections.txt | 32 | ||||
-rw-r--r-- | doc/udisks2.types | 2 | ||||
-rw-r--r-- | policy/org.freedesktop.udisks2.policy.in | 20 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/udiskscrypttabentry.c | 214 | ||||
-rw-r--r-- | src/udiskscrypttabentry.h | 42 | ||||
-rw-r--r-- | src/udiskscrypttabmonitor.c | 444 | ||||
-rw-r--r-- | src/udiskscrypttabmonitor.h | 38 | ||||
-rw-r--r-- | src/udisksdaemon.c | 26 | ||||
-rw-r--r-- | src/udisksdaemon.h | 1 | ||||
-rw-r--r-- | src/udisksdaemontypes.h | 6 | ||||
-rw-r--r-- | src/udiskslinuxblock.c | 647 | ||||
-rw-r--r-- | src/udiskslinuxprovider.c | 42 | ||||
-rw-r--r-- | src/udisksprivate.h | 6 |
16 files changed, 1573 insertions, 58 deletions
diff --git a/data/org.freedesktop.UDisks2.xml b/data/org.freedesktop.UDisks2.xml index 7048cee..4a5b21c 100644 --- a/data/org.freedesktop.UDisks2.xml +++ b/data/org.freedesktop.UDisks2.xml @@ -372,13 +372,26 @@ <property name="IdUUID" type="ay" access="read"/> <!-- Configuration: - The configuration sources for the device. + The configuration for the device. This is an array of pairs of (@type, @details) where @type is a string identifying the configuration source (e.g. <literal>fstab</literal>) and @details contains the actual configuration data. + Use the + org.freedesktop.UDisks2.BlockDevice.AddConfigurationItem(), + org.freedesktop.UDisks2.BlockDevice.RemoveConfigurationItem() + and + org.freedesktop.UDisks2.BlockDevice.UpdateConfigurationItem() + methods to add, remove and update configuration items. + + Use + org.freedesktop.UDisks2.BlockDevice.GetSecretConfiguration() + to get the secrets (e.g. passphrases) that may be part of the + configuration but isn't exported in this property for + security reasons. + For entries of type <literal>fstab</literal>, it means that the block device is referenced in the system-wide <filename>/etc/fstab</filename> file. Known configuration @@ -410,11 +423,44 @@ </varlistentry> </variablelist> - Use the + For entries of type <literal>crypttab</literal>, it means that + the block device is referenced in the system-wide + <filename>/etc/crypttab</filename> file. Known configuration + items for type <literal>crypttab</literal> are + <variablelist> + <varlistentry> + <term>name (type <literal>'ay'</literal>)</term> + <listitem><para>The name to set the device up as</para></listitem> + </varlistentry> + <varlistentry> + <term>device (type <literal>'ay'</literal>)</term> + <listitem><para>The special device</para></listitem> + </varlistentry> + <varlistentry> + <term>passphrase-path (type <literal>'ay'</literal>)</term> + <listitem><para>Either empty to specify that no password is set, + otherwise a path to a file containing the encryption password. + This may also point to a special device file in <filename>/dev</filename> + such as <literal>/dev/random</literal>. + </para></listitem> + </varlistentry> + <varlistentry> + <term>passphrase-contents (type <literal>'ay'</literal>)</term> + <listitem><para>The contents of the file containing the encryption password, if applicable. + This is only available via the org.freedesktop.UDisks2.BlockDevice.GetSecretConfiguration() + method.</para></listitem> + </varlistentry> + <varlistentry> + <term>opts (type <literal>'ay'</literal>)</term> + <listitem><para>Options</para></listitem> + </varlistentry> + </variablelist> + For security reasons, when creating a new + <literal>crypttab</literal> entry (via the org.freedesktop.UDisks2.BlockDevice.AddConfigurationItem() - and - org.freedesktop.UDisks2.BlockDevice.RemoveConfigurationItem() - methods to add/remove configuration items. + method), then the <option>passphrase-path</option> must + reference an unexisting file in the + <filename>/etc/luks-keys</filename> directory. --> <property name="Configuration" type="a(sa{sv})" access="read"/> @@ -494,9 +540,10 @@ @item: The configuration item to add. @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>). - Adds a new configuration item. See the - #org.freedesktop.UDisks2.BlockDevice:Configuration property - for details about valid configuration items. + Adds a new configuration item. + + See the #org.freedesktop.UDisks2.BlockDevice:Configuration + property for details about valid configuration items. --> <method name="AddConfigurationItem"> <arg name="item" direction="in" type="(sa{sv})"/> @@ -508,15 +555,53 @@ @item: The configuration item to remove. @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>). - Removes a new configuration item. See the - #org.freedesktop.UDisks2.BlockDevice:Configuration property - for details about valid configuration items. + Removes an existing configuration item. + + See the #org.freedesktop.UDisks2.BlockDevice:Configuration + property for details about valid configuration items. --> <method name="RemoveConfigurationItem"> <arg name="item" direction="in" type="(sa{sv})"/> <arg name="options" direction="in" type="a{sv}"/> </method> + <!-- + UpdateConfigurationItem: + @old_item: The configuration item to remove. + @new_item: The configuration item to add. Must be of the same type as @old_item. + @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>). + + Removes a configuration item and adds a new one. This is + equivalent to calling + org.freedesktop.UDisks2.BlockDevice.RemoveConfigurationItem() + followed by + org.freedesktop.UDisks2.BlockDevice.AddConfigurationItem() + with the change that only one PolicyKit check is made + and that @new_item can be validated against @old_item. + + See the #org.freedesktop.UDisks2.BlockDevice:Configuration + property for details about valid configuration items. + --> + <method name="UpdateConfigurationItem"> + <arg name="old_item" direction="in" type="(sa{sv})"/> + <arg name="new_item" direction="in" type="(sa{sv})"/> + <arg name="options" direction="in" type="a{sv}"/> + </method> + + <!-- + GetSecretConfiguration: + @options: Options (currently unused except for <link linkend="udisks-std-options">standard options</link>). + @configuration: The resulting configuration. + + Returns the same value as in the + #org.freedesktop.UDisks2.BlockDevice:Configuration property + but without secret information filtered out. + --> + <method name="GetSecretConfiguration"> + <arg name="options" direction="in" type="a{sv}"/> + <arg name="configuration" direction="out" type="a(sa{sv})"/> + </method> + </interface> <!-- ********************************************************************** --> diff --git a/doc/udisks2-docs.xml b/doc/udisks2-docs.xml index 8cd93c6..854f37e 100644 --- a/doc/udisks2-docs.xml +++ b/doc/udisks2-docs.xml @@ -178,6 +178,8 @@ <xi:include href="xml/udisksmountmonitor.xml"/> <xi:include href="xml/udisksfstabentry.xml"/> <xi:include href="xml/udisksfstabmonitor.xml"/> + <xi:include href="xml/udiskscrypttabentry.xml"/> + <xi:include href="xml/udiskscrypttabmonitor.xml"/> <xi:include href="xml/udiskspersistentstore.xml"/> <xi:include href="xml/udiskscleanup.xml"/> <xi:include href="xml/udisksprovider.xml"/> diff --git a/doc/udisks2-sections.txt b/doc/udisks2-sections.txt index b29bcc1..b6ca719 100644 --- a/doc/udisks2-sections.txt +++ b/doc/udisks2-sections.txt @@ -59,6 +59,7 @@ udisks_daemon_get_connection udisks_daemon_get_object_manager udisks_daemon_get_mount_monitor udisks_daemon_get_fstab_monitor +udisks_daemon_get_crypttab_monitor udisks_daemon_get_linux_provider udisks_daemon_get_persistent_store udisks_daemon_get_authority @@ -1018,3 +1019,34 @@ UDISKS_IS_FSTAB_MONITOR <SUBSECTION Private> udisks_fstab_monitor_get_type </SECTION> + +<SECTION> +<FILE>udiskscrypttabentry</FILE> +<TITLE>UDisksCrypttabEntry</TITLE> +UDisksCrypttabEntry +udisks_crypttab_entry_get_name +udisks_crypttab_entry_get_device +udisks_crypttab_entry_get_passphrase_path +udisks_crypttab_entry_get_options +udisks_crypttab_entry_compare +<SUBSECTION Standard> +UDISKS_TYPE_CRYPTTAB_ENTRY +UDISKS_CRYPTTAB_ENTRY +UDISKS_IS_CRYPTTAB_ENTRY +<SUBSECTION Private> +udisks_crypttab_entry_get_type +</SECTION> + +<SECTION> +<FILE>udiskscrypttabmonitor</FILE> +<TITLE>UDisksCrypttabMonitor</TITLE> +UDisksCrypttabMonitor +udisks_crypttab_monitor_new +udisks_crypttab_monitor_get_entries +<SUBSECTION Standard> +UDISKS_TYPE_CRYPTTAB_MONITOR +UDISKS_CRYPTTAB_MONITOR +UDISKS_IS_CRYPTTAB_MONITOR +<SUBSECTION Private> +udisks_crypttab_monitor_get_type +</SECTION> diff --git a/doc/udisks2.types b/doc/udisks2.types index 79031a2..7b0ceee 100644 --- a/doc/udisks2.types +++ b/doc/udisks2.types @@ -20,6 +20,8 @@ udisks_linux_manager_get_type udisks_cleanup_get_type udisks_fstab_entry_get_type udisks_fstab_monitor_get_type +udisks_crypttab_entry_get_type +udisks_crypttab_monitor_get_type udisks_drive_get_type udisks_drive_proxy_get_type diff --git a/policy/org.freedesktop.udisks2.policy.in b/policy/org.freedesktop.udisks2.policy.in index 9d76a85..8e61608 100644 --- a/policy/org.freedesktop.udisks2.policy.in +++ b/policy/org.freedesktop.udisks2.policy.in @@ -132,14 +132,30 @@ </defaults> </action> - <!-- Manage system-wide configuration files such as /etc/fstab --> + <!-- Manage system-wide configuration files such as /etc/fstab or + /etc/crypttab ... including files referenced by these files. + + It is insecure to automatically grant this to groups of users or + to allow a process to retain the authorization. + --> <action id="org.freedesktop.udisks2.modify-system-configuration"> <_description>Modify system-wide configuration</_description> <_message>Authentication is required to modify system-wide configuration</_message> <defaults> <allow_any>auth_admin</allow_any> <allow_inactive>auth_admin</allow_inactive> - <allow_active>auth_admin_keep</allow_active> + <allow_active>auth_admin</allow_active> + </defaults> + </action> + + <!-- Get secrets from system-wide configuration files --> + <action id="org.freedesktop.udisks2.read-system-configuration-secrets"> + <_description>Modify system-wide configuration</_description> + <_message>Authentication is required to retrieve secrets from system-wide configuration</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin</allow_active> </defaults> </action> diff --git a/src/Makefile.am b/src/Makefile.am index b23e7de..962cf12 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,6 +67,8 @@ libudisks_daemon_la_SOURCES = \ udisksprivate.h \ udisksfstabentry.h udisksfstabentry.c \ udisksfstabmonitor.h udisksfstabmonitor.c \ + udiskscrypttabentry.h udiskscrypttabentry.c \ + udiskscrypttabmonitor.h udiskscrypttabmonitor.c \ $(BUILT_SOURCES) \ $(NULL) diff --git a/src/udiskscrypttabentry.c b/src/udiskscrypttabentry.c new file mode 100644 index 0000000..71181cc --- /dev/null +++ b/src/udiskscrypttabentry.c @@ -0,0 +1,214 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen <zeuthen@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include <glib/gi18n-lib.h> + +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <mntent.h> + +#include <glib.h> +#include <glib-object.h> + +#include "udiskscrypttabentry.h" +#include "udisksprivate.h" + +/** + * SECTION:udiskscrypttabentry + * @title: UDisksCrypttabEntry + * @short_description: Object corresponding to an entry in the crypttab file + * + * Object corresponding to an entry in the + * <filename>/etc/crypttab</filename> file. You cannot instantiate this + * type yourself – use #UDisksCrypttabMonitor. + */ + +/** + * UDisksCrypttabEntry: + * + * The #UDisksCrypttabEntry structure contains only private data and should + * only be accessed using the provided API. + */ +struct _UDisksCrypttabEntry +{ + GObject parent_instance; + + gchar *name; + gchar *device; + gchar *passphrase_path; + gchar *options; +}; + +typedef struct _UDisksCrypttabEntryClass UDisksCrypttabEntryClass; + +struct _UDisksCrypttabEntryClass +{ + GObjectClass parent_class; +}; + +G_DEFINE_TYPE (UDisksCrypttabEntry, udisks_crypttab_entry, G_TYPE_OBJECT); + +static void +udisks_crypttab_entry_finalize (GObject *object) +{ + UDisksCrypttabEntry *entry = UDISKS_CRYPTTAB_ENTRY (object); + + g_free (entry->name); + g_free (entry->device); + g_free (entry->passphrase_path); + g_free (entry->options); + + if (G_OBJECT_CLASS (udisks_crypttab_entry_parent_class)->finalize) + G_OBJECT_CLASS (udisks_crypttab_entry_parent_class)->finalize (object); +} + +static void +udisks_crypttab_entry_init (UDisksCrypttabEntry *crypttab_entry) +{ +} + +static void +udisks_crypttab_entry_class_init (UDisksCrypttabEntryClass *klass) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = udisks_crypttab_entry_finalize; +} + +UDisksCrypttabEntry * +_udisks_crypttab_entry_new (const gchar *name, + const gchar *device, + const gchar *passphrase_path, + const gchar *options) +{ + UDisksCrypttabEntry *entry; + + entry = UDISKS_CRYPTTAB_ENTRY (g_object_new (UDISKS_TYPE_CRYPTTAB_ENTRY, NULL)); + entry->name = g_strdup (name); + entry->device = g_strdup (device); + entry->passphrase_path = g_strdup (passphrase_path); + entry->options = g_strdup (options); + + return entry; +} + +/** + * udisks_crypttab_entry_compare: + * @entry: A #UDisksCrypttabEntry + * @other_entry: Another #UDisksCrypttabEntry. + * + * Comparison function for comparing two #UDisksCrypttabEntry objects. + * + * Returns: Negative value if @entry < @other_entry; zero if @entry = @other_entry; positive value if @entry > @other_entry. + */ +gint +udisks_crypttab_entry_compare (UDisksCrypttabEntry *entry, + UDisksCrypttabEntry *other_entry) +{ + gint ret; + + g_return_val_if_fail (UDISKS_IS_CRYPTTAB_ENTRY (entry), 0); + g_return_val_if_fail (UDISKS_IS_CRYPTTAB_ENTRY (other_entry), 0); + + ret = g_strcmp0 (other_entry->name, entry->name); + if (ret != 0) + goto out; + + ret = g_strcmp0 (other_entry->device, entry->device); + if (ret != 0) + goto out; + + ret = g_strcmp0 (other_entry->passphrase_path, entry->passphrase_path); + if (ret != 0) + goto out; + + ret = g_strcmp0 (other_entry->options, entry->options); + + out: + return ret; +} + +/** + * udisks_crypttab_entry_get_name: + * @entry: A #UDisksCrypttabEntry. + * + * Gets the name field of @entry. + * + * Returns: The name field. + */ +const gchar * +udisks_crypttab_entry_get_name (UDisksCrypttabEntry *entry) +{ + g_return_val_if_fail (UDISKS_IS_CRYPTTAB_ENTRY (entry), NULL); + return entry->name; +} + +/** + * udisks_crypttab_entry_get_device: + * @entry: A #UDisksCrypttabEntry. + * + * Gets the device field of @entry. + * + * Returns: The device field. + */ +const gchar * +udisks_crypttab_entry_get_device (UDisksCrypttabEntry *entry) +{ + g_return_val_if_fail (UDISKS_IS_CRYPTTAB_ENTRY (entry), NULL); + return entry->device; +} + +/** + * udisks_crypttab_entry_get_passphrase_path: + * @entry: A #UDisksCrypttabEntry. + * + * Gets the passphrase path field of @entry. + * + * Returns: The passphrase path field. + */ +const gchar * +udisks_crypttab_entry_get_passphrase_path (UDisksCrypttabEntry *entry) +{ + g_return_val_if_fail (UDISKS_IS_CRYPTTAB_ENTRY (entry), NULL); + return entry->passphrase_path; +} + +/** + * udisks_crypttab_entry_get_options: + * @entry: A #UDisksCrypttabEntry. + * + * Gets the options field of @entry. + * + * Returns: The options field. + */ +const gchar * +udisks_crypttab_entry_get_options (UDisksCrypttabEntry *entry) +{ + g_return_val_if_fail (UDISKS_IS_CRYPTTAB_ENTRY (entry), NULL); + return entry->options; +} + diff --git a/src/udiskscrypttabentry.h b/src/udiskscrypttabentry.h new file mode 100644 index 0000000..f1b8734 --- /dev/null +++ b/src/udiskscrypttabentry.h @@ -0,0 +1,42 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008-2010 David Zeuthen <zeuthen@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UDISKS_CRYPTTAB_ENTRY_H__ +#define __UDISKS_CRYPTTAB_ENTRY_H__ + +#include "udisksdaemontypes.h" + +G_BEGIN_DECLS + +#define UDISKS_TYPE_CRYPTTAB_ENTRY (udisks_crypttab_entry_get_type ()) +#define UDISKS_CRYPTTAB_ENTRY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_CRYPTTAB_ENTRY, UDisksCrypttabEntry)) +#define UDISKS_IS_CRYPTTAB_ENTRY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_CRYPTTAB_ENTRY)) + +GType udisks_crypttab_entry_get_type (void) G_GNUC_CONST; +const gchar *udisks_crypttab_entry_get_name (UDisksCrypttabEntry *entry); +const gchar *udisks_crypttab_entry_get_device (UDisksCrypttabEntry *entry); +const gchar *udisks_crypttab_entry_get_passphrase_path (UDisksCrypttabEntry *entry); +const gchar *udisks_crypttab_entry_get_options (UDisksCrypttabEntry *entry); +gint udisks_crypttab_entry_compare (UDisksCrypttabEntry *entry, + UDisksCrypttabEntry *other_entry); + +G_END_DECLS + +#endif /* __UDISKS_CRYPTTAB_ENTRY_H__ */ diff --git a/src/udiskscrypttabmonitor.c b/src/udiskscrypttabmonitor.c new file mode 100644 index 0000000..f3b54ea --- /dev/null +++ b/src/udiskscrypttabmonitor.c @@ -0,0 +1,444 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008 David Zeuthen <zeuthen@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include <glib/gi18n-lib.h> + +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <glib.h> +#include <glib-object.h> + +#include "udiskscrypttabmonitor.h" +#include "udiskscrypttabentry.h" +#include "udisksprivate.h" +#include "udiskslogging.h" + +/** + * SECTION:udiskscrypttabmonitor + * @title: UDisksCrypttabMonitor + * @short_description: Monitors entries in the crypttab file + * + * This type is used for monitoring entries in the + * <filename>/etc/crypttab</filename> file. + */ + +/** + * UDisksCrypttabMonitor: + * + * The #UDisksCrypttabMonitor structure contains only private data and + * should only be accessed using the provided API. + */ +struct _UDisksCrypttabMonitor +{ + GObject parent_instance; + + gboolean have_data; + GList *crypttab_entries; + + GFileMonitor *file_monitor; +}; + +typedef struct _UDisksCrypttabMonitorClass UDisksCrypttabMonitorClass; + +struct _UDisksCrypttabMonitorClass +{ + GObjectClass parent_class; + + void (*entry_added) (UDisksCrypttabMonitor *monitor, + UDisksCrypttabEntry *entry); + void (*entry_removed) (UDisksCrypttabMonitor *monitor, + UDisksCrypttabEntry *entry); +}; + +/*--------------------------------------------------------------------------------------------------------------*/ + +enum + { + ENTRY_ADDED_SIGNAL, + ENTRY_REMOVED_SIGNAL, + LAST_SIGNAL, + }; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (UDisksCrypttabMonitor, udisks_crypttab_monitor, G_TYPE_OBJECT) + +static void udisks_crypttab_monitor_ensure (UDisksCrypttabMonitor *monitor); +static void udisks_crypttab_monitor_invalidate (UDisksCrypttabMonitor *monitor); +static void udisks_crypttab_monitor_constructed (GObject *object); + +static void +udisks_crypttab_monitor_finalize (GObject *object) +{ + UDisksCrypttabMonitor *monitor = UDISKS_CRYPTTAB_MONITOR (object); + + g_object_unref (monitor->file_monitor); + + g_list_foreach (monitor->crypttab_entries, (GFunc) g_object_unref, NULL); + g_list_free (monitor->crypttab_entries); + + if (G_OBJECT_CLASS (udisks_crypttab_monitor_parent_class)->finalize != NULL) + G_OBJECT_CLASS (udisks_crypttab_monitor_parent_class)->finalize (object); +} + +static void +udisks_crypttab_monitor_init (UDisksCrypttabMonitor *monitor) +{ + monitor->crypttab_entries = NULL; +} + +static void +udisks_crypttab_monitor_class_init (UDisksCrypttabMonitorClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = udisks_crypttab_monitor_finalize; + gobject_class->constructed = udisks_crypttab_monitor_constructed; + + /** + * UDisksCrypttabMonitor::entry-added + * @monitor: A #UDisksCrypttabMonitor. + * @entry: The #UDisksCrypttabEntry that was added. + * + * Emitted when a crypttab entry is added. + * + * This signal is emitted in the + * <link linkend="g-main-context-push-thread-default">thread-default main loop</link> + * that @monitor was created in. + */ + signals[ENTRY_ADDED_SIGNAL] = g_signal_new ("entry-added", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (UDisksCrypttabMonitorClass, entry_added), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + UDISKS_TYPE_CRYPTTAB_ENTRY); + + /** + * UDisksCrypttabMonitor::entry-removed + * @monitor: A #UDisksCrypttabMonitor. + * @entry: The #UDisksCrypttabEntry that was removed. + * + * Emitted when a crypttab entry is removed. + * + * This signal is emitted in the + * <link linkend="g-main-context-push-thread-default">thread-default main loop</link> + * that @monitor was created in. + */ + signals[ENTRY_REMOVED_SIGNAL] = g_signal_new ("entry-removed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + G_STRUCT_OFFSET (UDisksCrypttabMonitorClass, entry_removed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + UDISKS_TYPE_CRYPTTAB_ENTRY); +} + +static void +diff_sorted_lists (GList *list1, + GList *list2, + GCompareFunc compare, + GList **added, + GList **removed) +{ + int order; + + *added = *removed = NULL; + + while (list1 != NULL && list2 != NULL) + { + order = (*compare) (list1->data, list2->data); + if (order < 0) + { + *removed = g_list_prepend (*removed, list1->data); + list1 = list1->next; + } + else if (order > 0) + { + *added = g_list_prepend (*added, list2->data); + list2 = list2->next; + } + else + { /* same item */ + list1 = list1->next; + list2 = list2->next; + } + } + + while (list1 != NULL) + { + *removed = g_list_prepend (*removed, list1->data); + list1 = list1->next; + } + while (list2 != NULL) + { + *added = g_list_prepend (*added, list2->data); + list2 = list2->next; + } +} + +static void +reload_crypttab_entries (UDisksCrypttabMonitor *monitor) +{ + GList *old_crypttab_entries; + GList *cur_crypttab_entries; + GList *added; + GList *removed; + GList *l; + + udisks_crypttab_monitor_ensure (monitor); + + old_crypttab_entries = g_list_copy (monitor->crypttab_entries); + g_list_foreach (old_crypttab_entries, (GFunc) g_object_ref, NULL); + + udisks_crypttab_monitor_invalidate (monitor); + udisks_crypttab_monitor_ensure (monitor); + + cur_crypttab_entries = g_list_copy (monitor->crypttab_entries); + + old_crypttab_entries = g_list_sort (old_crypttab_entries, (GCompareFunc) udisks_crypttab_entry_compare); + cur_crypttab_entries = g_list_sort (cur_crypttab_entries, (GCompareFunc) udisks_crypttab_entry_compare); + diff_sorted_lists (old_crypttab_entries, cur_crypttab_entries, (GCompareFunc) udisks_crypttab_entry_compare, &added, &removed); + + for (l = removed; l != NULL; l = l->next) + { + UDisksCrypttabEntry *entry = UDISKS_CRYPTTAB_ENTRY (l->data); + g_signal_emit (monitor, signals[ENTRY_REMOVED_SIGNAL], 0, entry); + } + + for (l = added; l != NULL; l = l->next) + { + UDisksCrypttabEntry *entry = UDISKS_CRYPTTAB_ENTRY (l->data); + g_signal_emit (monitor, signals[ENTRY_ADDED_SIGNAL], 0, entry); + } + + g_list_foreach (old_crypttab_entries, (GFunc) g_object_unref, NULL); + g_list_free (old_crypttab_entries); + g_list_free (cur_crypttab_entries); + g_list_free (removed); + g_list_free (added); +} + +static void +on_file_monitor_changed (GFileMonitor *file_monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + UDisksCrypttabMonitor *monitor = UDISKS_CRYPTTAB_MONITOR (user_data); + if (event_type == G_FILE_MONITOR_EVENT_CHANGED || + event_type == G_FILE_MONITOR_EVENT_CREATED) + { + udisks_debug ("/etc/crypttab changed!"); + reload_crypttab_entries (monitor); + } +} + +static void +udisks_crypttab_monitor_constructed (GObject *object) +{ + UDisksCrypttabMonitor *monitor = UDISKS_CRYPTTAB_MONITOR (object); + GError *error; + GFile *file; + + file = g_file_new_for_path ("/etc/crypttab"); + error = NULL; + monitor->file_monitor = g_file_monitor_file (file, + G_FILE_MONITOR_NONE, + NULL, /* cancellable */ + &error); + if (monitor->file_monitor == NULL) + { + udisks_error ("Error monitoring /etc/crypttab: %s (%s, %d)", + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + } + else + { + g_signal_connect (monitor->file_monitor, + "changed", + G_CALLBACK (on_file_monitor_changed), + monitor); + } + g_object_unref (file); + + if (G_OBJECT_CLASS (udisks_crypttab_monitor_parent_class)->constructed != NULL) + (*G_OBJECT_CLASS (udisks_crypttab_monitor_parent_class)->constructed) (object); +} + +/** + * udisks_crypttab_monitor_new: + * + * Creates a new #UDisksCrypttabMonitor object. + * + * Signals are emitted in the <link + * linkend="g-main-context-push-thread-default">thread-default main + * loop</link> that this function is called from. + * + * Returns: A #UDisksCrypttabMonitor. Free with g_object_unref(). + */ +UDisksCrypttabMonitor * +udisks_crypttab_monitor_new (void) +{ + return UDISKS_CRYPTTAB_MONITOR (g_object_new (UDISKS_TYPE_CRYPTTAB_MONITOR, NULL)); +} + +static void +udisks_crypttab_monitor_invalidate (UDisksCrypttabMonitor *monitor) +{ + monitor->have_data = FALSE; + + g_list_foreach (monitor->crypttab_entries, (GFunc) g_object_unref, NULL); + g_list_free (monitor->crypttab_entries); + monitor->crypttab_entries = NULL; +} + + +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +have_entry (UDisksCrypttabMonitor *monitor, + UDisksCrypttabEntry *entry) +{ + GList *l; + gboolean ret; + + ret = FALSE; + for (l = monitor->crypttab_entries; l != NULL; l = l->next) + { + UDisksCrypttabEntry *other_entry = UDISKS_CRYPTTAB_ENTRY (l->data); + if (udisks_crypttab_entry_compare (entry, other_entry) == 0) + { + ret = TRUE; + goto out; + } + } + out: + return ret; +} + +static void +udisks_crypttab_monitor_ensure (UDisksCrypttabMonitor *monitor) +{ + gchar *contents; + gchar **lines; + GError *error; + guint n; + + contents = NULL; + lines = NULL; + + if (monitor->have_data) + goto out; + + error = NULL; + if (!g_file_get_contents ("/etc/crypttab", + &contents, + NULL, /* size */ + &error)) + { + udisks_warning ("Error opening /etc/crypttab file: %s (%s, %d)", + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + goto out; + } + + lines = g_strsplit (contents, "\n", 0); + + for (n = 0; lines != NULL && lines[n] != NULL; n++) + { + gchar **tokens; + guint num_tokens; + UDisksCrypttabEntry *entry; + + const gchar *line = lines[n]; + if (strlen (line) == 0) + continue; + if (line[0] == '#') + continue; + + tokens = g_strsplit_set (line, " \t", 0); + num_tokens = g_strv_length (tokens); + if (num_tokens < 2) + { + udisks_warning ("Line %d of /etc/crypttab only contains %d tokens", n, num_tokens); + goto continue_loop; + } + + entry = _udisks_crypttab_entry_new (tokens[0], + tokens[1], + num_tokens >= 3 ? tokens[2] : NULL, + num_tokens >= 4 ? tokens[3] : NULL); + if (!have_entry (monitor, entry)) + { + monitor->crypttab_entries = g_list_prepend (monitor->crypttab_entries, entry); + } + else + { + g_object_unref (entry); + } + + continue_loop: + g_strfreev (tokens); + } + + monitor->have_data = TRUE; + + out: + g_free (contents); + g_strfreev (lines); +} + +/** + * udisks_crypttab_monitor_get_entries: + * @monitor: A #UDisksCrypttabMonitor. + * + * Gets all /etc/crypttab entries + * + * Returns: (transfer full) (element-type UDisksCrypttabEntry): A list of #UDisksCrypttabEntry objects that must be freed with g_list_free() after each element has been freed with g_object_unref(). + */ +GList * +udisks_crypttab_monitor_get_entries (UDisksCrypttabMonitor *monitor) +{ + GList *ret; + + g_return_val_if_fail (UDISKS_IS_CRYPTTAB_MONITOR (monitor), NULL); + + udisks_crypttab_monitor_ensure (monitor); + + ret = g_list_copy (monitor->crypttab_entries); + g_list_foreach (ret, (GFunc) g_object_ref, NULL); + return ret; +} + diff --git a/src/udiskscrypttabmonitor.h b/src/udiskscrypttabmonitor.h new file mode 100644 index 0000000..837bc4c --- /dev/null +++ b/src/udiskscrypttabmonitor.h @@ -0,0 +1,38 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2008-2010 David Zeuthen <zeuthen@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UDISKS_CRYPTTAB_MONITOR_H__ +#define __UDISKS_CRYPTTAB_MONITOR_H__ + +#include "udisksdaemontypes.h" + +G_BEGIN_DECLS + +#define UDISKS_TYPE_CRYPTTAB_MONITOR (udisks_crypttab_monitor_get_type ()) +#define UDISKS_CRYPTTAB_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), UDISKS_TYPE_CRYPTTAB_MONITOR, UDisksCrypttabMonitor)) +#define UDISKS_IS_CRYPTTAB_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), UDISKS_TYPE_CRYPTTAB_MONITOR)) + +GType udisks_crypttab_monitor_get_type (void) G_GNUC_CONST; +UDisksCrypttabMonitor *udisks_crypttab_monitor_new (void); +GList *udisks_crypttab_monitor_get_entries (UDisksCrypttabMonitor *monitor); + +G_END_DECLS + +#endif /* __UDISKS_CRYPTTAB_MONITOR_H__ */ diff --git a/src/udisksdaemon.c b/src/udisksdaemon.c index 3b72b26..4532746 100644 --- a/src/udisksdaemon.c +++ b/src/udisksdaemon.c @@ -35,6 +35,8 @@ #include "udiskscleanup.h" #include "udisksfstabmonitor.h" #include "udisksfstabentry.h" +#include "udiskscrypttabmonitor.h" +#include "udiskscrypttabentry.h" /** * SECTION:udisksdaemon @@ -69,6 +71,8 @@ struct _UDisksDaemon UDisksCleanup *cleanup; UDisksFstabMonitor *fstab_monitor; + + UDisksCrypttabMonitor *crypttab_monitor; }; struct _UDisksDaemonClass @@ -83,6 +87,7 @@ enum PROP_OBJECT_MANAGER, PROP_MOUNT_MONITOR, PROP_FSTAB_MONITOR, + PROP_CRYPTTAB_MONITOR, }; G_DEFINE_TYPE (UDisksDaemon, udisks_daemon, G_TYPE_OBJECT); @@ -102,6 +107,7 @@ udisks_daemon_finalize (GObject *object) g_object_unref (daemon->mount_monitor); g_object_unref (daemon->connection); g_object_unref (daemon->fstab_monitor); + g_object_unref (daemon->crypttab_monitor); if (G_OBJECT_CLASS (udisks_daemon_parent_class)->finalize != NULL) G_OBJECT_CLASS (udisks_daemon_parent_class)->finalize (object); @@ -133,6 +139,10 @@ udisks_daemon_get_property (GObject *object, g_value_set_object (value, udisks_daemon_get_fstab_monitor (daemon)); break; + case PROP_CRYPTTAB_MONITOR: + g_value_set_object (value, udisks_daemon_get_crypttab_monitor (daemon)); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -218,6 +228,7 @@ udisks_daemon_constructed (GObject *object) daemon); daemon->fstab_monitor = udisks_fstab_monitor_new (); + daemon->crypttab_monitor = udisks_crypttab_monitor_new (); /* now add providers */ daemon->linux_provider = udisks_linux_provider_new (daemon); @@ -371,6 +382,21 @@ udisks_daemon_get_fstab_monitor (UDisksDaemon *daemon) } /** + * udisks_daemon_get_crypttab_monitor: + * @daemon: A #UDisksDaemon + * + * Gets the crypttab monitor used by @daemon. + * + * Returns: A #UDisksCrypttabMonitor. Do not free, the object is owned by @daemon. + */ +UDisksCrypttabMonitor * +udisks_daemon_get_crypttab_monitor (UDisksDaemon *daemon) +{ + g_return_val_if_fail (UDISKS_IS_DAEMON (daemon), NULL); + return daemon->crypttab_monitor; +} + +/** * udisks_daemon_get_linux_provider: * @daemon: A #UDisksDaemon. * diff --git a/src/udisksdaemon.h b/src/udisksdaemon.h index 684e673..56bbdaf 100644 --- a/src/udisksdaemon.h +++ b/src/udisksdaemon.h @@ -35,6 +35,7 @@ GDBusConnection *udisks_daemon_get_connection (UDisksDaemon * GDBusObjectManagerServer *udisks_daemon_get_object_manager (UDisksDaemon *daemon); UDisksMountMonitor *udisks_daemon_get_mount_monitor (UDisksDaemon *daemon); UDisksFstabMonitor *udisks_daemon_get_fstab_monitor (UDisksDaemon *daemon); +UDisksCrypttabMonitor *udisks_daemon_get_crypttab_monitor (UDisksDaemon *daemon); UDisksLinuxProvider *udisks_daemon_get_linux_provider (UDisksDaemon *daemon); UDisksPersistentStore *udisks_daemon_get_persistent_store (UDisksDaemon *daemon); PolkitAuthority *udisks_daemon_get_authority (UDisksDaemon *daemon); diff --git a/src/udisksdaemontypes.h b/src/udisksdaemontypes.h index fe4b1ba..edf97c2 100644 --- a/src/udisksdaemontypes.h +++ b/src/udisksdaemontypes.h @@ -82,6 +82,12 @@ typedef struct _UDisksFstabMonitor UDisksFstabMonitor; struct _UDisksFstabEntry; typedef struct _UDisksFstabEntry UDisksFstabEntry; +struct _UDisksCrypttabMonitor; +typedef struct _UDisksCrypttabMonitor UDisksCrypttabMonitor; + +struct _UDisksCrypttabEntry; +typedef struct _UDisksCrypttabEntry UDisksCrypttabEntry; + /** * UDisksThreadedJobFunc: * @job: A #UDisksThreadedJob. diff --git a/src/udiskslinuxblock.c b/src/udiskslinuxblock.c index 027c940..d8cfe23 100644 --- a/src/udiskslinuxblock.c +++ b/src/udiskslinuxblock.c @@ -45,6 +45,8 @@ #include "udiskslinuxprovider.h" #include "udisksfstabmonitor.h" #include "udisksfstabentry.h" +#include "udiskscrypttabmonitor.h" +#include "udiskscrypttabentry.h" /** * SECTION:udiskslinuxblock @@ -371,6 +373,48 @@ update_iface (UDisksLinuxBlock *block, /* ---------------------------------------------------------------------------------------------------- */ +static GVariant *calculate_configuration (UDisksLinuxBlock *block, + gboolean include_secrets, + GError **error); + +static gboolean +on_get_secret_configuration (UDisksBlockDevice *block, + GDBusMethodInvocation *invocation, + GVariant *options, + gpointer user_data) +{ + UDisksLinuxBlock *object = UDISKS_LINUX_BLOCK (user_data); + GVariant *configuration; + GError *error; + + error = NULL; + configuration = calculate_configuration (object, TRUE, &error); + if (configuration == NULL) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + + if (!udisks_daemon_util_check_authorization_sync (object->daemon, + NULL, + "org.freedesktop.udisks2.read-system-configuration-secrets", + options, + N_("Authentication is required to read system-level secrets"), + invocation)) + { + g_variant_unref (configuration); + goto out; + } + + udisks_block_device_complete_get_secret_configuration (object->iface_block_device, invocation, + configuration); /* consumes floating ref */ + + out: + return TRUE; /* returning TRUE means that we handled the method invocation */ +} + +/* ---------------------------------------------------------------------------------------------------- */ + static gchar * escape_fstab (const gchar *source) { @@ -442,8 +486,8 @@ out: } static gboolean -add_remove_fstab_entry (GVariant *add, - GVariant *remove, +add_remove_fstab_entry (GVariant *remove, + GVariant *add, GError **error) { struct mntent mntent_remove; @@ -598,6 +642,252 @@ add_remove_fstab_entry (GVariant *add, return ret; } +/* ---------------------------------------------------------------------------------------------------- */ + +static gboolean +has_whitespace (const gchar *s) +{ + guint n; + g_return_val_if_fail (s != NULL, TRUE); + for (n = 0; s[n] != '\0'; n++) + if (g_ascii_isspace (s[n])) + return TRUE; + return FALSE; +} + +static gboolean +add_remove_crypttab_entry (GVariant *remove, + GVariant *add, + GError **error) +{ + const gchar *remove_name = NULL; + const gchar *remove_device = NULL; + const gchar *remove_passphrase_path = NULL; + const gchar *remove_options = NULL; + const gchar *add_name = NULL; + const gchar *add_device = NULL; + const gchar *add_passphrase_path = NULL; + const gchar *add_options = NULL; + const gchar *add_passphrase_contents = NULL; + gboolean ret; + gchar *contents; + gchar **lines; + GString *str; + gboolean removed; + guint n; + + contents = NULL; + lines = NULL; + str = NULL; + ret = FALSE; + + if (remove != NULL) + { + if (!g_variant_lookup (remove, "name", "^&ay", &remove_name) || + !g_variant_lookup (remove, "device", "^&ay", &remove_device) || + !g_variant_lookup (remove, "passphrase-path", "^&ay", &remove_passphrase_path) || + !g_variant_lookup (remove, "options", "^&ay", &remove_options)) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Missing name, device, passphrase-path, options or parameter in entry to remove"); + goto out; + } + } + + if (add != NULL) + { + if (!g_variant_lookup (add, "name", "^&ay", &add_name) || + !g_variant_lookup (add, "device", "^&ay", &add_device) || + !g_variant_lookup (add, "passphrase-path", "^&ay", &add_passphrase_path) || + !g_variant_lookup (add, "options", "^&ay", &add_options) || + !g_variant_lookup (add, "passphrase-contents", "^&ay", &add_passphrase_contents)) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Missing name, device, passphrase-path, options or passphrase-contents parameter in entry to add"); + goto out; + } + + /* reject strings with whitespace in them */ + if (has_whitespace (add_name) || + has_whitespace (add_device) || + has_whitespace (add_passphrase_path) || + has_whitespace (add_options)) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "One of name, device, passphrase-path or options parameter are invalid (whitespace)"); + goto out; + } + } + + if (!g_file_get_contents ("/etc/crypttab", + &contents, + NULL, + error)) + goto out; + + lines = g_strsplit (contents, "\n", 0); + + str = g_string_new (NULL); + removed = FALSE; + for (n = 0; lines != NULL && lines[n] != NULL; n++) + { + const gchar *line = lines[n]; + if (strlen (line) == 0 && lines[n+1] == NULL) + break; + if (remove != NULL && !removed) + { + gchar parsed_name[512]; + gchar parsed_device[512]; + gchar parsed_passphrase_path[512]; + gchar parsed_options[512]; + guint num_parsed; + + num_parsed = sscanf (line, "%511s %511s %511s %511s", + parsed_name, parsed_device, parsed_passphrase_path, parsed_options); + if (num_parsed >= 2) + { + if (num_parsed < 3 || g_strcmp0 (parsed_passphrase_path, "none") == 0) + strcpy (parsed_passphrase_path, ""); + if (num_parsed < 4) + strcpy (parsed_options, ""); + if (g_strcmp0 (parsed_name, remove_name) == 0 && + g_strcmp0 (parsed_device, remove_device) == 0 && + g_strcmp0 (parsed_passphrase_path, remove_passphrase_path) == 0 && + g_strcmp0 (parsed_options, remove_options) == 0) + { + /* Nuke passphrase file */ + if (strlen (remove_passphrase_path) > 0 && !g_str_has_prefix (remove_passphrase_path, "/dev")) + { + /* Is this exploitable? No, 1. the user would have to control + * the /etc/crypttab file for us to delete it; and 2. editing the + * /etc/crypttab file requires a polkit authorization that can't + * be retained (e.g. the user is always asked for the password).. + */ + if (unlink (remove_passphrase_path) != 0) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error deleting file `%s' with passphrase", + remove_passphrase_path); + goto out; + } + } + removed = TRUE; + continue; + } + } + } + g_string_append (str, line); + g_string_append_c (str, '\n'); + } + + if (remove != NULL && !removed) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Didn't find entry to remove"); + goto out; + } + + if (add != NULL) + { + /* First write add_passphrase_content to add_passphrase_path, + * if applicable.. + * + * Is this exploitable? No, because editing the /etc/crypttab + * file requires a polkit authorization that can't be retained + * (e.g. the user is always asked for the password)... + * + * Just to be on the safe side we only allow writing into the + * directory /etc/luks-keys if create a _new_ entry. + */ + if (strlen (add_passphrase_path) > 0) + { + gchar *filename; + if (g_strcmp0 (add_passphrase_path, remove_passphrase_path) == 0) + { + filename = g_strdup (add_passphrase_path); + } + else + { + if (!g_str_has_prefix (add_passphrase_path, "/etc/luks-keys/")) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Crypttab passphrase file can only be created in the /etc/luks-keys directory"); + goto out; + } + /* ensure the directory exists */ + if (g_mkdir_with_parents ("/etc/luks-keys", 0700) != 0) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Error creating /etc/luks-keys directory: %m"); + goto out; + } + /* avoid symlink attacks */ + filename = g_strdup_printf ("/etc/luks-keys/%s", strrchr (add_passphrase_path, '/') + 1); + } + + /* Bail if the requested file already exists */ + if (g_file_test (filename, G_FILE_TEST_EXISTS)) + { + g_set_error (error, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Refusing to overwrite existing file %s", + filename); + g_free (filename); + goto out; + } + + /* TODO: XXX: would like to use mode 0600 here - umask(3) at start-up? */ + if (!g_file_set_contents (filename, + add_passphrase_contents, + -1, + error)) + { + g_free (filename); + goto out; + } + g_free (filename); + } + g_string_append_printf (str, "%s %s %s %s\n", + add_name, + add_device, + strlen (add_passphrase_path) > 0 ? add_passphrase_path : "none", + add_options); + } + + /* TODO: XXX: ugh, the mode is wrong.. umask(3) at start-up? */ + if (!g_file_set_contents ("/etc/crypttab", + str->str, + -1, + error) != 0) + goto out; + + ret = TRUE; + + out: + g_strfreev (lines); + g_free (contents); + if (str != NULL) + g_string_free (str, TRUE); + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + static gboolean on_add_configuration_item (UDisksBlockDevice *block, GDBusMethodInvocation *invocation, @@ -612,32 +902,49 @@ on_add_configuration_item (UDisksBlockDevice *block, g_variant_get (item, "(&s@a{sv})", &type, &details); - if (g_strcmp0 (type, "fstab") != 0) + if (g_strcmp0 (type, "fstab") == 0) + { + if (!udisks_daemon_util_check_authorization_sync (object->daemon, + NULL, + "org.freedesktop.udisks2.modify-system-configuration", + options, + N_("Authentication is required to add an entry to the /etc/fstab file"), + invocation)) + goto out; + error = NULL; + if (!add_remove_fstab_entry (NULL, details, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + udisks_block_device_complete_add_configuration_item (block, invocation); + } + else if (g_strcmp0 (type, "crypttab") == 0) + { + if (!udisks_daemon_util_check_authorization_sync (object->daemon, + NULL, + "org.freedesktop.udisks2.modify-system-configuration", + options, + N_("Authentication is required to add an entry to the /etc/crypttab file"), + invocation)) + goto out; + error = NULL; + if (!add_remove_crypttab_entry (NULL, details, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + udisks_block_device_complete_add_configuration_item (block, invocation); + } + else { g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, - "Only fstab items can be added"); + "Only /etc/fstab or /etc/crypttab items can be added"); goto out; } - if (!udisks_daemon_util_check_authorization_sync (object->daemon, - NULL, - "org.freedesktop.udisks2.modify-system-configuration", - options, - N_("Authentication is required to modify the /etc/fstab file"), - invocation)) - goto out; - - error = NULL; - if (!add_remove_fstab_entry (details, NULL, &error)) - { - g_dbus_method_invocation_take_error (invocation, error); - goto out; - } - - udisks_block_device_complete_add_configuration_item (block, invocation); - out: g_variant_unref (details); return TRUE; /* returning TRUE means that we handled the method invocation */ @@ -657,34 +964,126 @@ on_remove_configuration_item (UDisksBlockDevice *block, g_variant_get (item, "(&s@a{sv})", &type, &details); - if (g_strcmp0 (type, "fstab") != 0) + if (g_strcmp0 (type, "fstab") == 0) + { + if (!udisks_daemon_util_check_authorization_sync (object->daemon, + NULL, + "org.freedesktop.udisks2.modify-system-configuration", + options, + N_("Authentication is required to remove an entry from /etc/fstab file"), + invocation)) + goto out; + error = NULL; + if (!add_remove_fstab_entry (details, NULL, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + udisks_block_device_complete_add_configuration_item (block, invocation); + } + else if (g_strcmp0 (type, "crypttab") == 0) + { + if (!udisks_daemon_util_check_authorization_sync (object->daemon, + NULL, + "org.freedesktop.udisks2.modify-system-configuration", + options, + N_("Authentication is required to remove an entry from the /etc/crypttab file"), + invocation)) + goto out; + error = NULL; + if (!add_remove_crypttab_entry (details, NULL, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + udisks_block_device_complete_add_configuration_item (block, invocation); + } + else { g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED, - "Only fstab items can be removed"); + "Only fstab or crypttab items can be removed"); goto out; } - if (!udisks_daemon_util_check_authorization_sync (object->daemon, - NULL, - "org.freedesktop.udisks2.modify-system-configuration", - options, - N_("Authentication is required to modify the /etc/fstab file"), - invocation)) - goto out; + out: + g_variant_unref (details); + return TRUE; /* returning TRUE means that we handled the method invocation */ +} - error = NULL; - if (!add_remove_fstab_entry (NULL, details, &error)) +static gboolean +on_update_configuration_item (UDisksBlockDevice *block, + GDBusMethodInvocation *invocation, + GVariant *old_item, + GVariant *new_item, + GVariant *options, + gpointer user_data) +{ + UDisksLinuxBlock *object = UDISKS_LINUX_BLOCK (user_data); + const gchar *old_type; + const gchar *new_type; + GVariant *old_details; + GVariant *new_details; + GError *error; + + g_variant_get (old_item, "(&s@a{sv})", &old_type, &old_details); + g_variant_get (new_item, "(&s@a{sv})", &new_type, &new_details); + if (g_strcmp0 (old_type, new_type) != 0) { - g_dbus_method_invocation_take_error (invocation, error); + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "old and new item are not of the same type"); goto out; } - udisks_block_device_complete_add_configuration_item (block, invocation); + if (g_strcmp0 (old_type, "fstab") == 0) + { + if (!udisks_daemon_util_check_authorization_sync (object->daemon, + NULL, + "org.freedesktop.udisks2.modify-system-configuration", + options, + N_("Authentication is required to modify the /etc/fstab file"), + invocation)) + goto out; + error = NULL; + if (!add_remove_fstab_entry (old_details, new_details, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + udisks_block_device_complete_add_configuration_item (block, invocation); + } + else if (g_strcmp0 (old_type, "crypttab") == 0) + { + if (!udisks_daemon_util_check_authorization_sync (object->daemon, + NULL, + "org.freedesktop.udisks2.modify-system-configuration", + options, + N_("Authentication is required to modify the /etc/crypttab file"), + invocation)) + goto out; + error = NULL; + if (!add_remove_crypttab_entry (old_details, new_details, &error)) + { + g_dbus_method_invocation_take_error (invocation, error); + goto out; + } + udisks_block_device_complete_add_configuration_item (block, invocation); + } + else + { + g_dbus_method_invocation_return_error (invocation, + UDISKS_ERROR, + UDISKS_ERROR_FAILED, + "Only fstab or crypttab items can be updated"); + goto out; + } out: - g_variant_unref (details); + g_variant_unref (new_details); + g_variant_unref (old_details); return TRUE; /* returning TRUE means that we handled the method invocation */ } @@ -701,6 +1100,10 @@ static void block_device_connect (UDisksLinuxBlock *block) { g_signal_connect (block->iface_block_device, + "handle-get-secret-configuration", + G_CALLBACK (on_get_secret_configuration), + block); + g_signal_connect (block->iface_block_device, "handle-add-configuration-item", G_CALLBACK (on_add_configuration_item), block); @@ -708,6 +1111,10 @@ block_device_connect (UDisksLinuxBlock *block) "handle-remove-configuration-item", G_CALLBACK (on_remove_configuration_item), block); + g_signal_connect (block->iface_block_device, + "handle-update-configuration-item", + G_CALLBACK (on_update_configuration_item), + block); } static gchar * @@ -943,24 +1350,88 @@ find_fstab_entries_for_device (UDisksLinuxBlock *block) return ret; } -static void -block_device_update_configuration (UDisksLinuxBlock *block, - const gchar *uevent_action, - UDisksBlockDevice *iface, - const gchar *device_file, - UDisksDrive *drive) +static GList * +find_crypttab_entries_for_device (UDisksLinuxBlock *block) +{ + GList *entries; + GList *l; + GList *ret; + + ret = NULL; + + /* if this is too slow, we could add lookup methods to UDisksCrypttabMonitor... */ + entries = udisks_crypttab_monitor_get_entries (udisks_daemon_get_crypttab_monitor (block->daemon)); + for (l = entries; l != NULL; l = l->next) + { + UDisksCrypttabEntry *entry = UDISKS_CRYPTTAB_ENTRY (l->data); + const gchar *const *symlinks; + const gchar *device_in_entry; + gchar *device; + guint n; + + device_in_entry = udisks_crypttab_entry_get_device (entry); + device = NULL; + if (g_str_has_prefix (device_in_entry, "UUID=")) + { + device = g_strdup_printf ("/dev/disk/by-uuid/%s", device_in_entry + 5); + } + else if (g_str_has_prefix (device_in_entry, "LABEL=")) + { + device = g_strdup_printf ("/dev/disk/by-label/%s", device_in_entry + 6); + } + else if (g_str_has_prefix (device_in_entry, "/dev")) + { + device = g_strdup (device_in_entry); + } + else + { + /* ignore non-device entries */ + goto continue_loop; + } + + symlinks = udisks_block_device_get_symlinks (block->iface_block_device); + if (symlinks != NULL) + { + for (n = 0; symlinks[n] != NULL; n++) + { + if (g_strcmp0 (device, symlinks[n]) == 0) + { + ret = g_list_prepend (ret, g_object_ref (entry)); + } + } + } + + continue_loop: + g_free (device); + } + + g_list_foreach (entries, (GFunc) g_object_unref, NULL); + g_list_free (entries); + return ret; +} + +/* returns a floating GVariant */ +static GVariant * +calculate_configuration (UDisksLinuxBlock *block, + gboolean include_secrets, + GError **error) { GList *entries; GList *l; GVariantBuilder builder; + GVariant *ret; + + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + ret = NULL; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(sa{sv})")); + /* First the /etc/fstab entries */ entries = find_fstab_entries_for_device (block); for (l = entries; l != NULL; l = l->next) { UDisksFstabEntry *entry = UDISKS_FSTAB_ENTRY (l->data); GVariantBuilder dict_builder; - g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&dict_builder, "{sv}", "fsname", g_variant_new_bytestring (udisks_fstab_entry_get_fsname (entry))); @@ -978,12 +1449,100 @@ block_device_update_configuration (UDisksLinuxBlock *block, "(sa{sv})", "fstab", &dict_builder); } + g_list_foreach (entries, (GFunc) g_object_unref, NULL); + g_list_free (entries); - udisks_block_device_set_configuration (block->iface_block_device, - g_variant_builder_end (&builder)); + /* Then the /etc/crypttab entries */ + entries = find_crypttab_entries_for_device (block); + for (l = entries; l != NULL; l = l->next) + { + UDisksCrypttabEntry *entry = UDISKS_CRYPTTAB_ENTRY (l->data); + GVariantBuilder dict_builder; + const gchar *passphrase_path; + const gchar *options; + gchar *passphrase_contents; + gsize passphrase_contents_length; + + passphrase_path = udisks_crypttab_entry_get_passphrase_path (entry); + if (passphrase_path == NULL || g_strcmp0 (passphrase_path, "none") == 0) + passphrase_path = ""; + passphrase_contents = NULL; + if (!(g_strcmp0 (passphrase_path, "") == 0 || g_str_has_prefix (passphrase_path, "/dev"))) + { + if (include_secrets) + { + if (!g_file_get_contents (passphrase_path, + &passphrase_contents, + &passphrase_contents_length, + error)) + { + g_prefix_error (error, + "Error loading secrets from file `%s' referenced in /etc/crypttab entry: ", + passphrase_path); + g_variant_builder_clear (&builder); + g_list_foreach (entries, (GFunc) g_object_unref, NULL); + g_list_free (entries); + goto out; + } + } + } + options = udisks_crypttab_entry_get_options (entry); + if (options == NULL) + options = ""; + + g_variant_builder_init (&dict_builder, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&dict_builder, "{sv}", "name", + g_variant_new_bytestring (udisks_crypttab_entry_get_name (entry))); + g_variant_builder_add (&dict_builder, "{sv}", "device", + g_variant_new_bytestring (udisks_crypttab_entry_get_device (entry))); + g_variant_builder_add (&dict_builder, "{sv}", "passphrase-path", + g_variant_new_bytestring (passphrase_path)); + if (passphrase_contents != NULL) + { + g_variant_builder_add (&dict_builder, "{sv}", "passphrase-contents", + g_variant_new_bytestring (passphrase_contents)); + } + g_variant_builder_add (&dict_builder, "{sv}", "options", + g_variant_new_bytestring (options)); + g_variant_builder_add (&builder, + "(sa{sv})", + "crypttab", &dict_builder); + if (passphrase_contents != NULL) + { + memset (passphrase_contents, '\0', passphrase_contents_length); + g_free (passphrase_contents); + } + } g_list_foreach (entries, (GFunc) g_object_unref, NULL); g_list_free (entries); + + ret = g_variant_builder_end (&builder); + + out: + return ret; +} + +static void +block_device_update_configuration (UDisksLinuxBlock *block, + const gchar *uevent_action, + UDisksBlockDevice *iface, + const gchar *device_file, + UDisksDrive *drive) +{ + GVariant *configuration; + GError *error; + + error = NULL; + configuration = calculate_configuration (block, FALSE, &error); + if (configuration == NULL) + { + udisks_warning ("Error loading configuration: %s (%s, %d)", + error->message, g_quark_to_string (error->domain), error->code); + g_error_free (error); + configuration = g_variant_new ("a(sa{sv})", NULL); + } + udisks_block_device_set_configuration (block->iface_block_device, configuration); } static void diff --git a/src/udiskslinuxprovider.c b/src/udiskslinuxprovider.c index 3a4f5be..60da384 100644 --- a/src/udiskslinuxprovider.c +++ b/src/udiskslinuxprovider.c @@ -93,6 +93,14 @@ static void fstab_monitor_on_entry_removed (UDisksFstabMonitor *monitor, UDisksFstabEntry *entry, gpointer user_data); +static void crypttab_monitor_on_entry_added (UDisksCrypttabMonitor *monitor, + UDisksCrypttabEntry *entry, + gpointer user_data); + +static void crypttab_monitor_on_entry_removed (UDisksCrypttabMonitor *monitor, + UDisksCrypttabEntry *entry, + gpointer user_data); + G_DEFINE_TYPE (UDisksLinuxProvider, udisks_linux_provider, UDISKS_TYPE_PROVIDER); static void @@ -120,6 +128,12 @@ udisks_linux_provider_finalize (GObject *object) g_signal_handlers_disconnect_by_func (udisks_daemon_get_fstab_monitor (daemon), G_CALLBACK (fstab_monitor_on_entry_removed), provider); + g_signal_handlers_disconnect_by_func (udisks_daemon_get_crypttab_monitor (daemon), + G_CALLBACK (crypttab_monitor_on_entry_added), + provider); + g_signal_handlers_disconnect_by_func (udisks_daemon_get_crypttab_monitor (daemon), + G_CALLBACK (crypttab_monitor_on_entry_removed), + provider); if (G_OBJECT_CLASS (udisks_linux_provider_parent_class)->finalize != NULL) G_OBJECT_CLASS (udisks_linux_provider_parent_class)->finalize (object); @@ -201,7 +215,7 @@ udisks_linux_provider_start (UDisksProvider *_provider) provider->coldplug = FALSE; - /* update BlockDevice:FstabEntries whenever fstab entries are added or removed */ + /* update BlockDevice:Configuration whenever fstab or crypttab entries are added or removed */ g_signal_connect (udisks_daemon_get_fstab_monitor (daemon), "entry-added", G_CALLBACK (fstab_monitor_on_entry_added), @@ -210,6 +224,14 @@ udisks_linux_provider_start (UDisksProvider *_provider) "entry-removed", G_CALLBACK (fstab_monitor_on_entry_removed), provider); + g_signal_connect (udisks_daemon_get_crypttab_monitor (daemon), + "entry-added", + G_CALLBACK (crypttab_monitor_on_entry_added), + provider); + g_signal_connect (udisks_daemon_get_crypttab_monitor (daemon), + "entry-removed", + G_CALLBACK (crypttab_monitor_on_entry_removed), + provider); } @@ -586,3 +608,21 @@ fstab_monitor_on_entry_removed (UDisksFstabMonitor *monitor, UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); update_all_block_devices (provider); } + +static void +crypttab_monitor_on_entry_added (UDisksCrypttabMonitor *monitor, + UDisksCrypttabEntry *entry, + gpointer user_data) +{ + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); + update_all_block_devices (provider); +} + +static void +crypttab_monitor_on_entry_removed (UDisksCrypttabMonitor *monitor, + UDisksCrypttabEntry *entry, + gpointer user_data) +{ + UDisksLinuxProvider *provider = UDISKS_LINUX_PROVIDER (user_data); + update_all_block_devices (provider); +} diff --git a/src/udisksprivate.h b/src/udisksprivate.h index 3cc2bf8..5dfd77e 100644 --- a/src/udisksprivate.h +++ b/src/udisksprivate.h @@ -22,6 +22,7 @@ #define __UDISKS_PRIVATE_H__ #include "udisksdaemontypes.h" +#include <mntent.h> G_BEGIN_DECLS @@ -31,6 +32,11 @@ UDisksMount *_udisks_mount_new (dev_t dev, UDisksFstabEntry *_udisks_fstab_entry_new (const struct mntent *mntent); +UDisksCrypttabEntry *_udisks_crypttab_entry_new (const gchar *name, + const gchar *device, + const gchar *passphrase, + const gchar *options); + G_END_DECLS #endif /* __UDISKS_PRIVATE_H__ */ |