[gparted] Add PasswordRAMStore module (#795617)



commit 04637a3426bd36c88947322f75f7eae2a820141c
Author: Mike Fleetwood <mike fleetwood googlemail com>
Date:   Fri Oct 6 20:50:41 2017 +0100

    Add PasswordRAMStore module (#795617)
    
    Application level requirements for secure password management were set
    out in "LUKS password handling, threats and preventative measures" [1].
    
    The requirements are:
    1) Passwords are stored in RAM and are not allowed to be paged to swap.
       (However hibernating with GParted still running will write all of RAM
       to swap).
    2) Passwords are wiped from RAM when no longer needed.  When each
       password is no longer needed and when GParted closes.
    3) Passwords are referenced by unique key.  Recommend using LUKS UUIDs
       as the unique key.
       (Each LUKS password should only ever need to be entered once for each
       execution of GParted.  Therefore the passwords can't be stored in any
       of the existing data structures such as Partitions or LUKS_Info cache
       because all of these are cleared and reloaded on each device
       refresh).
    
    There seems to be two possible implementation methods: use an existing
    library to provide secure memory handling, or write our own.
    Libgcrypt [2] and libsodium [3] cryptographic libraries both provide
    secure memory handling.  (Secure memory is quite simple really, some
    virtual memory locked into RAM which is zeroed when no longer needed).
    Linking to an encryption library just to provide secure memory seems
    like using a sledge hammer to crack a nut.  Also because of requirement
    (3) above a module is needed to "own" the pointers to the passwords in
    the secure memory.  Managing the secure memory ourselves is probably no
    more code that that needed to interface to libgcrypt.  Therefore handle
    the secure memory ourselves.
    
    So far the module is only compiled.  It is not used anywhere in GParted.
    
    [1] LUKS password handling, threats and preventative measures
        https://bugzilla.gnome.org/show_bug.cgi?id=627701#c56
    
    [2] libgcrypt general purpose cryptographic library, as used in GNU
        Privacy Guard
        https://gnupg.org/related_software/libgcrypt/
    
    [3] libsodium crypto library
        https://download.libsodium.org/doc/
    
    Bug 795617 - Implement opening and closing of LUKS mappings

 include/Makefile.am        |    1 +
 include/PasswordRAMStore.h |   47 ++++++++++
 po/POTFILES.in             |    1 +
 src/Makefile.am            |    1 +
 src/PasswordRAMStore.cc    |  207 ++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 257 insertions(+), 0 deletions(-)
---
diff --git a/include/Makefile.am b/include/Makefile.am
index d0a9d3e..16cd4c7 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -41,6 +41,7 @@ EXTRA_DIST = \
        Partition.h                     \
        PartitionLUKS.h                 \
        PartitionVector.h               \
+       PasswordRAMStore.h              \
        PipeCapture.h                   \
        Proc_Partitions_Info.h          \
        ProgressBar.h                   \
diff --git a/include/PasswordRAMStore.h b/include/PasswordRAMStore.h
new file mode 100644
index 0000000..acfabb8
--- /dev/null
+++ b/include/PasswordRAMStore.h
@@ -0,0 +1,47 @@
+/* Copyright (C) 2018 Mike Fleetwood
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/* PasswordRAMStore
+ *
+ * Memory only store of passwords and passphrases.  Works like an associative array with
+ * a unique key used to identify each password.  Passwords are C strings which are stored
+ * in a block of the process' virtual memory locked into RAM.  Looked up pointers to
+ * passwords are valid at least until the next time the store is modified by an insert or
+ * erase call.  Passwords are wiped from memory when no longer wanted.
+ *
+ * Recommend using LUKS UUIDs as the key when storing LUKS passphrases.
+ */
+
+#ifndef GPARTED_PASSWORDRAMSTORE_H
+#define GPARTED_PASSWORDRAMSTORE_H
+
+#include <glibmm/ustring.h>
+
+namespace GParted
+{
+
+class PasswordRAMStore
+{
+public:
+       static bool insert( const Glib::ustring & key, const char * password );
+       static bool erase( const Glib::ustring & key );
+       static const char * lookup( const Glib::ustring & key );
+};
+
+} //GParted
+
+#endif /* GPARTED_PASSWORDRAMSTORE_H */
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a89cade..d359a92 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -38,6 +38,7 @@ src/OperationResizeMove.cc
 src/Partition.cc
 src/PartitionLUKS.cc
 src/PartitionVector.cc
+src/PasswordRAMStore.cc
 src/ProgressBar.cc
 src/SWRaid_Info.cc
 src/TreeView_Detail.cc
diff --git a/src/Makefile.am b/src/Makefile.am
index 4314657..20a5de3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,7 @@ gpartedbin_SOURCES = \
        Partition.cc                    \
        PartitionLUKS.cc                \
        PartitionVector.cc              \
+       PasswordRAMStore.cc             \
        PipeCapture.cc                  \
        Proc_Partitions_Info.cc         \
        ProgressBar.cc                  \
diff --git a/src/PasswordRAMStore.cc b/src/PasswordRAMStore.cc
new file mode 100644
index 0000000..1e29533
--- /dev/null
+++ b/src/PasswordRAMStore.cc
@@ -0,0 +1,207 @@
+/* Copyright (C) 2017 Mike Fleetwood
+ *
+ *  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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "PasswordRAMStore.h"
+
+#include <stddef.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <glibmm/ustring.h>
+#include <iostream>
+#include <vector>
+
+namespace GParted
+{
+
+struct PWEntry
+{
+       Glib::ustring key;       // Unique key identifying this password
+       char *        password;  // Pointer to the password in protected_mem
+       size_t        len;       // Number of bytes in the password
+};
+
+class PWStore
+{
+public:
+       typedef std::vector<PWEntry>::iterator iterator;
+
+       PWStore();
+       ~PWStore();
+
+       bool insert( const Glib::ustring & key, const char * password );
+       bool erase( const Glib::ustring & key );
+       const char * lookup( const Glib::ustring & key );
+
+private:
+       iterator find_key( const Glib::ustring & key );
+
+       std::vector<PWEntry> pw_entries;     // Linear vector of password entries
+       char *               protected_mem;  // Block of virtual memory locked into RAM
+};
+
+// Example PWStore data model.  After this sequence of calls:
+//     mystore = PWStore();
+//     mystore.insert( "UUID1", "password1", 9 );
+//     mystore.insert( "UUID2", "password2", 9 );
+//     mystore.insert( "UUID3", "password3", 9 );
+//     mystore.erase( "UUID2" );
+// The data would be:
+//                   {key    , password, len}
+//     pw_entries = [{"UUID1",     PTR1, 9  },
+//                   {"UUID3",     PTR3, 9  }
+//                  ]
+//                     PTR1                           PTR3
+//                      v                              v
+//     protected_mem = "password1\0\0\0\0\0\0\0\0\0\0\0password3\0..."
+//
+// Description of processing:
+// Pw_entries (password entries) and the bytes of the passwords themselves (protected_mem)
+// are always stored in the same order.  A new password is always added at the end of
+// pw_entries and after the last password in protected_mem.  Lookup of a password is a
+// linear search for the key in pw_entries.  Erasing an entry just zeros the bytes of the
+// password in protected_mem and erases the entry in pw_entries vector.  No compaction of
+// unused bytes in protected_mem is performed.  Reuse of protected_mem only occurs when
+// the password entry at the end of the pw_entries vector is erased and subsequently a new
+// password inserted.
+
+// The 4096 bytes of protected memory is enough to store 195, 20 byte passwords.
+const size_t ProtectedMemSize = 4096;
+
+PWStore::PWStore()
+{
+       // MAP_ANONYMOUS also ensures RAM is zero initialised.
+       protected_mem = (char *) mmap( NULL, ProtectedMemSize, PROT_READ|PROT_WRITE,
+                                      MAP_PRIVATE|MAP_ANONYMOUS|MAP_LOCKED, -1, 0 );
+       if ( protected_mem == MAP_FAILED )
+       {
+               protected_mem = NULL;
+               std::cerr << "No locked virtual memory for password RAM store" << std::endl;
+       }
+}
+
+PWStore::~PWStore()
+{
+       // WARNING:
+       // memset() can be optimised away if the compiler knows the memory is not accessed
+       // again.  In this case the pointer to the zeroed memory is passed to munmap()
+       // afterwards so the compiler has to assume the memory is accessed so can't
+       // optimise the memset() away.
+       // Reference:
+       // * SEI CERT C Coding Standard, MSC06-C. Beware of compiler optimizations
+       //   https://www.securecoding.cert.org/confluence/display/c/MSC06-C.+Beware+of+compiler+optimizations
+       //
+       // NOTE:
+       // For secure overwriting of memory C11 has memset_s(), Linux kernel has
+       // memzero_explicit(), FreeBSD/OpenBSD have explicit_bzero() and Windows has
+       // SecureZeroMemory().
+       if ( protected_mem != NULL )
+       {
+               memset( protected_mem, '\0', ProtectedMemSize );
+               munmap( protected_mem, ProtectedMemSize );
+       }
+}
+
+bool PWStore::insert( const Glib::ustring & key, const char * password )
+{
+       if ( protected_mem == NULL )
+               // No locked memory for passwords
+               return false;
+
+       if ( find_key( key ) != pw_entries.end() )
+               // Entry already exists
+               return false;
+
+       char * next_password = protected_mem;
+       size_t available_len = ProtectedMemSize - 1;
+       if ( pw_entries.size() )
+       {
+               const PWEntry & last_pw_entry = pw_entries.back();
+               next_password = last_pw_entry.password + last_pw_entry.len + 1;
+               available_len = next_password - protected_mem - 1;
+       }
+       size_t pw_len = strlen( password );
+       if ( pw_len <= available_len )
+       {
+               PWEntry new_pw_entry = {key, next_password, pw_len};
+               pw_entries.push_back( new_pw_entry );
+               memcpy( next_password, password, pw_len );
+               next_password[pw_len] = '\0';
+               return true;
+       }
+
+       // Not enough space available
+       std::cerr << "Password RAM store exhausted" << std::endl;
+       return false;
+}
+
+bool PWStore::erase( const Glib::ustring & key )
+{
+       iterator pw_entry_iter = find_key( key );
+       if ( pw_entry_iter != pw_entries.end() )
+       {
+               memset( pw_entry_iter->password, '\0', pw_entry_iter->len );
+               pw_entries.erase( pw_entry_iter );
+               return true;
+       }
+
+       // No such key
+       return false;
+}
+
+const char * PWStore::lookup( const Glib::ustring & key )
+{
+       iterator pw_entry_iter = find_key( key );
+       if ( pw_entry_iter != pw_entries.end() )
+       {
+               return pw_entry_iter->password;
+       }
+
+       // No such key
+       return NULL;
+}
+
+PWStore::iterator PWStore::find_key( const Glib::ustring & key )
+{
+       for ( iterator pw_entry_iter = pw_entries.begin() ; pw_entry_iter != pw_entries.end(); 
++pw_entry_iter )
+       {
+               if ( pw_entry_iter->key == key )
+                       return pw_entry_iter;
+       }
+
+       return pw_entries.end();
+}
+
+// The single password RAM store
+static PWStore single_pwstore;
+
+// PasswordRAMStore public methods
+
+bool PasswordRAMStore::insert( const Glib::ustring & key, const char * password )
+{
+       return single_pwstore.insert( key, password );
+}
+
+bool PasswordRAMStore::erase( const Glib::ustring & key )
+{
+       return single_pwstore.erase( key );
+}
+
+const char * PasswordRAMStore::lookup( const Glib::ustring & key )
+{
+       return single_pwstore.lookup( key );
+}
+
+} //GParted


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]