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:
parent
7fe4148074
commit
04637a3426
|
@ -41,6 +41,7 @@ EXTRA_DIST = \
|
||||||
Partition.h \
|
Partition.h \
|
||||||
PartitionLUKS.h \
|
PartitionLUKS.h \
|
||||||
PartitionVector.h \
|
PartitionVector.h \
|
||||||
|
PasswordRAMStore.h \
|
||||||
PipeCapture.h \
|
PipeCapture.h \
|
||||||
Proc_Partitions_Info.h \
|
Proc_Partitions_Info.h \
|
||||||
ProgressBar.h \
|
ProgressBar.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 */
|
|
@ -38,6 +38,7 @@ src/OperationResizeMove.cc
|
||||||
src/Partition.cc
|
src/Partition.cc
|
||||||
src/PartitionLUKS.cc
|
src/PartitionLUKS.cc
|
||||||
src/PartitionVector.cc
|
src/PartitionVector.cc
|
||||||
|
src/PasswordRAMStore.cc
|
||||||
src/ProgressBar.cc
|
src/ProgressBar.cc
|
||||||
src/SWRaid_Info.cc
|
src/SWRaid_Info.cc
|
||||||
src/TreeView_Detail.cc
|
src/TreeView_Detail.cc
|
||||||
|
|
|
@ -51,6 +51,7 @@ gpartedbin_SOURCES = \
|
||||||
Partition.cc \
|
Partition.cc \
|
||||||
PartitionLUKS.cc \
|
PartitionLUKS.cc \
|
||||||
PartitionVector.cc \
|
PartitionVector.cc \
|
||||||
|
PasswordRAMStore.cc \
|
||||||
PipeCapture.cc \
|
PipeCapture.cc \
|
||||||
Proc_Partitions_Info.cc \
|
Proc_Partitions_Info.cc \
|
||||||
ProgressBar.cc \
|
ProgressBar.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
|
Loading…
Reference in New Issue