summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.h.in3
-rw-r--r--data/fprintd.service.in3
-rw-r--r--data/meson.build7
-rw-r--r--meson.build1
-rw-r--r--meson_options.txt4
-rw-r--r--src/main.c118
6 files changed, 135 insertions, 1 deletions
diff --git a/config.h.in b/config.h.in
index d45da15..c687d8a 100644
--- a/config.h.in
+++ b/config.h.in
@@ -12,3 +12,6 @@
/* Whether current polkit version supports autopointers */
#mesondefine POLKIT_HAS_AUTOPOINTERS
+
+/* Whether to enable the libfprint side-channel cache attack workarounds. */
+#mesondefine CONFIG_LIBFPRINT_PRIVATE \ No newline at end of file
diff --git a/data/fprintd.service.in b/data/fprintd.service.in
index ea311e4..9140be1 100644
--- a/data/fprintd.service.in
+++ b/data/fprintd.service.in
@@ -18,7 +18,8 @@ StateDirectoryMode=0700
ProtectHome=true
PrivateTmp=true
-SystemCallFilter=@system-service
+SystemCallArchitectures=native
+@syscall_filter@
# Network
PrivateNetwork=true
diff --git a/data/meson.build b/data/meson.build
index 92bfa2e..086ea7d 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -12,9 +12,16 @@ configure_file(
)
if get_option('systemd')
+ if get_option('libfprint_private')
+ syscall_filter = '# permit seccomp so that fprintd can block memfd_create\nSystemCallFilter=@system-service seccomp'
+ else
+ syscall_filter = 'SystemCallFilter=@system-service\nSystemCallFilter=~memfd_create'
+ endif
+
configure_file(
configuration: configuration_data({
'libexecdir': fprintd_installdir,
+ 'syscall_filter' : syscall_filter,
}),
input: 'fprintd.service.in',
output: 'fprintd.service',
diff --git a/meson.build b/meson.build
index 1d5d9ba..d9b1ea3 100644
--- a/meson.build
+++ b/meson.build
@@ -163,6 +163,7 @@ cdata.set_quoted('PACKAGE_VERSION', meson.project_version())
cdata.set_quoted('VERSION', meson.project_version())
cdata.set_quoted('SYSCONFDIR', sysconfdir)
cdata.set('POLKIT_HAS_AUTOPOINTERS', polkit_gobject_dep.version().version_compare('>= 0.114'))
+cdata.set('CONFIG_LIBFPRINT_PRIVATE', get_option('libfprint_private'))
config_h = configure_file(
input: 'config.h.in',
diff --git a/meson_options.txt b/meson_options.txt
index 5daa9a4..343eb90 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -23,3 +23,7 @@ option('gtk_doc',
type: 'boolean',
value: false,
description: 'Use gtk-doc to build documentation')
+option('libfprint_private',
+ type: 'boolean',
+ value: true,
+ description: 'Create private copy of libfprint code pages at runtime')
diff --git a/src/main.c b/src/main.c
index f2328da..26a4561 100644
--- a/src/main.c
+++ b/src/main.c
@@ -18,6 +18,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#define _GNU_SOURCE
+
#include "config.h"
#include <locale.h>
@@ -36,6 +38,17 @@
#include "storage.h"
#include "file_storage.h"
+#ifdef CONFIG_LIBFPRINT_PRIVATE
+#include <fcntl.h>
+#include <link.h>
+#include <sys/mman.h>
+
+#include <linux/seccomp.h>
+#include <linux/filter.h>
+#include <sys/syscall.h>
+#include <sys/prctl.h>
+#endif
+
fp_storage store;
static gboolean no_timeout = FALSE;
@@ -146,6 +159,106 @@ on_name_lost (GDBusConnection *connection,
g_main_loop_quit (loop);
}
+#ifdef CONFIG_LIBFPRINT_PRIVATE
+static void
+create_private_executable_copy (void *addr, size_t length)
+{
+ void *mapping = NULL;
+ int memfd;
+
+ /* memfd is more robust (and we can lock it down later). */
+ memfd = memfd_create ("libfprint-copy", MFD_ALLOW_SEALING);
+ if (memfd < 0)
+ g_error ("Could not create memfd for libfprint code");
+
+ if (ftruncate (memfd, length) < 0)
+ g_error ("Could not set length of memfd");
+
+ mapping = mmap (NULL, length, PROT_WRITE, MAP_SHARED, memfd, 0);
+ if (mapping == MAP_FAILED)
+ g_error ("Failed to mmap memfd to copy data");
+
+ memcpy (mapping, addr, length);
+ if (munmap (mapping, length) < 0)
+ g_error ("Error unmapping area for copying libfprint code into");
+
+ if (fcntl (memfd, F_ADD_SEALS, F_SEAL_SEAL | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) < 0)
+ g_error ("Failed to seal memfd against modifications");
+
+ mapping = mmap (addr, length, PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_FIXED, memfd, 0);
+ if (mapping != addr)
+ g_error ("Failed to mmap memfd as executable memory");
+
+ close (memfd);
+}
+
+static int
+dl_iterate_cb (struct dl_phdr_info *info, size_t size, void *data)
+{
+ void *addr;
+
+ if (strstr (info->dlpi_name, "libfprint") == NULL)
+ return 0;
+
+ for (int j = 0; j < info->dlpi_phnum; j++)
+ {
+ if (info->dlpi_phdr[j].p_type != PT_LOAD)
+ continue;
+
+ if ((info->dlpi_phdr[j].p_flags & 0x1) != 0x1)
+ continue;
+
+ if (info->dlpi_phdr[j].p_flags != 0x5)
+ g_error ("Found executable mapping that is not RX only.");
+
+ addr = (void *) (info->dlpi_addr + info->dlpi_phdr[j].p_vaddr);
+ create_private_executable_copy (addr, info->dlpi_phdr[j].p_memsz);
+
+ *(size_t *) data += info->dlpi_phdr[j].p_memsz;
+ }
+
+ return 1;
+}
+
+static void
+protect_libfprint (void)
+{
+ size_t protected_bytes = 0x0;
+
+ dl_iterate_phdr (dl_iterate_cb, &protected_bytes);
+
+ if (protected_bytes == 0)
+ g_error ("The libfprint executable memory was not protected againts side-channel attacks");
+}
+
+static void
+disable_memfd (void)
+{
+ static struct sock_filter filter[] = {
+ /* [1] Load syscall number */
+ BPF_STMT (BPF_LD | BPF_W | BPF_ABS,
+ (offsetof (struct seccomp_data, nr))),
+
+ /* [2] Test whether it is */
+ BPF_JUMP (BPF_JMP | BPF_JEQ | BPF_K, SYS_memfd_create, 0, 1),
+
+ /* [3] Kill process */
+ BPF_STMT (BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),
+
+ /* [4] Allow everything other than SYS_memfd_create */
+ BPF_STMT (BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
+ };
+ static struct sock_fprog prog = {
+ .len = G_N_ELEMENTS (filter),
+ .filter = filter,
+ };
+
+ prctl (PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ if (syscall (SYS_seccomp, SECCOMP_SET_MODE_FILTER, 0, &prog))
+ g_error ("Could not install seccomp filter to prohibit memfd_create");
+}
+#endif
+
int
main (int argc, char **argv)
{
@@ -156,6 +269,11 @@ main (int argc, char **argv)
g_autoptr(GDBusConnection) connection = NULL;
guint32 request_name_ret;
+#ifdef CONFIG_LIBFPRINT_PRIVATE
+ protect_libfprint ();
+ disable_memfd ();
+#endif
+
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);