Merge branch 'issue-1190'

SEE #1190
main
Adriaan de Groot 6 years ago
commit 4c5008ecb1

@ -6,11 +6,31 @@ website will have to do for older versions.
# 3.2.11 (unreleased) #
This release contains contributions from (alphabetically by first name):
- No other contributors this time around.
This is a security release with no functional changes (except for
improved security) relative to 3.2.10. The Calamares team would like
to acknowledge the help of the following people in reporting and
understanding the issues (alphabetically by first name):
- Kevin Kofler
- Seth Arnold
- Simon Quigley
- Thomas Ward
## Core ##
No core changes.
## Modules ##
- *initramfs* could create an initramfs with insecure permissions.
Since the keyfile is included in the initramfs, an attacker could
read the file from the initramfs. #1190
- *luksbootkeyfile* created a key file where a window of opportunity
existed where the key file could have too-lax file permissions.
#1191 CVE-2019-13179
# 3.2.10 (2019-06-28) #

@ -0,0 +1,9 @@
calamares_add_plugin( luksbootkeyfile
TYPE job
EXPORT_MACRO PLUGINDLLEXPORT_PRO
SOURCES
LuksBootKeyFileJob.cpp
LINK_PRIVATE_LIBRARIES
calamares
SHARED_LIB
)

@ -0,0 +1,210 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares 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 3 of the License, or
* (at your option) any later version.
*
* Calamares 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#include "LuksBootKeyFileJob.h"
#include "utils/CalamaresUtilsSystem.h"
#include "utils/Logger.h"
#include "utils/UMask.h"
#include "utils/Variant.h"
#include "GlobalStorage.h"
#include "JobQueue.h"
LuksBootKeyFileJob::LuksBootKeyFileJob( QObject* parent )
: Calamares::CppJob( parent )
{
}
LuksBootKeyFileJob::~LuksBootKeyFileJob() {}
QString
LuksBootKeyFileJob::prettyName() const
{
return tr( "Configuring LUKS key file." );
}
struct LuksDevice
{
LuksDevice( const QMap< QString, QVariant >& pinfo )
: isValid( false )
, isRoot( false )
{
if ( pinfo.contains( "luksMapperName" ) )
{
QString fs = pinfo[ "fs" ].toString();
QString mountPoint = pinfo[ "mountPoint" ].toString();
if ( !mountPoint.isEmpty() || fs == QStringLiteral( "linuxswap" ) )
{
isValid = true;
isRoot = mountPoint == '/';
device = pinfo[ "device" ].toString();
passphrase = pinfo[ "luksPassphrase" ].toString();
}
}
}
bool isValid;
bool isRoot;
QString device;
QString passphrase;
};
struct LuksDeviceList
{
LuksDeviceList( const QVariant& partitions )
: valid( false )
{
if ( partitions.canConvert< QVariantList >() )
{
devices = getLuksDevices( partitions.toList() );
valid = true;
}
}
/** @brief Extract the luks passphrases setup.
*
* Given a list of partitions (as set up by the partitioning module,
* so there's maps with keys inside), returns just the list of
* luks passphrases for each device.
*/
static QList< LuksDevice >
getLuksDevices( const QVariantList& list )
{
QList< LuksDevice > luksItems;
for ( const auto& p : list )
{
if ( p.canConvert< QVariantMap >() )
{
LuksDevice d( p.toMap() );
if ( d.isValid )
{
luksItems.append( d );
}
}
}
return luksItems;
}
QList< LuksDevice > devices;
bool valid;
};
static const char keyfile[] = "/crypto_keyfile.bin";
static bool
generateTargetKeyfile()
{
CalamaresUtils::UMask m( CalamaresUtils::UMask::Safe );
auto r = CalamaresUtils::System::instance()->targetEnvCommand(
{ "dd", "bs=512", "count=4", "if=/dev/urandom", QString( "of=%1" ).arg( keyfile ) } );
if ( r.getExitCode() != 0 )
{
cWarning() << "Could not create LUKS keyfile:" << r.getOutput() << "(exit code" << r.getExitCode() << ')';
return false;
}
return true;
}
static bool
setupLuks( const LuksDevice& d )
{
auto r = CalamaresUtils::System::instance()->targetEnvCommand(
{ "cryptsetup", "luksAddKey", d.device, keyfile }, QString(), d.passphrase, 15 );
if ( r.getExitCode() != 0 )
{
cWarning() << "Could not configure LUKS keyfile on" << d.device << ':' << r.getOutput() << "(exit code"
<< r.getExitCode() << ')';
return false;
}
return true;
}
Calamares::JobResult
LuksBootKeyFileJob::exec()
{
const auto* gs = Calamares::JobQueue::instance()->globalStorage();
if ( !gs )
{
return Calamares::JobResult::internalError(
"LukeBootKeyFile", "No GlobalStorage defined.", Calamares::JobResult::InvalidConfiguration );
}
if ( !gs->contains( "partitions" ) )
{
cError() << "No GS[partitions] key.";
return Calamares::JobResult::internalError(
"LukeBootKeyFile", tr( "No partitions are defined." ), Calamares::JobResult::InvalidConfiguration );
}
LuksDeviceList s( gs->value( "partitions" ) );
if ( !s.valid )
{
cError() << "GS[partitions] is invalid";
return Calamares::JobResult::internalError(
"LukeBootKeyFile", tr( "No partitions are defined." ), Calamares::JobResult::InvalidConfiguration );
}
cDebug() << "There are" << s.devices.count() << "LUKS partitions";
if ( s.devices.count() < 1 )
{
cDebug() << Logger::SubEntry << "Nothing to do for LUKS.";
return Calamares::JobResult::ok();
}
auto it = std::partition( s.devices.begin(), s.devices.end(), []( const LuksDevice& d ) { return d.isRoot; } );
for ( const auto& d : s.devices )
{
cDebug() << Logger::SubEntry << d.isRoot << d.device << d.passphrase;
}
if ( it == s.devices.begin() )
{
// Then there was no root partition
cDebug() << Logger::SubEntry << "No root partition.";
return Calamares::JobResult::ok();
}
if ( s.devices.first().passphrase.isEmpty() )
{
cDebug() << Logger::SubEntry << "No root passphrase.";
return Calamares::JobResult::error(
tr( "Encrypted rootfs setup error" ),
tr( "Root partition %1 is LUKS but no passphrase has been set." ).arg( s.devices.first().device ) );
}
if ( !generateTargetKeyfile() )
{
return Calamares::JobResult::error(
tr( "Encrypted rootfs setup error" ),
tr( "Could not create LUKS key file for root partition %1." ).arg( s.devices.first().device ) );
}
for ( const auto& d : s.devices )
{
if ( !setupLuks( d ) )
return Calamares::JobResult::error(
tr( "Encrypted rootfs setup error" ),
tr( "Could configure LUKS key file on partition %1." ).arg( d.device ) );
}
return Calamares::JobResult::ok();
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( LuksBootKeyFileJobFactory, registerPlugin< LuksBootKeyFileJob >(); )

@ -0,0 +1,48 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2019, Adriaan de Groot <groot@kde.org>
*
* Calamares 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 3 of the License, or
* (at your option) any later version.
*
* Calamares 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LUKSBOOTKEYFILEJOB_H
#define LUKSBOOTKEYFILEJOB_H
#include "CppJob.h"
#include "PluginDllMacro.h"
#include "utils/PluginFactory.h"
#include <QObject>
#include <QVariantMap>
/** @brief Creates the LUKS boot key file and adds it to the cryptsetup.
*
* This job has no configuration, because it takes everything
* from the global storage settings set by others.
*/
class PLUGINDLLEXPORT LuksBootKeyFileJob : public Calamares::CppJob
{
Q_OBJECT
public:
explicit LuksBootKeyFileJob( QObject* parent = nullptr );
virtual ~LuksBootKeyFileJob() override;
QString prettyName() const override;
Calamares::JobResult exec() override;
};
CALAMARES_PLUGIN_FACTORY_DECLARATION( LuksBootKeyFileJobFactory )
#endif // LUKSBOOTKEYFILEJOB_H

@ -1,103 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# === This file is part of Calamares - <https://github.com/calamares> ===
#
# Copyright 2016, Teo Mrnjavac <teo@kde.org>
# Copyright 2017, Alf Gaida <agaida@siduction.org>
# Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
#
# Calamares 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 3 of the License, or
# (at your option) any later version.
#
# Calamares 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
import libcalamares
from libcalamares.utils import check_target_env_call
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Configuring LUKS key file.")
def run():
"""
This module sets up a file crypto_keyfile.bin on the rootfs, assuming the
rootfs is LUKS encrypted and a passphrase is provided. This file is then
included in the initramfs and used for unlocking the rootfs from a
previously unlocked GRUB2 session.
:return:
"""
partitions = libcalamares.globalstorage.value("partitions")
if not partitions:
libcalamares.utils.warning("partitions is empty, {!s}".format(partitions))
return (_("Configuration Error"),
_("No partitions are defined for <pre>{!s}</pre> to use." ).format("luksbootkey"))
luks_root_device = ""
luks_root_passphrase = ""
additional_luks_devices = []
for partition in partitions:
if partition["mountPoint"] == "/" and "luksMapperName" in partition:
luks_root_device = partition["device"]
luks_root_passphrase = partition["luksPassphrase"]
elif "luksMapperName" in partition and\
(partition["mountPoint"] or partition["fs"] == "linuxswap"):
additional_luks_devices.append((partition["device"],
partition["luksPassphrase"]))
if not luks_root_device:
return None
if not luks_root_passphrase:
libcalamares.utils.debug("No LUKS passphrase, root {!s}".format(luks_root_device))
return (
_("Encrypted rootfs setup error"),
_("Rootfs partition {!s} is LUKS but no passphrase found.").format(luks_root_device))
# Generate random keyfile
check_target_env_call(["dd",
"bs=512",
"count=4",
"if=/dev/urandom",
"of=/crypto_keyfile.bin"])
check_target_env_call(["cryptsetup",
"luksAddKey",
luks_root_device,
"/crypto_keyfile.bin"],
luks_root_passphrase,
15) # timeout 15s
for additional_device in additional_luks_devices:
check_target_env_call(["cryptsetup",
"luksAddKey",
additional_device[0],
"/crypto_keyfile.bin"],
additional_device[1],
15) # timeout 15s
check_target_env_call(["chmod",
"g-rwx,o-rwx",
"/crypto_keyfile.bin"])
return None

@ -1,5 +0,0 @@
---
type: "job"
name: "luksbootkeyfile"
interface: "python"
script: "main.py"
Loading…
Cancel
Save