diff --git a/.clang-format b/.clang-format index 1eb0867e1..997ad379b 100644 --- a/.clang-format +++ b/.clang-format @@ -6,7 +6,7 @@ AllowAllParametersOfDeclarationOnNextLine: 'false' AllowShortFunctionsOnASingleLine: Inline AllowShortIfStatementsOnASingleLine: 'false' AllowShortLoopsOnASingleLine: 'false' -AlwaysBreakAfterDefinitionReturnType: All +AlwaysBreakAfterReturnType: TopLevelDefinitions AlwaysBreakTemplateDeclarations: Yes BinPackArguments: 'false' BinPackParameters: 'false' @@ -19,7 +19,7 @@ FixNamespaceComments: 'true' IncludeBlocks: Preserve IndentWidth: '4' MaxEmptyLinesToKeep: '2' -NamespaceIndentation: Inner +NamespaceIndentation: None PointerAlignment: Left ReflowComments: 'false' SortIncludes: 'true' diff --git a/CHANGES b/CHANGES index 5085ad3c7..a73684e6a 100644 --- a/CHANGES +++ b/CHANGES @@ -3,7 +3,7 @@ contributors are listed. Note that Calamares does not have a historical changelog -- this log starts with version 3.2.0. The release notes on the website will have to do for older versions. -# 3.2.11 (unreleased) # +# 3.2.12 (unreleased) # This release contains contributions from (alphabetically by first name): @@ -12,6 +12,35 @@ This release contains contributions from (alphabetically by first name): ## Modules ## +# 3.2.11 (2019-07-06) # + +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 +Both CVE's have been resolved. + +## 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 CVE-2019-13178 + - *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) # This release contains contributions from (alphabetically by first name): diff --git a/CMakeLists.txt b/CMakeLists.txt index c7706ec5b..cae89fc91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ cmake_minimum_required( VERSION 3.2 FATAL_ERROR ) project( CALAMARES - VERSION 3.2.11 + VERSION 3.2.12 LANGUAGES C CXX ) set( CALAMARES_VERSION_RC 1 ) # Set to 0 during release cycle, 1 during development diff --git a/ci/calamaresstyle b/ci/calamaresstyle index 11c587ada..a877de7d1 100755 --- a/ci/calamaresstyle +++ b/ci/calamaresstyle @@ -9,7 +9,13 @@ set -e AS=$( which astyle ) -CF=$( which clang-format-7 ) + +for _cf in clang-format-7 clang-format-8 clang-format70 clang-format80 +do + # Not an error if this particular clang-format isn't found + CF=$( which $_cf || true ) + test -n "$CF" && break +done test -n "$AS" || { echo "! No astyle found in PATH"; exit 1 ; } test -n "$CF" || { echo "! No clang-format-7 found in PATH"; exit 1 ; } diff --git a/lang/calamares_cs_CZ.ts b/lang/calamares_cs_CZ.ts index 058848a3f..1f6ef2129 100644 --- a/lang/calamares_cs_CZ.ts +++ b/lang/calamares_cs_CZ.ts @@ -99,12 +99,12 @@ Reload Stylesheet - + Znovunačíst sešit se styly Widget Tree - + Strom widgetu @@ -159,12 +159,12 @@ Run command '%1' in target system. - + Spustit v cílovém systému příkaz „%1“. Run command '%1'. - + Spustit příkaz „%1“ diff --git a/lang/calamares_fi_FI.ts b/lang/calamares_fi_FI.ts index 22a7a81f6..be8928448 100644 --- a/lang/calamares_fi_FI.ts +++ b/lang/calamares_fi_FI.ts @@ -210,12 +210,12 @@ Waiting for %n module(s). - + Odotetaan %n moduuli(t).Odotetaan %n moduuli(t). (%n second(s)) - + (%n sekunttia(s))(%n sekunttia(s)) diff --git a/lang/python/ast/LC_MESSAGES/python.mo b/lang/python/ast/LC_MESSAGES/python.mo index c3c75acd6..6cacaf7af 100644 Binary files a/lang/python/ast/LC_MESSAGES/python.mo and b/lang/python/ast/LC_MESSAGES/python.mo differ diff --git a/lang/python/ast/LC_MESSAGES/python.po b/lang/python/ast/LC_MESSAGES/python.po index d5f56e687..d5c40ef55 100644 --- a/lang/python/ast/LC_MESSAGES/python.po +++ b/lang/python/ast/LC_MESSAGES/python.po @@ -323,7 +323,7 @@ msgstr "" #: src/modules/initramfs/main.py:49 msgid "Failed to run update-initramfs on the target" -msgstr "" +msgstr "Fallu al executar update-initramfs nel destín" #: src/modules/initramfs/main.py:50 src/modules/dracut/main.py:59 msgid "The exit code was {}" @@ -339,7 +339,7 @@ msgstr "" #: src/modules/dracut/main.py:58 msgid "Failed to run dracut on the target" -msgstr "" +msgstr "Fallu al executar dracut nel destín" #: src/modules/initramfscfg/main.py:41 msgid "Configuring initramfs." diff --git a/lang/python/tr_TR/LC_MESSAGES/python.mo b/lang/python/tr_TR/LC_MESSAGES/python.mo index 54ff6bebe..1e461e370 100644 Binary files a/lang/python/tr_TR/LC_MESSAGES/python.mo and b/lang/python/tr_TR/LC_MESSAGES/python.mo differ diff --git a/lang/python/tr_TR/LC_MESSAGES/python.po b/lang/python/tr_TR/LC_MESSAGES/python.po index 82b24e462..d61c4cc2b 100644 --- a/lang/python/tr_TR/LC_MESSAGES/python.po +++ b/lang/python/tr_TR/LC_MESSAGES/python.po @@ -96,7 +96,7 @@ msgstr "Dosya sistemlerini ayırın." #: src/modules/unpackfs/main.py:41 msgid "Filling up filesystems." -msgstr "Dosya sistemini genişlet." +msgstr "Dosya sistemi genişletiliyor." #: src/modules/unpackfs/main.py:159 msgid "rsync failed with error code {}." diff --git a/src/branding/default/show.qml b/src/branding/default/show.qml index 43b407283..c989676dd 100644 --- a/src/branding/default/show.qml +++ b/src/branding/default/show.qml @@ -71,6 +71,6 @@ Presentation function onActivate() { presentation.currentSlide = 0; advanceTimer.running = true - console.log("Component activated"); + console.log("QML Component (default slideshow) activated"); } } diff --git a/src/libcalamares/CMakeLists.txt b/src/libcalamares/CMakeLists.txt index f74c11b0c..19bcc921d 100644 --- a/src/libcalamares/CMakeLists.txt +++ b/src/libcalamares/CMakeLists.txt @@ -46,6 +46,7 @@ set( libSources utils/PluginFactory.cpp utils/Retranslator.cpp utils/String.cpp + utils/UMask.cpp utils/Variant.cpp utils/Yaml.cpp ) @@ -146,7 +147,7 @@ install( FILES ${utilsHeaders} DESTINATION include/libcalam # if ( ECM_FOUND AND BUILD_TESTING ) ecm_add_test( - Tests.cpp + utils/Tests.cpp TEST_NAME libcalamarestest LINK_LIBRARIES diff --git a/src/libcalamares/utils/CalamaresUtilsSystem.cpp b/src/libcalamares/utils/CalamaresUtilsSystem.cpp index 5990fbc42..7e55e6119 100644 --- a/src/libcalamares/utils/CalamaresUtilsSystem.cpp +++ b/src/libcalamares/utils/CalamaresUtilsSystem.cpp @@ -85,8 +85,10 @@ System::System( bool doChroot, QObject* parent ) { Q_ASSERT( !s_instance ); s_instance = this; - if ( !doChroot ) + if ( !doChroot && Calamares::JobQueue::instance() && Calamares::JobQueue::instance()->globalStorage() ) + { Calamares::JobQueue::instance()->globalStorage()->insert( "rootMountPoint", "/" ); + } } @@ -243,6 +245,69 @@ System::runCommand( return ProcessResult(r, output); } +QString +System::targetPath( const QString& path ) const +{ + QString completePath; + + if ( doChroot() ) + { + Calamares::GlobalStorage* gs = Calamares::JobQueue::instance() ? Calamares::JobQueue::instance()->globalStorage() : nullptr; + + if ( !gs || !gs->contains( "rootMountPoint" ) ) + { + cWarning() << "No rootMountPoint in global storage, cannot create target file" << path; + return QString(); + } + + completePath = gs->value( "rootMountPoint" ).toString() + '/' + path; + } + else + { + completePath = QStringLiteral( "/" ) + path; + } + + return completePath; +} + +QString +System::createTargetFile( const QString& path, const QByteArray& contents ) const +{ + QString completePath = targetPath( path ); + if ( completePath.isEmpty() ) + { + return QString(); + } + + QFile f( completePath ); + if ( f.exists() ) + { + return QString(); + } + + QIODevice::OpenMode m = +#if QT_VERSION >= QT_VERSION_CHECK( 5, 11, 0 ) + // New flag from Qt 5.11, implies WriteOnly + QIODevice::NewOnly | +#endif + QIODevice::WriteOnly | QIODevice::Truncate; + + if ( !f.open( m ) ) + { + return QString(); + } + + if ( f.write( contents ) != contents.size() ) + { + f.close(); + f.remove(); + return QString(); + } + + f.close(); + return QFileInfo( f ).canonicalFilePath(); +} + QPair System::getTotalMemoryB() const diff --git a/src/libcalamares/utils/CalamaresUtilsSystem.h b/src/libcalamares/utils/CalamaresUtilsSystem.h index c17d52e93..f778b67c9 100644 --- a/src/libcalamares/utils/CalamaresUtilsSystem.h +++ b/src/libcalamares/utils/CalamaresUtilsSystem.h @@ -205,6 +205,40 @@ public: return targetEnvOutput( QStringList{ command }, output, workingPath, stdInput, timeoutSec ); } + + /** @brief Gets a path to a file in the target system, from the host. + * + * @param path Path to the file; this is interpreted + * from the root of the target system (whatever that may be, + * but / in the chroot, or / in OEM modes). + * + * @return The complete path to the target file, from + * the root of the host system, or empty on failure. + * + * For instance, during installation where the target root is + * mounted on /tmp/calamares-something, asking for targetPath("/etc/passwd") + * will give you /tmp/calamares-something/etc/passwd. + * + * No attempt is made to canonicalize anything, since paths might not exist. + */ + DLLEXPORT QString targetPath( const QString& path ) const; + + /** @brief Create a (small-ish) file in the target system. + * + * @param path Path to the file; this is interpreted + * from the root of the target system (whatever that may be, + * but / in the chroot, or / in OEM modes). + * @param contents Actual content of the file. + * + * Will not overwrite files. Returns an empty string if the + * target file already exists. + * + * @return The complete canonical path to the target file from the + * root of the host system, or empty on failure. (Here, it is + * possible to be canonical because the file exists). + */ + DLLEXPORT QString createTargetFile( const QString& path, const QByteArray& contents ) const; + /** * @brief getTotalMemoryB returns the total main memory, in bytes. * diff --git a/src/libcalamares/Tests.cpp b/src/libcalamares/utils/Tests.cpp similarity index 69% rename from src/libcalamares/Tests.cpp rename to src/libcalamares/utils/Tests.cpp index 3b7624537..2f2b7ff4b 100644 --- a/src/libcalamares/Tests.cpp +++ b/src/libcalamares/utils/Tests.cpp @@ -18,23 +18,24 @@ #include "Tests.h" -#include "utils/CalamaresUtilsSystem.h" -#include "utils/Logger.h" -#include "utils/Yaml.h" +#include "CalamaresUtilsSystem.h" +#include "Logger.h" +#include "UMask.h" +#include "Yaml.h" #include #include +#include +#include +#include + QTEST_GUILESS_MAIN( LibCalamaresTests ) -LibCalamaresTests::LibCalamaresTests() -{ -} +LibCalamaresTests::LibCalamaresTests() {} -LibCalamaresTests::~LibCalamaresTests() -{ -} +LibCalamaresTests::~LibCalamaresTests() {} void LibCalamaresTests::initTestCase() @@ -46,9 +47,9 @@ LibCalamaresTests::testDebugLevels() { Logger::setupLogLevel( Logger::LOG_DISABLE ); - QCOMPARE( Logger::logLevel(), static_cast( Logger::LOG_DISABLE ) ); + QCOMPARE( Logger::logLevel(), static_cast< unsigned int >( Logger::LOG_DISABLE ) ); - for ( unsigned int level = 0; level <= Logger::LOGVERBOSE ; ++level ) + for ( unsigned int level = 0; level <= Logger::LOGVERBOSE; ++level ) { Logger::setupLogLevel( level ); QCOMPARE( Logger::logLevel(), level ); @@ -67,7 +68,9 @@ LibCalamaresTests::testLoadSaveYaml() QFile f( "settings.conf" ); // Find the nearest settings.conf to read for ( unsigned int up = 0; !f.exists() && ( up < 4 ); ++up ) + { f.setFileName( QString( "../" ) + f.fileName() ); + } cDebug() << QDir().absolutePath() << f.fileName() << f.exists(); QVERIFY( f.exists() ); @@ -75,7 +78,7 @@ LibCalamaresTests::testLoadSaveYaml() CalamaresUtils::saveYaml( "out.yaml", map ); auto other_map = CalamaresUtils::loadYaml( "out.yaml" ); - CalamaresUtils::saveYaml(" out2.yaml", other_map ); + CalamaresUtils::saveYaml( " out2.yaml", other_map ); QCOMPARE( map, other_map ); QFile::remove( "out.yaml" ); @@ -121,10 +124,7 @@ void LibCalamaresTests::testCommands() { using CalamaresUtils::System; - auto r = System::runCommand( - System::RunLocation::RunInHost, - { "/bin/ls", "/tmp" } - ); + auto r = System::runCommand( System::RunLocation::RunInHost, { "/bin/ls", "/tmp" } ); QVERIFY( r.getExitCode() == 0 ); @@ -136,25 +136,41 @@ LibCalamaresTests::testCommands() QVERIFY( !r.getOutput().contains( tfn.fileName() ) ); // Run ls again, now that the file exists - r = System::runCommand( - System::RunLocation::RunInHost, - { "/bin/ls", "/tmp" } - ); + r = System::runCommand( System::RunLocation::RunInHost, { "/bin/ls", "/tmp" } ); QVERIFY( r.getOutput().contains( tfn.fileName() ) ); // .. and without a working directory set, assume builddir != /tmp - r = System::runCommand( - System::RunLocation::RunInHost, - { "/bin/ls" } - ); + r = System::runCommand( System::RunLocation::RunInHost, { "/bin/ls" } ); QVERIFY( !r.getOutput().contains( tfn.fileName() ) ); - r = System::runCommand( - System::RunLocation::RunInHost, - { "/bin/ls" }, - "/tmp" - ); + r = System::runCommand( System::RunLocation::RunInHost, { "/bin/ls" }, "/tmp" ); QVERIFY( r.getOutput().contains( tfn.fileName() ) ); - - +} + +void +LibCalamaresTests::testUmask() +{ + struct stat mystat; + + QTemporaryFile ft; + QVERIFY( ft.open() ); + + mode_t m = CalamaresUtils::setUMask( 022 ); + QCOMPARE( CalamaresUtils::setUMask( m ), m ); + + for ( int i = 0; i <= 0777 /* octal! */; ++i ) + { + QByteArray name = ( ft.fileName() + QChar( '.' ) + QString::number( i, 8 ) ).toLatin1(); + CalamaresUtils::UMask um( i ); + int fd = creat( name, 0777 ); + QVERIFY( fd >= 0 ); + close( fd ); + QFileInfo fi( name ); + QVERIFY( fi.exists() ); + QCOMPARE( stat( name, &mystat ), 0 ); + QCOMPARE( mystat.st_mode & 0777, 0777 & ~i ); + QCOMPARE( unlink( name ), 0 ); + } + QCOMPARE( CalamaresUtils::setUMask( 022 ), m ); + QCOMPARE( CalamaresUtils::setUMask( m ), 022 ); } diff --git a/src/libcalamares/Tests.h b/src/libcalamares/utils/Tests.h similarity index 93% rename from src/libcalamares/Tests.h rename to src/libcalamares/utils/Tests.h index 5cdb3912b..d9140e0d0 100644 --- a/src/libcalamares/Tests.h +++ b/src/libcalamares/utils/Tests.h @@ -36,6 +36,9 @@ private Q_SLOTS: void testLoadSaveYamlExtended(); // Do a find() in the src dir void testCommands(); + + /** @brief Test that all the UMask objects work correctly. */ + void testUmask(); }; #endif diff --git a/src/libcalamares/utils/UMask.cpp b/src/libcalamares/utils/UMask.cpp new file mode 100644 index 000000000..358a52887 --- /dev/null +++ b/src/libcalamares/utils/UMask.cpp @@ -0,0 +1,44 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#include "UMask.h" + +#include +#include + +namespace CalamaresUtils +{ +mode_t +setUMask( mode_t u ) +{ + return umask( u ); +} + +UMask::UMask( mode_t u ) + : m_mode( setUMask( u ) ) +{ +} + +UMask::~UMask() +{ + setUMask( m_mode ); +} + +static_assert( UMask::Safe == ( S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH ), "Bad permissions." ); + +} // namespace CalamaresUtils diff --git a/src/libcalamares/utils/UMask.h b/src/libcalamares/utils/UMask.h new file mode 100644 index 000000000..58b1a56ad --- /dev/null +++ b/src/libcalamares/utils/UMask.h @@ -0,0 +1,55 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#ifndef UTILS_UMASK_H +#define UTILS_UMASK_H + +#include "DllMacro.h" + +#include + +namespace CalamaresUtils +{ +/// @brief Wrapper for umask(2) +DLLEXPORT mode_t setUMask( mode_t u ); + +/** @brief RAII for setting and re-setting umask. + * + * Create an object of this class to set the umask, + * and the umask is reset to its original value when + * the object goes out of scope. + */ +class DLLEXPORT UMask +{ +public: + UMask( mode_t u ); + ~UMask(); + + /** @brief a "safe" umask + * + * This umask will switch off group- and other- permissions for + * files, so that the file cannot be read, written, or executed + * except by the owner. + */ + static constexpr mode_t Safe = 077; // octal! +private: + mode_t m_mode; +}; +} // namespace CalamaresUtils + +#endif diff --git a/src/libcalamaresui/utils/DebugWindow.cpp b/src/libcalamaresui/utils/DebugWindow.cpp index 62ab504d3..ed016dd97 100644 --- a/src/libcalamaresui/utils/DebugWindow.cpp +++ b/src/libcalamaresui/utils/DebugWindow.cpp @@ -223,7 +223,7 @@ DebugWindow::DebugWindow() { for ( auto* w : qApp->topLevelWidgets() ) { - auto deb = cDebug(); + Logger::CDebug deb; dumpWidgetTree( deb, w, 0 ); } }); diff --git a/src/modules/initcpio/CMakeLists.txt b/src/modules/initcpio/CMakeLists.txt new file mode 100644 index 000000000..990220b10 --- /dev/null +++ b/src/modules/initcpio/CMakeLists.txt @@ -0,0 +1,26 @@ +calamares_add_plugin( initcpio + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + InitcpioJob.cpp + LINK_PRIVATE_LIBRARIES + calamares + SHARED_LIB +) + +if( ECM_FOUND AND BUILD_TESTING ) + ecm_add_test( + Tests.cpp + TEST_NAME + initcpiotest + LINK_LIBRARIES + ${CALAMARES_LIBRARIES} + calamares + calamares_job_initcpio # From above + ${YAMLCPP_LIBRARY} + Qt5::Core + Qt5::Test + ) + set_target_properties( initcpiotest PROPERTIES AUTOMOC TRUE ) + target_include_directories( initcpiotest PRIVATE /usr/local/include ) +endif() diff --git a/src/modules/initcpio/InitcpioJob.cpp b/src/modules/initcpio/InitcpioJob.cpp new file mode 100644 index 000000000..38017d83e --- /dev/null +++ b/src/modules/initcpio/InitcpioJob.cpp @@ -0,0 +1,108 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#include "InitcpioJob.h" + +#include "utils/CalamaresUtilsSystem.h" +#include "utils/Logger.h" +#include "utils/UMask.h" +#include "utils/Variant.h" + +#include +#include + +InitcpioJob::InitcpioJob( QObject* parent ) + : Calamares::CppJob( parent ) +{ +} + +InitcpioJob::~InitcpioJob() {} + + +QString +InitcpioJob::prettyName() const +{ + return tr( "Creating initramfs with mkinitcpio." ); +} + +void +fixPermissions( const QDir& d ) +{ + for ( const auto& fi : d.entryInfoList( { "initramfs*" }, QDir::Files ) ) + { + QFile f( fi.absoluteFilePath() ); + if ( f.exists() ) + { + cDebug() << "initcpio fixing permissions for" << f.fileName(); + f.setPermissions( QFileDevice::ReadOwner | QFileDevice::WriteOwner ); + } + } +} + +Calamares::JobResult +InitcpioJob::exec() +{ + CalamaresUtils::UMask m( CalamaresUtils::UMask::Safe ); + + if ( m_unsafe ) + { + cDebug() << "Skipping mitigations for unsafe initramfs permissions."; + } + else + { + QDir d( CalamaresUtils::System::instance()->targetPath( "/boot" ) ); + if ( d.exists() ) + { + fixPermissions( d ); + } + } + + cDebug() << "Updating initramfs with kernel" << m_kernel; + auto r = CalamaresUtils::System::instance()->targetEnvCommand( + { "mkinitcpio", "-p", m_kernel }, QString(), QString(), 0 ); + return r.explainProcess( "mkinitcpio", 10 ); +} + +void +InitcpioJob::setConfigurationMap( const QVariantMap& configurationMap ) +{ + m_kernel = CalamaresUtils::getString( configurationMap, "kernel" ); + if ( m_kernel.isEmpty() ) + { + m_kernel = QStringLiteral( "all" ); + } + else if ( m_kernel == "$uname" ) + { + auto r = CalamaresUtils::System::runCommand( + CalamaresUtils::System::RunLocation::RunInHost, { "/bin/uname", "-r" }, QString(), QString(), 3 ); + if ( r.getExitCode() == 0 ) + { + m_kernel = r.getOutput(); + cDebug() << "*initcpio* using running kernel" << m_kernel; + } + else + { + cWarning() << "*initcpio* could not determine running kernel, using 'all'." << Logger::Continuation + << r.getExitCode() << r.getOutput(); + } + } + + m_unsafe = CalamaresUtils::getBool( configurationMap, "be_unsafe", false ); +} + +CALAMARES_PLUGIN_FACTORY_DEFINITION( InitcpioJobFactory, registerPlugin< InitcpioJob >(); ) diff --git a/src/modules/initcpio/InitcpioJob.h b/src/modules/initcpio/InitcpioJob.h new file mode 100644 index 000000000..11358d749 --- /dev/null +++ b/src/modules/initcpio/InitcpioJob.h @@ -0,0 +1,50 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#ifndef INITCPIOJOB_H +#define INITCPIOJOB_H + +#include "CppJob.h" +#include "PluginDllMacro.h" +#include "utils/PluginFactory.h" + +#include +#include + +class PLUGINDLLEXPORT InitcpioJob : public Calamares::CppJob +{ + Q_OBJECT + +public: + explicit InitcpioJob( QObject* parent = nullptr ); + virtual ~InitcpioJob() override; + + QString prettyName() const override; + + Calamares::JobResult exec() override; + + void setConfigurationMap( const QVariantMap& configurationMap ) override; + +private: + QString m_kernel; + bool m_unsafe = false; +}; + +CALAMARES_PLUGIN_FACTORY_DECLARATION( InitcpioJobFactory ) + +#endif // INITCPIOJOB_H diff --git a/src/modules/initcpio/Tests.cpp b/src/modules/initcpio/Tests.cpp new file mode 100644 index 000000000..e0590ba25 --- /dev/null +++ b/src/modules/initcpio/Tests.cpp @@ -0,0 +1,59 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#include "Tests.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" + +#include "utils/Logger.h" +#include "utils/Yaml.h" + +#include + +#include +#include + +extern void fixPermissions( const QDir& d ); + +QTEST_GUILESS_MAIN( InitcpioTests ) + +InitcpioTests::InitcpioTests() +{ +} + +InitcpioTests::~InitcpioTests() +{ +} + +void +InitcpioTests::initTestCase() +{ +} + +void InitcpioTests::testFixPermissions() +{ + Logger::setupLogLevel( Logger::LOGDEBUG ); + cDebug() << "Fixing up /boot"; + fixPermissions( QDir( "/boot" ) ); + cDebug() << "Fixing up /nonexistent"; + fixPermissions( QDir( "/nonexistent/nonexistent" ) ); + QVERIFY( true ); +} + diff --git a/src/modules/initcpio/Tests.h b/src/modules/initcpio/Tests.h new file mode 100644 index 000000000..5bab26d3f --- /dev/null +++ b/src/modules/initcpio/Tests.h @@ -0,0 +1,36 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#ifndef TESTS_H +#define TESTS_H + +#include + +class InitcpioTests : public QObject +{ + Q_OBJECT +public: + InitcpioTests(); + ~InitcpioTests() override; + +private Q_SLOTS: + void initTestCase(); + void testFixPermissions(); +}; + +#endif diff --git a/src/modules/initcpio/initcpio.conf b/src/modules/initcpio/initcpio.conf index 466a8785d..8ad71e9f5 100644 --- a/src/modules/initcpio/initcpio.conf +++ b/src/modules/initcpio/initcpio.conf @@ -1,3 +1,23 @@ # Run mkinitcpio(8) with the given preset value --- +# There is only one configuration item for this module, +# the kernel to be loaded. This can have the following +# values: +# - empty or unset, interpreted as "all" +# - the literal string "$uname" (without quotes, with dollar), +# which will use the output of `uname -r` to determine the +# running kernel, and use that. +# - any other string. +# +# Whatever is set, that string is passed as *preset* argument to the +# `-p` option of *mkinitcpio*. Take care that both "$uname" operates +# in the host system, and might not be correct if the target system is +# updated (to a newer kernel) as part of the installation. +# +# Note that "all" is probably not a good preset to use either. kernel: linux312 + +# Set this to true to turn off mitigations for lax file +# permissions on initramfs (which, in turn, can compromise +# your LUKS encryption keys, CVS-2019-13179). +be_unsafe: false diff --git a/src/modules/initcpio/main.py b/src/modules/initcpio/main.py deleted file mode 100644 index 796f68721..000000000 --- a/src/modules/initcpio/main.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# === This file is part of Calamares - === -# -# Copyright 2014, Philip Müller -# Copyright 2019, Adriaan de Groot -# -# 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 . - -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 _("Creating initramfs with mkinitcpio.") - -def run(): - """ Calls routine to create kernel initramfs image. - - :return: - """ - from subprocess import CalledProcessError - - kernel = libcalamares.job.configuration['kernel'] - try: - check_target_env_call(['mkinitcpio', '-p', kernel]) - except CalledProcessError as e: - libcalamares.utils.warning(str(e)) - return ( _( "Process Failed" ), - _( "Process
mkinitcpio
failed with error code {!s}. The command was
{!s}
." ).format( e.returncode, e.cmd ) ) - - return None diff --git a/src/modules/initcpio/module.desc b/src/modules/initcpio/module.desc deleted file mode 100644 index f84fc43a8..000000000 --- a/src/modules/initcpio/module.desc +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: "job" -name: "initcpio" -interface: "python" -script: "main.py" diff --git a/src/modules/initramfs/CMakeLists.txt b/src/modules/initramfs/CMakeLists.txt index 79bb650c5..c2496b2a1 100644 --- a/src/modules/initramfs/CMakeLists.txt +++ b/src/modules/initramfs/CMakeLists.txt @@ -7,3 +7,20 @@ calamares_add_plugin( initramfs calamares SHARED_LIB ) + +if( ECM_FOUND AND BUILD_TESTING ) + ecm_add_test( + Tests.cpp + TEST_NAME + initramfstest + LINK_LIBRARIES + ${CALAMARES_LIBRARIES} + calamares + calamares_job_initramfs # From above + ${YAMLCPP_LIBRARY} + Qt5::Core + Qt5::Test + ) + set_target_properties( initramfstest PROPERTIES AUTOMOC TRUE ) + target_include_directories( initramfstest PRIVATE /usr/local/include ) +endif() diff --git a/src/modules/initramfs/InitramfsJob.cpp b/src/modules/initramfs/InitramfsJob.cpp index db9f8038a..01d400443 100644 --- a/src/modules/initramfs/InitramfsJob.cpp +++ b/src/modules/initramfs/InitramfsJob.cpp @@ -20,6 +20,7 @@ #include "utils/CalamaresUtilsSystem.h" #include "utils/Logger.h" +#include "utils/UMask.h" #include "utils/Variant.h" InitramfsJob::InitramfsJob( QObject* parent ) @@ -40,9 +41,29 @@ InitramfsJob::prettyName() const Calamares::JobResult InitramfsJob::exec() { + CalamaresUtils::UMask m( CalamaresUtils::UMask::Safe ); + cDebug() << "Updating initramfs with kernel" << m_kernel; + + if ( m_unsafe ) + { + cDebug() << "Skipping mitigations for unsafe initramfs permissions."; + } + else + { + // First make sure we generate a safe initramfs with suitable permissions. + static const char confFile[] = "/etc/initramfs-tools/conf.d/calamares-safe-initramfs.conf"; + static const char contents[] = "UMASK=0077\n"; + if ( CalamaresUtils::System::instance()->createTargetFile( confFile, QByteArray( contents ) ).isEmpty() ) + { + cWarning() << Logger::SubEntry << "Could not configure safe UMASK for initramfs."; + // But continue anyway. + } + } + + // And then do the ACTUAL work. auto r = CalamaresUtils::System::instance()->targetEnvCommand( - { "update-initramfs", "-k", m_kernel, "-c", "-t" }, QString(), QString(), 10 ); + { "update-initramfs", "-k", m_kernel, "-c", "-t" }, QString(), QString(), 0 ); return r.explainProcess( "update-initramfs", 10 ); } @@ -70,6 +91,8 @@ InitramfsJob::setConfigurationMap( const QVariantMap& configurationMap ) << r.getExitCode() << r.getOutput(); } } + + m_unsafe = CalamaresUtils::getBool( configurationMap, "be_unsafe", false ); } CALAMARES_PLUGIN_FACTORY_DEFINITION( InitramfsJobFactory, registerPlugin< InitramfsJob >(); ) diff --git a/src/modules/initramfs/InitramfsJob.h b/src/modules/initramfs/InitramfsJob.h index 63aed4136..9eeb81fea 100644 --- a/src/modules/initramfs/InitramfsJob.h +++ b/src/modules/initramfs/InitramfsJob.h @@ -42,6 +42,7 @@ public: private: QString m_kernel; + bool m_unsafe = false; }; CALAMARES_PLUGIN_FACTORY_DECLARATION( InitramfsJobFactory ) diff --git a/src/modules/initramfs/Tests.cpp b/src/modules/initramfs/Tests.cpp new file mode 100644 index 000000000..936c94097 --- /dev/null +++ b/src/modules/initramfs/Tests.cpp @@ -0,0 +1,96 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#include "Tests.h" + +#include "GlobalStorage.h" +#include "JobQueue.h" +#include "Settings.h" + +#include "utils/CalamaresUtilsSystem.h" +#include "utils/Logger.h" +#include "utils/Yaml.h" + +#include + +#include +#include + +QTEST_GUILESS_MAIN( InitramfsTests ) + +InitramfsTests::InitramfsTests() +{ +} + +InitramfsTests::~InitramfsTests() +{ +} + +void +InitramfsTests::initTestCase() +{ + Logger::setupLogLevel( Logger::LOGDEBUG ); +} + +static const char contents[] = "UMASK=0077\n"; +static const char confFile[] = "/tmp/calamares-safe-umask"; + +void InitramfsTests::cleanup() +{ + QFile::remove( confFile ); +} + +void InitramfsTests::testCreateHostFile() +{ + + CalamaresUtils::System s( false ); // don't chroot + QString path = s.createTargetFile( confFile, QByteArray( contents ) ); + QVERIFY( !path.isEmpty() ); + QCOMPARE( path, confFile ); // don't chroot, so path create relative to / + QVERIFY( QFile::exists( confFile ) ); + + QFileInfo fi( confFile ); + QVERIFY( fi.exists() ); + QCOMPARE( fi.size(), sizeof( contents )-1 ); // don't count trailing NUL + + QFile::remove( confFile ); +} + +void InitramfsTests::testCreateTargetFile() +{ + static const char short_confFile[] = "/calamares-safe-umask"; + + CalamaresUtils::System s( true ); + QString path = s.createTargetFile( short_confFile, QByteArray( contents ) ); + QVERIFY( path.isEmpty() ); // because no rootmountpoint is set + + Calamares::JobQueue j; + j.globalStorage()->insert( "rootMountPoint", "/tmp" ); + + path = s.createTargetFile( short_confFile, QByteArray( contents ) ); + QVERIFY( path.endsWith( short_confFile ) ); // chroot, so path create relative to + QVERIFY( path.startsWith( "/tmp/" ) ); + QVERIFY( QFile::exists( path ) ); + + QFileInfo fi( path ); + QVERIFY( fi.exists() ); + QCOMPARE( fi.size(), sizeof( contents )-1 ); // don't count trailing NUL + + QFile::remove( path ); + +} diff --git a/src/modules/initramfs/Tests.h b/src/modules/initramfs/Tests.h new file mode 100644 index 000000000..01394d4fa --- /dev/null +++ b/src/modules/initramfs/Tests.h @@ -0,0 +1,39 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#ifndef TESTS_H +#define TESTS_H + +#include + +class InitramfsTests : public QObject +{ + Q_OBJECT +public: + InitramfsTests(); + ~InitramfsTests() override; + +private Q_SLOTS: + void initTestCase(); + void cleanup(); + + void testCreateHostFile(); + void testCreateTargetFile(); +}; + +#endif diff --git a/src/modules/initramfs/initramfs.conf b/src/modules/initramfs/initramfs.conf index 4e5eda202..a989d83c3 100644 --- a/src/modules/initramfs/initramfs.conf +++ b/src/modules/initramfs/initramfs.conf @@ -29,3 +29,8 @@ # 3.2.9 and earlier which passed "all" as version. kernel: "all" + +# Set this to true to turn off mitigations for lax file +# permissions on initramfs (which, in turn, can compromise +# your LUKS encryption keys, CVS-2019-13179). +be_unsafe: false diff --git a/src/modules/luksbootkeyfile/CMakeLists.txt b/src/modules/luksbootkeyfile/CMakeLists.txt new file mode 100644 index 000000000..be0f0fa28 --- /dev/null +++ b/src/modules/luksbootkeyfile/CMakeLists.txt @@ -0,0 +1,9 @@ +calamares_add_plugin( luksbootkeyfile + TYPE job + EXPORT_MACRO PLUGINDLLEXPORT_PRO + SOURCES + LuksBootKeyFileJob.cpp + LINK_PRIVATE_LIBRARIES + calamares + SHARED_LIB +) diff --git a/src/modules/luksbootkeyfile/LuksBootKeyFileJob.cpp b/src/modules/luksbootkeyfile/LuksBootKeyFileJob.cpp new file mode 100644 index 000000000..292c768a9 --- /dev/null +++ b/src/modules/luksbootkeyfile/LuksBootKeyFileJob.cpp @@ -0,0 +1,214 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#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; +}; + +/** @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; +} + +struct LuksDeviceList +{ + LuksDeviceList( const QVariant& partitions ) + : valid( false ) + { + if ( partitions.canConvert< QVariantList >() ) + { + devices = getLuksDevices( partitions.toList() ); + valid = true; + } + } + + 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; + } + // Give ample time to check that the file was created correctly + r = CalamaresUtils::System::instance()->targetEnvCommand( { "ls", "-la", "/" } ); + cDebug() << "In target system after creating LUKS file" << r.getOutput(); + 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 ? "root" : "dev." ) << d.device << "passphrase?" + << !d.passphrase.isEmpty(); + } + + 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 >(); ) diff --git a/src/modules/luksbootkeyfile/LuksBootKeyFileJob.h b/src/modules/luksbootkeyfile/LuksBootKeyFileJob.h new file mode 100644 index 000000000..2d4d6d319 --- /dev/null +++ b/src/modules/luksbootkeyfile/LuksBootKeyFileJob.h @@ -0,0 +1,48 @@ +/* === This file is part of Calamares - === + * + * Copyright 2019, Adriaan de Groot + * + * 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 . + */ + +#ifndef LUKSBOOTKEYFILEJOB_H +#define LUKSBOOTKEYFILEJOB_H + +#include "CppJob.h" +#include "PluginDllMacro.h" +#include "utils/PluginFactory.h" + +#include +#include + +/** @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 diff --git a/src/modules/luksbootkeyfile/main.py b/src/modules/luksbootkeyfile/main.py deleted file mode 100644 index fb0146cf8..000000000 --- a/src/modules/luksbootkeyfile/main.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# === This file is part of Calamares - === -# -# Copyright 2016, Teo Mrnjavac -# Copyright 2017, Alf Gaida -# Copyright 2017, 2019, Adriaan de Groot -# -# 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 . - -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
{!s}
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 diff --git a/src/modules/luksbootkeyfile/module.desc b/src/modules/luksbootkeyfile/module.desc deleted file mode 100644 index 11a0173d5..000000000 --- a/src/modules/luksbootkeyfile/module.desc +++ /dev/null @@ -1,5 +0,0 @@ ---- -type: "job" -name: "luksbootkeyfile" -interface: "python" -script: "main.py"