gparted/src/LUKS_Info.cc

167 lines
5.4 KiB
C++

/* Copyright (C) 2015 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 "LUKS_Info.h"
#include "BlockSpecial.h"
#include "Utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
namespace GParted
{
// Cache of active dm-crypt mappings.
// Example entry:
// {name="sdb6_crypt", container=BlockSpecial{"/dev/sdb6", 8, 22}, offset=2097152, length=534773760}
std::vector<LUKS_Mapping> LUKS_Info::luks_mapping_cache;
bool LUKS_Info::cache_initialised = false;
void LUKS_Info::clear_cache()
{
luks_mapping_cache.clear();
cache_initialised = false;
}
const LUKS_Mapping & LUKS_Info::get_cache_entry( const Glib::ustring & path )
{
initialise_if_required();
return get_cache_entry_internal( path );
}
//Private methods
void LUKS_Info::initialise_if_required()
{
if ( ! cache_initialised )
{
load_cache();
cache_initialised = true;
}
}
void LUKS_Info::load_cache()
{
luks_mapping_cache.clear();
Glib::ustring output;
Glib::ustring error;
Utils::execute_command( "dmsetup table --target crypt", output, error, true );
// dmsetup output is one line per dm-crypt mapping containing fields:
// NAME: START LENGTH TARGET CIPHER KEY IVOFFSET DEVPATH OFFSET ...
// or the text:
// No devices found
// First 4 fields are defined by the dmsetup program by the print call in the
// _status() function:
// https://git.fedorahosted.org/cgit/lvm2.git/tree/tools/dmsetup.c?id=v2_02_118#n1715
// Field 5 onwards are called parameters and documented in the kernel source:
// https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/device-mapper/dm-crypt.txt?id=v5.0
std::vector<Glib::ustring> lines;
Utils::tokenize( output, lines, "\n" );
for ( unsigned int i = 0 ; i < lines.size() ; i ++ )
{
LUKS_Mapping luks_map;
std::vector<Glib::ustring> fields;
Utils::tokenize( lines[i], fields, " " );
const unsigned DMCRYPT_FIELD_Name = 0;
const unsigned DMCRYPT_FIELD_length = 2;
const unsigned DMCRYPT_FIELD_key = 5;
const unsigned DMCRYPT_FIELD_devpath = 7;
const unsigned DMCRYPT_FIELD_offset = 8;
if ( fields.size() <= DMCRYPT_FIELD_offset )
continue;
// Extract LUKS mapping name
Glib::ustring name = fields[DMCRYPT_FIELD_Name];
size_t len = name.length();
if ( len <= 1 || name[len-1] != ':' )
continue;
luks_map.name = name.substr( 0, len-1 );
// Extract LUKS underlying device containing the encrypted data. May be
// either a device name (/dev/sda1) or major, minor pair (8:1).
luks_map.container = BlockSpecial();
Glib::ustring devpath = fields[DMCRYPT_FIELD_devpath];
unsigned long maj = 0UL;
unsigned long min = 0UL;
if ( devpath.length() > 0 && devpath[0] == '/' )
luks_map.container = BlockSpecial( devpath );
else if ( sscanf( devpath.c_str(), "%lu:%lu", &maj, &min ) == 2 )
{
luks_map.container.m_major = maj;
luks_map.container.m_minor = min;
}
else
continue;
// Extract LUKS offset and length. Dm-crypt reports all sizes in units of
// 512 byte sectors.
luks_map.offset = -1LL;
luks_map.length = -1LL;
Byte_Value offset = atoll( fields[DMCRYPT_FIELD_offset].c_str() );
Byte_Value length = atoll( fields[DMCRYPT_FIELD_length].c_str() );
if ( offset > 0LL && length > 0LL )
{
luks_map.offset = offset * 512LL;
luks_map.length = length * 512LL;
}
else
continue;
// Extract LUKS mapping master key location. Following the cryptsetup
// implementation method; first character of KEY field is ":" means the
// master key was stored in the kernel key ring, otherwise it is stored in
// the Device-Mapper crypt target itself.
// * cryptsetup/v2.3.5/lib/libdevmapper.c:_dm_target_query_crypt()
// https://gitlab.com/cryptsetup/cryptsetup/-/blob/v2.3.5/lib/libdevmapper.c#L2031
// * cryptsetup/v2.3.5/src/cryptsetup.c:action_status()
// https://gitlab.com/cryptsetup/cryptsetup/-/blob/v2.3.5/src/cryptsetup.c#L839
luks_map.key_loc = (fields[DMCRYPT_FIELD_key][0] == ':') ? KEYLOC_KeyRing : KEYLOC_DMCrypt;
luks_mapping_cache.push_back( luks_map );
}
}
// Return LUKS cache entry for the named underlying block device path,
// or not found substitute when no entry exists.
const LUKS_Mapping & LUKS_Info::get_cache_entry_internal( const Glib::ustring & path )
{
BlockSpecial bs = BlockSpecial( path );
for ( unsigned int i = 0 ; i < luks_mapping_cache.size() ; i ++ )
{
if ( bs == luks_mapping_cache[i].container )
{
// Store underlying block device path in the BlockSpecial object
// if not already known. Not required, just for completeness.
if ( ! luks_mapping_cache[i].container.m_name.length() )
luks_mapping_cache[i].container.m_name = path;
return luks_mapping_cache[i];
}
}
static LUKS_Mapping not_found = {"", BlockSpecial(), -1LL, -1LL};
return not_found;
}
}//GParted