Create private copy of libfprint executable pages

This protects against side-channel attacks by evicting the libfprint
executable code from the CPU caches.
This commit is contained in:
Benjamin Berg 2021-08-11 17:06:47 +02:00
parent e3b0d52ce5
commit 53db534514
6 changed files with 135 additions and 1 deletions

View file

@ -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

View file

@ -18,7 +18,8 @@ StateDirectoryMode=0700
ProtectHome=true
PrivateTmp=true
SystemCallFilter=@system-service
SystemCallArchitectures=native
@syscall_filter@
# Network
PrivateNetwork=true

View file

@ -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',

View file

@ -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',

View file

@ -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')

View file

@ -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);