diff options
author | Benjamin Berg <bberg@redhat.com> | 2021-08-11 17:06:47 +0200 |
---|---|---|
committer | Benjamin Berg <bberg@redhat.com> | 2021-08-16 14:11:44 +0200 |
commit | 53db534514059eb7cc0395492682252daedccf91 (patch) | |
tree | a3abdeab9f4533bb0da0a8dde81b380793d1b4c1 | |
parent | e3b0d52ce504e9f0b10755bfbc329c79bd6a5519 (diff) |
Create private copy of libfprint executable pagesbenzea/private-libfprint-copy
This protects against side-channel attacks by evicting the libfprint
executable code from the CPU caches.
-rw-r--r-- | config.h.in | 3 | ||||
-rw-r--r-- | data/fprintd.service.in | 3 | ||||
-rw-r--r-- | data/meson.build | 7 | ||||
-rw-r--r-- | meson.build | 1 | ||||
-rw-r--r-- | meson_options.txt | 4 | ||||
-rw-r--r-- | src/main.c | 118 |
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') @@ -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); |