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
This commit is contained in:
Mike Fleetwood 2017-10-06 20:50:41 +01:00 committed by Curtis Gedak
parent 7fe4148074
commit 04637a3426
5 changed files with 257 additions and 0 deletions

View file

@ -41,6 +41,7 @@ EXTRA_DIST = \
Partition.h \
PartitionLUKS.h \
PartitionVector.h \
PasswordRAMStore.h \
PipeCapture.h \
Proc_Partitions_Info.h \
ProgressBar.h \

View file

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

View file

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

View file

@ -51,6 +51,7 @@ gpartedbin_SOURCES = \
Partition.cc \
PartitionLUKS.cc \
PartitionVector.cc \
PasswordRAMStore.cc \
PipeCapture.cc \
Proc_Partitions_Info.cc \
ProgressBar.cc \

207
src/PasswordRAMStore.cc Normal file
View file

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