From 1d4bb0f7e3e75a8f163ae97eb6f85cf2e2cbee19 Mon Sep 17 00:00:00 2001 From: Packit Service Date: Feb 04 2021 08:11:01 +0000 Subject: Prepare for a new update Reverting patches so we can apply the latest update and changes can be seen in the spec file and sources. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index ea9ef53..f3dac6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,12 @@ endif() # Local scanning tools option(SCAP_WORKBENCH_LOCAL_SCAN_ENABLED "If enabled, scanning of local machine is possible from workbench. Else the option is disabled in the GUI." TRUE) -if(${OPENSCAP_VERSION_MAJOR} LESS 2 AND ${OPENSCAP_VERSION_MINOR} LESS 3 AND ${OPENSCAP_VERSION_PATCH} LESS 16) # i.e. oscap<1.2.16 - message(FATAL_ERROR "Library-powered generation of result-based remediation roles is supported only if you have oscap>=1.2.16, whereas you have oscap==${OPENSCAP_VERSION}") +option(SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION "If enabled, result-based remediation roles will be generated by calls to the libopenscap library (instead of being generated by the oscap subprocess). Requires openscap>=1.2.16" FALSE) + +if (SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION) + if(${OPENSCAP_VERSION_MAJOR} LESS 2 AND ${OPENSCAP_VERSION_MINOR} LESS 3 AND ${OPENSCAP_VERSION_PATCH} LESS 16) # i.e. oscap<1.2.16 + message(FATAL_ERROR "Library-powered generation of result-based remediation roles is supported only if you have oscap>=1.2.16, whereas you have oscap==${OPENSCAP_VERSION}") + endif() endif() find_program(NICE_EXECUTABLE NAMES nice) # fully optional, local scan still available when missing diff --git a/doc/user_manual.adoc b/doc/user_manual.adoc index d97aa6f..1fffed7 100644 --- a/doc/user_manual.adoc +++ b/doc/user_manual.adoc @@ -524,6 +524,20 @@ Both while opening the files and when scanning. This option is discouraged and should only be used by content creators and/or people who really know what they are doing. +== Known issues + +=== Result-based remediations of tailored profiles + +Saving remediation roles to the disk may not work for a customized profile. Specifically, it won't work if you add additional rules to it. +If this limitation affects you, follow these steps: + +Remark: You will need to use the oscap command-line utility, which is bundled together with scap-workbench. + +1. Save the scan results +2. Save your profile customization to a file using the "File->Save customization only" option. +3. Run this command: oscap xccdf generate fix --output --result-id '' --tailoring-file . +Refer to oscap xccdf generate fix -h if you want other than Bash output. + == Where to Get Help? You ask for help with the application using diff --git a/include/Config.h.in b/include/Config.h.in index 0ef124f..58ba306 100644 --- a/include/Config.h.in +++ b/include/Config.h.in @@ -39,6 +39,7 @@ #define SCAP_WORKBENCH_LOCAL_PKEXEC_OSCAP_PATH "@CMAKE_INSTALL_FULL_LIBEXECDIR@/scap-workbench-pkexec-oscap.sh" #define SCAP_WORKBENCH_LOCAL_RPM_EXTRACT_PATH "@CMAKE_INSTALL_FULL_LIBEXECDIR@/scap-workbench-rpm-extract.sh" #define SCAP_WORKBENCH_REMOTE_OSCAP_PATH "oscap" +#cmakedefine SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION #cmakedefine SCAP_WORKBENCH_LOCAL_SSH_FOUND #define SCAP_WORKBENCH_LOCAL_SSH_PATH "@SSH_EXECUTABLE@" #cmakedefine SCAP_WORKBENCH_LOCAL_SETSID_FOUND diff --git a/include/Exceptions.h b/include/Exceptions.h index 1b87689..84bee42 100644 --- a/include/Exceptions.h +++ b/include/Exceptions.h @@ -57,6 +57,9 @@ SCAP_WORKBENCH_SIMPLE_EXCEPTION(SshConnectionException, SCAP_WORKBENCH_SIMPLE_EXCEPTION(TailoringWindowException, "There was a problem with TailoringWindow!\n"); +SCAP_WORKBENCH_SIMPLE_EXCEPTION(TemporaryDirException, + "There was a problem with TemporaryDir!\n"); + SCAP_WORKBENCH_SIMPLE_EXCEPTION(OscapScannerRemoteSshException, "There was a problem with OscapScannerRemoteSsh!\n"); diff --git a/include/ForwardDecls.h b/include/ForwardDecls.h index 3059a1c..b973547 100644 --- a/include/ForwardDecls.h +++ b/include/ForwardDecls.h @@ -50,6 +50,7 @@ class SyncProcess; class SSGIntegrationDialog; class TailoringWindow; class TailorProfileDialog; +class TemporaryDir; class XCCDFItemPropertiesDockWidget; class XCCDFItemSelectUndoCommand; class XCCDFValueChangeUndoCommand; diff --git a/include/OscapScannerLocal.h b/include/OscapScannerLocal.h index 00b5ab3..4370744 100644 --- a/include/OscapScannerLocal.h +++ b/include/OscapScannerLocal.h @@ -22,9 +22,10 @@ #ifndef SCAP_WORKBENCH_OSCAP_SCANNER_LOCAL_H_ #define SCAP_WORKBENCH_OSCAP_SCANNER_LOCAL_H_ +#include + #include "ForwardDecls.h" #include "OscapScannerBase.h" -#include "Utils.h" class OscapScannerLocal : public OscapScannerBase @@ -53,7 +54,7 @@ class OscapScannerLocal : public OscapScannerBase void evaluateWithOfflineRemediation(); void evaluateWithOtherSettings(); - static void setFilenameToTempFile(SpacelessQTemporaryFile& file); + static void setFilenameToTempFile(QTemporaryFile& file); }; #endif diff --git a/include/RPMOpenHelper.h b/include/RPMOpenHelper.h index b6bd606..18d1bad 100644 --- a/include/RPMOpenHelper.h +++ b/include/RPMOpenHelper.h @@ -23,7 +23,7 @@ #define SCAP_WORKBENCH_RPM_OPEN_HELPER_H_ #include "ForwardDecls.h" -#include "Utils.h" +#include "TemporaryDir.h" #include /** @@ -66,7 +66,7 @@ class RPMOpenHelper private: static QString getRPMExtractPath(); - SpacelessQTemporaryDir mTempDir; + TemporaryDir mTempDir; QString mInputPath; QString mTailoringPath; diff --git a/include/RemediationRoleSaver.h b/include/RemediationRoleSaver.h index 95938db..c668739 100644 --- a/include/RemediationRoleSaver.h +++ b/include/RemediationRoleSaver.h @@ -93,38 +93,76 @@ class PuppetProfileRemediationSaver : public ProfileBasedRemediationSaver }; +#ifndef SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION +/// Base for all result-based remediation generators that uses oscap process +class ResultBasedProcessRemediationSaver : public RemediationSaverBase +{ + public: + ResultBasedProcessRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, + const QString& saveMessage, const QString& filetypeExtension, const QString& filetypeTemplate, const QString& fixType); + + private: + virtual void saveToFile(const QString& filename); + QTemporaryFile mArfFile; +}; + + +class BashResultRemediationSaver : public ResultBasedProcessRemediationSaver +{ + public: + BashResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents); +}; + + +class AnsibleResultRemediationSaver : public ResultBasedProcessRemediationSaver +{ + public: + AnsibleResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents); +}; + + +class PuppetResultRemediationSaver : public ResultBasedProcessRemediationSaver +{ + public: + PuppetResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents); +}; + +#else // i.e. SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION is defined + +/// Base for all result-based remediation generators that uses the openscap library class ResultBasedLibraryRemediationSaver : public RemediationSaverBase { public: - ResultBasedLibraryRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath, + ResultBasedLibraryRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& saveMessage, const QString& filetypeExtension, const QString& filetypeTemplate, const QString& fixType); private: virtual void saveToFile(const QString& filename); - SpacelessQTemporaryFile mArfFile; - QString tailoring; + QTemporaryFile mArfFile; }; class BashResultRemediationSaver : public ResultBasedLibraryRemediationSaver { public: - BashResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath); + BashResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents); }; class AnsibleResultRemediationSaver : public ResultBasedLibraryRemediationSaver { public: - AnsibleResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath); + AnsibleResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents); }; class PuppetResultRemediationSaver : public ResultBasedLibraryRemediationSaver { public: - PuppetResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath); + PuppetResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents); }; +#endif // SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION + #endif // SCAP_WORKBENCH_REMEDIATION_ROLE_SAVER_H_ diff --git a/include/RemoteSsh.h b/include/RemoteSsh.h index 8ae8ba7..7971a0e 100644 --- a/include/RemoteSsh.h +++ b/include/RemoteSsh.h @@ -24,7 +24,7 @@ #include "ForwardDecls.h" #include "ProcessHelpers.h" -#include "Utils.h" +#include "TemporaryDir.h" #include class SshConnection : public QObject @@ -57,7 +57,7 @@ class SshConnection : public QObject QString mTarget; unsigned short mPort; - SpacelessQTemporaryDir* mSocketDir; + TemporaryDir* mSocketDir; QString mMasterSocket; QProcessEnvironment mEnvironment; diff --git a/include/ResultViewer.h b/include/ResultViewer.h index 2ec8d57..978a23a 100644 --- a/include/ResultViewer.h +++ b/include/ResultViewer.h @@ -23,9 +23,9 @@ #define SCAP_WORKBENCH_RESULT_VIEWER_H_ #include "ForwardDecls.h" -#include "Utils.h" #include +#include #include #include #include @@ -97,10 +97,8 @@ class ResultViewer : public QWidget QByteArray mResults; QByteArray mReport; /// If user requests to open the file via desktop services - SpacelessQTemporaryFile* mReportFile; + QTemporaryFile* mReportFile; QByteArray mARF; - - QString tailoringFilePath; }; #endif diff --git a/include/ScanningSession.h b/include/ScanningSession.h index 25ccc60..c1d2b49 100644 --- a/include/ScanningSession.h +++ b/include/ScanningSession.h @@ -23,8 +23,9 @@ #define SCAP_WORKBENCH_SCANNING_SESSION_H_ #include "ForwardDecls.h" -#include "Utils.h" +#include +#include #include #include #include @@ -291,7 +292,7 @@ class ScanningSession mutable struct xccdf_tailoring* mTailoring; /// Temporary copy of opened DS or XCCDF file - SpacelessQTemporaryDir* mTempOpenDir; + QTemporaryDir* mTempOpenDir; /// Path to temporary DS or XCCDF file QString mTempOpenPath; /// Path to original DS or XCCDF file @@ -300,9 +301,9 @@ class ScanningSession QSet mClosureOfOriginalFile; /// Temporary file provides auto deletion and a valid temp file path - SpacelessQTemporaryFile mTailoringFile; + QTemporaryFile mTailoringFile; /// Temporary file provides auto deletion and a valid temp file path - SpacelessQTemporaryFile mGuideFile; + QTemporaryFile mGuideFile; /// Whether or not validation should be skipped bool mSkipValid; diff --git a/include/TemporaryDir.h b/include/TemporaryDir.h new file mode 100644 index 0000000..fadabec --- /dev/null +++ b/include/TemporaryDir.h @@ -0,0 +1,74 @@ +/* + * Copyright 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * 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 3 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 . + * + * Authors: + * Martin Preisler + */ + +#ifndef SCAP_WORKBENCH_TEMPORARY_DIR_H_ +#define SCAP_WORKBENCH_TEMPORARY_DIR_H_ + +#include "ForwardDecls.h" +#include + +/** + * @brief Creates a (LOCAL!) temporary directory and auto destroys it if told so + * + * This structure is lazy, it only creates the temp directory when asked about + * its path. Before you query the path the directory won't be created. + * + * @note Default setting is to auto-remove the directory on destruction. + * @internal We should replace this with QTemporaryDir when scap-workbench moves to Qt5 + */ +class TemporaryDir +{ + public: + TemporaryDir(); + ~TemporaryDir(); + + /** + * @brief Changes the auto-remove settings + * + * If autoRemove is true the structure will recursively remove the entire + * temporary directory (that is the default setting). Else it will just + * create it and it's up to the user to destroy it. + */ + void setAutoRemove(const bool autoRemove); + + /// @see TemporaryDir::setAutoRemove + bool getAutoRemove() const; + + /** + * @brief Returns absolute path of created temporary directory + * + * @exception TemporaryDirException Failed to create temporary directory (nonzero exit code from mktemp -d) + */ + const QString& getPath() const; + + private: + /** + * Ensures that temporary directory has been created and the stored path is valid. + */ + void ensurePath() const; + + /// Holds absolute path of the created temporary directory + mutable QString mPath; + /// @see TemporaryDir::setAutoRemove + bool mAutoRemove; +}; + +#endif diff --git a/include/Utils.h b/include/Utils.h index fb9c150..c3b6f01 100644 --- a/include/Utils.h +++ b/include/Utils.h @@ -28,8 +28,6 @@ #include #include #include -#include -#include /** * @brief Retrieves QDir representing the share directory @@ -117,14 +115,4 @@ void openUrlGuarded(const QUrl& url); */ const QString& getSetSidPath(); -class SpacelessQTemporaryFile: public QTemporaryFile { - public: - SpacelessQTemporaryFile (); -}; - -class SpacelessQTemporaryDir: public QTemporaryDir { - public: - SpacelessQTemporaryDir (); -}; - #endif diff --git a/scap-workbench-oscap.sh b/scap-workbench-oscap.sh index 9066444..216376f 100755 --- a/scap-workbench-oscap.sh +++ b/scap-workbench-oscap.sh @@ -93,7 +93,7 @@ function chown_copy # chown only required if wrapper_{uid,gid} differs from real_{uid,gid} if [ $wrapper_uid -ne $real_uid ] || [ $wrapper_gid -ne $real_gid ]; then - chown $wrapper_uid:$wrapper_gid "$where" + chown $wrapper_uid:$wrapper_gid $where fi } diff --git a/src/OscapCapabilities.cpp b/src/OscapCapabilities.cpp index c85804f..03099fb 100644 --- a/src/OscapCapabilities.cpp +++ b/src/OscapCapabilities.cpp @@ -50,10 +50,8 @@ void OscapCapabilities::clear() static bool versionGreaterOrEqual(const QString& a, const QString& b) { - // the first split chops off any suffix such as _alpha2 - // we have a guarantee that _ will occur at most once in the string - const QStringList aSplit = a.split("_")[0].split('.'); - const QStringList bSplit = b.split("_")[0].split('.'); + const QStringList aSplit = a.split('.'); + const QStringList bSplit = b.split('.'); // we only compare versions of the same number of components! assert(aSplit.size() == bSplit.size()); @@ -91,11 +89,7 @@ void OscapCapabilities::parse(const QString& mmv) const QStringList firstLine = lines[0].split(' ', QString::SkipEmptyParts); const QString& versionCandidate = firstLine.last(); - // Examples: - // 1.3.0_alpha2 - // 0.8.0 - // 1.2.18 - if (!versionCandidate.contains(QRegExp("^([0-9]+\\.){2,}[0-9]+(_[a-z0-9]+)?$"))) + if (!versionCandidate.contains(QRegExp("^([0-9]+\\.){2,}[0-9]+$"))) return; // TODO: Throw exception? mVersion = versionCandidate; diff --git a/src/OscapScannerBase.cpp b/src/OscapScannerBase.cpp index daf08b9..3d4075c 100644 --- a/src/OscapScannerBase.cpp +++ b/src/OscapScannerBase.cpp @@ -24,6 +24,7 @@ #include #include +#include #include extern "C" diff --git a/src/OscapScannerLocal.cpp b/src/OscapScannerLocal.cpp index dd9891f..48687f2 100644 --- a/src/OscapScannerLocal.cpp +++ b/src/OscapScannerLocal.cpp @@ -22,6 +22,7 @@ #include "OscapScannerLocal.h" #include "ProcessHelpers.h" #include "ScanningSession.h" +#include "TemporaryDir.h" #include #include @@ -32,7 +33,7 @@ extern "C" #include } -void OscapScannerLocal::setFilenameToTempFile(SpacelessQTemporaryFile& file) +void OscapScannerLocal::setFilenameToTempFile(QTemporaryFile& file) { file.open(); file.close(); @@ -96,21 +97,21 @@ void OscapScannerLocal::evaluate() // This is mainly for check-engine-results and oval-results, to ensure // we get a full report, including info from these files. openscap's XSLT // uses info in the check engine results if it can find them. - SpacelessQTemporaryDir workingDir; - process.setWorkingDirectory(workingDir.path()); + TemporaryDir workingDir; + process.setWorkingDirectory(workingDir.getPath()); QStringList args; - SpacelessQTemporaryFile inputARFFile; + QTemporaryFile inputARFFile; - SpacelessQTemporaryFile arfFile; + QTemporaryFile arfFile; arfFile.setAutoRemove(true); setFilenameToTempFile(arfFile); - SpacelessQTemporaryFile reportFile; + QTemporaryFile reportFile; reportFile.setAutoRemove(true); setFilenameToTempFile(reportFile); - SpacelessQTemporaryFile resultFile; + QTemporaryFile resultFile; resultFile.setAutoRemove(true); setFilenameToTempFile(resultFile); @@ -222,7 +223,7 @@ QStringList OscapScannerLocal::getCommandLineArgs() const if (mScannerMode == SM_OFFLINE_REMEDIATION) { - SpacelessQTemporaryFile inputARFFile; + QTemporaryFile inputARFFile; inputARFFile.setAutoRemove(true); inputARFFile.open(); inputARFFile.write(getARFForRemediation()); diff --git a/src/OscapScannerRemoteSsh.cpp b/src/OscapScannerRemoteSsh.cpp index 75122df..760eed0 100644 --- a/src/OscapScannerRemoteSsh.cpp +++ b/src/OscapScannerRemoteSsh.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -104,7 +105,7 @@ QStringList OscapScannerRemoteSsh::getCommandLineArgs() const if (mScannerMode == SM_OFFLINE_REMEDIATION) { - SpacelessQTemporaryFile inputARFFile; + QTemporaryFile inputARFFile; inputARFFile.setAutoRemove(true); inputARFFile.open(); inputARFFile.write(getARFForRemediation()); @@ -380,7 +381,7 @@ QString OscapScannerRemoteSsh::copyInputFileOver() QString localPath = ""; - SpacelessQTemporaryFile inputARFFile; + QTemporaryFile inputARFFile; inputARFFile.setAutoRemove(true); if (mScannerMode == SM_OFFLINE_REMEDIATION) { diff --git a/src/RPMOpenHelper.cpp b/src/RPMOpenHelper.cpp index b12f109..565eaa1 100644 --- a/src/RPMOpenHelper.cpp +++ b/src/RPMOpenHelper.cpp @@ -33,12 +33,12 @@ RPMOpenHelper::RPMOpenHelper(const QString& path) const QFileInfo pathInfo(path); proc.setCommand(getRPMExtractPath()); proc.setArguments(QStringList(pathInfo.absoluteFilePath())); - proc.setWorkingDirectory(mTempDir.path()); + proc.setWorkingDirectory(mTempDir.getPath()); } proc.run(); - const QDir tempDir(mTempDir.path()); + const QDir tempDir(mTempDir.getPath()); if (proc.getExitCode() != 0) { diff --git a/src/RemediationRoleSaver.cpp b/src/RemediationRoleSaver.cpp index a986673..247475e 100644 --- a/src/RemediationRoleSaver.cpp +++ b/src/RemediationRoleSaver.cpp @@ -27,7 +27,6 @@ #include "RemediationRoleSaver.h" #include "DiagnosticsDialog.h" -#include "Utils.h" extern "C" { @@ -35,9 +34,14 @@ extern "C" #include #include #include +#ifdef SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION + // vvv This include is used only for library-based generation of result-base remediation roles + // vvv and it requires (relatively recent) openscap 1.2.16 #include +#endif } +#include "TemporaryDir.h" const QString bashSaveMessage = QObject::tr("Save remediation role as a bash script"); @@ -159,8 +163,82 @@ PuppetProfileRemediationSaver::PuppetProfileRemediationSaver(QWidget* parentWind puppetSaveMessage, puppetFiletypeExtension, puppetFiletypeTemplate, puppetFixType) {} -ResultBasedLibraryRemediationSaver::ResultBasedLibraryRemediationSaver( - QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath, +#ifndef SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION +ResultBasedProcessRemediationSaver::ResultBasedProcessRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, + const QString& saveMessage, const QString& filetypeExtension, const QString& filetypeTemplate, const QString& fixType): + RemediationSaverBase(parentWindow, saveMessage, filetypeExtension, filetypeTemplate, fixType) +{ + mArfFile.setAutoRemove(true); + mArfFile.open(); + mArfFile.write(arfContents); + mArfFile.close(); +} + +void ResultBasedProcessRemediationSaver::saveToFile(const QString& filename) +{ + QStringList args; + args.append("xccdf"); + args.append("generate"); + args.append("fix"); + + args.append("--template"); + args.append(mTemplateString); + args.append("--output"); + args.append(filename); + + // vvv This will work, if there is only one result ID in the ARF file, it will be picked no matter what the argument value is. + // However, ommitting --result-id "" won't work. + args.append("--result-id"); + args.append(""); + + args.append(mArfFile.fileName()); + + // Launching a process and going through its output is something we do already in OscapScannerLocal::evaluate() + // This is a lightweight launch though. + QProcess process(mParentWindow); + + TemporaryDir workingDir; + process.setWorkingDirectory(workingDir.getPath()); + QString program(SCAP_WORKBENCH_LOCAL_OSCAP_PATH); + + process.start(program, args); + process.waitForStarted(); + + const unsigned int remediationGenerationTimeout = 10000; + + const int process_finished_on_time = process.waitForFinished(remediationGenerationTimeout); + + if (!process_finished_on_time) + { + QString message = QObject::tr("The process that was supposed to generate remediations didn't finish on time (i.e. within %1 secs), so it was terminated.").arg(remediationGenerationTimeout / 1000); + process.kill(); + throw std::runtime_error(message.toUtf8().constData()); + } + + if (process.exitCode() != 0) + { + QString completeErrorMessage(QObject::tr("Exit code of 'oscap' was %1: %2")); + throw std::runtime_error(completeErrorMessage.arg(process.exitCode()).arg(QString(process.readAllStandardError())).toUtf8().constData()); + } +} + +BashResultRemediationSaver::BashResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents): + ResultBasedProcessRemediationSaver(parentWindow, arfContents, + bashSaveMessage, bashFiletypeExtension, bashFiletypeTemplate, bashFixTemplate) +{} + +AnsibleResultRemediationSaver::AnsibleResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents): + ResultBasedProcessRemediationSaver(parentWindow, arfContents, + ansibleSaveMessage, ansibleFiletypeExtension, ansibleFiletypeTemplate, ansibleFixType) +{} + +PuppetResultRemediationSaver::PuppetResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents): + ResultBasedProcessRemediationSaver(parentWindow, arfContents, + puppetSaveMessage, puppetFiletypeExtension, puppetFiletypeTemplate, puppetFixType) +{} + +#else // i.e. SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION is defined +ResultBasedLibraryRemediationSaver::ResultBasedLibraryRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& saveMessage, const QString& filetypeExtension, const QString& filetypeTemplate, const QString& fixType): RemediationSaverBase(parentWindow, saveMessage, filetypeExtension, filetypeTemplate, fixType) { @@ -168,7 +246,6 @@ ResultBasedLibraryRemediationSaver::ResultBasedLibraryRemediationSaver( mArfFile.open(); mArfFile.write(arfContents); mArfFile.close(); - tailoring = tailoringFilePath; } void ResultBasedLibraryRemediationSaver::saveToFile(const QString& filename) @@ -205,9 +282,6 @@ void ResultBasedLibraryRemediationSaver::saveToFile(const QString& filename) if (session == NULL) throw std::runtime_error("Couldn't get XCCDF session from the report source"); - if (!tailoring.isNull()) { - xccdf_session_set_user_tailoring_file(session, tailoring.toUtf8().constData()); - } xccdf_session_set_loading_flags(session, XCCDF_SESSION_LOAD_XCCDF); if (xccdf_session_load(session) != 0) @@ -242,18 +316,19 @@ void ResultBasedLibraryRemediationSaver::saveToFile(const QString& filename) } } -BashResultRemediationSaver::BashResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath): - ResultBasedLibraryRemediationSaver(parentWindow, arfContents, tailoringFilePath, +BashResultRemediationSaver::BashResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents): + ResultBasedLibraryRemediationSaver(parentWindow, arfContents, bashSaveMessage, bashFiletypeExtension, bashFiletypeTemplate, bashFixTemplate) {} -AnsibleResultRemediationSaver::AnsibleResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath): - ResultBasedLibraryRemediationSaver(parentWindow, arfContents, tailoringFilePath, +AnsibleResultRemediationSaver::AnsibleResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents): + ResultBasedLibraryRemediationSaver(parentWindow, arfContents, ansibleSaveMessage, ansibleFiletypeExtension, ansibleFiletypeTemplate, ansibleFixType) {} -PuppetResultRemediationSaver::PuppetResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents, const QString& tailoringFilePath): - ResultBasedLibraryRemediationSaver(parentWindow, arfContents, tailoringFilePath, +PuppetResultRemediationSaver::PuppetResultRemediationSaver(QWidget* parentWindow, const QByteArray& arfContents): + ResultBasedLibraryRemediationSaver(parentWindow, arfContents, puppetSaveMessage, puppetFiletypeExtension, puppetFiletypeTemplate, puppetFixType) {} +#endif // SCAP_WORKBENCH_USE_LIBRARY_FOR_RESULT_BASED_REMEDIATION_ROLES_GENERATION diff --git a/src/RemoteSsh.cpp b/src/RemoteSsh.cpp index 9f737b8..36c359f 100644 --- a/src/RemoteSsh.cpp +++ b/src/RemoteSsh.cpp @@ -109,8 +109,8 @@ void SshConnection::connect() mSocketDir = 0; } - mSocketDir = new SpacelessQTemporaryDir(); - mMasterSocket = mSocketDir->path() + "/ssh_socket"; + mSocketDir = new TemporaryDir(); + mMasterSocket = mSocketDir->getPath() + "/ssh_socket"; } catch (const SyncProcessException& e) { diff --git a/src/ResultViewer.cpp b/src/ResultViewer.cpp index c544474..c65be2d 100644 --- a/src/ResultViewer.cpp +++ b/src/ResultViewer.cpp @@ -114,9 +114,6 @@ void ResultViewer::loadContent(Scanner* scanner) if (mInputBaseName.endsWith("-xccdf")) mInputBaseName.chop(QString("-xccdf").length()); } - if (session->isSelectedProfileTailoring()) { - tailoringFilePath = session->getTailoringFilePath(); - } mReport.clear(); scanner->getReport(mReport); @@ -161,7 +158,7 @@ void ResultViewer::openReport() mReportFile = 0; } - mReportFile = new SpacelessQTemporaryFile(); + mReportFile = new QTemporaryFile(); mReportFile->setFileTemplate(mReportFile->fileTemplate() + ".html"); mReportFile->open(); mReportFile->write(mReport); @@ -176,19 +173,19 @@ void ResultViewer::openReport() void ResultViewer::generateBashRemediationRole() { - BashResultRemediationSaver remediation(this, mARF, tailoringFilePath); + BashResultRemediationSaver remediation(this, mARF); remediation.selectFilenameAndSaveRole(); } void ResultViewer::generateAnsibleRemediationRole() { - AnsibleResultRemediationSaver remediation(this, mARF, tailoringFilePath); + AnsibleResultRemediationSaver remediation(this, mARF); remediation.selectFilenameAndSaveRole(); } void ResultViewer::generatePuppetRemediationRole() { - PuppetResultRemediationSaver remediation(this, mARF, tailoringFilePath); + PuppetResultRemediationSaver remediation(this, mARF); remediation.selectFilenameAndSaveRole(); } diff --git a/src/SaveAsRPMDialog.cpp b/src/SaveAsRPMDialog.cpp index 611d15f..6c53744 100644 --- a/src/SaveAsRPMDialog.cpp +++ b/src/SaveAsRPMDialog.cpp @@ -21,6 +21,7 @@ #include "SaveAsRPMDialog.h" #include "MainWindow.h" +#include "TemporaryDir.h" #include "ScanningSession.h" #include "ProcessHelpers.h" @@ -127,7 +128,7 @@ void SaveAsRPMDialog::slotFinished(int result) args.append(cwd.relativeFilePath(*it)); } - SpacelessQTemporaryDir tailoringDir; + TemporaryDir tailoringDir; // Tailoring file is a special case since it may be in memory only. // In case it is memory only we don't want it to cause our common ancestor dir to be / @@ -137,7 +138,7 @@ void SaveAsRPMDialog::slotFinished(int result) QFileInfo tailoringFile(mScanningSession->getTailoringFilePath()); assert(tailoringFile.exists()); - const QString tailoringFilePath = QString("%1/%2").arg(tailoringDir.path(), "tailoring-xccdf.xml"); + const QString tailoringFilePath = QString("%1/%2").arg(tailoringDir.getPath(), "tailoring-xccdf.xml"); ScanningSession::copyOrReplace(tailoringFile.absoluteFilePath(), tailoringFilePath); diff --git a/src/ScanningSession.cpp b/src/ScanningSession.cpp index 20ce501..a486761 100644 --- a/src/ScanningSession.cpp +++ b/src/ScanningSession.cpp @@ -34,6 +34,8 @@ extern "C" { #include #include +#include +#include #include #include #include @@ -159,7 +161,7 @@ void ScanningSession::cloneToTemporaryFile(const QString& path) // Clean the temporary directory if it is open already, then create // a new one. cleanTmpDir(); - mTempOpenDir = new SpacelessQTemporaryDir(); + mTempOpenDir = new QTemporaryDir(); // Recalling is unlikely to succeed, so throw a fatal exception if (!mTempOpenDir->isValid()) diff --git a/src/TemporaryDir.cpp b/src/TemporaryDir.cpp new file mode 100644 index 0000000..0fed255 --- /dev/null +++ b/src/TemporaryDir.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2013 Red Hat Inc., Durham, North Carolina. + * All Rights Reserved. + * + * 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 3 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 . + * + * Authors: + * Martin Preisler + */ + +#include "TemporaryDir.h" +#include "ProcessHelpers.h" +#include "Exceptions.h" + +#include +#include + +static bool recursiveRemoveDir(const QString& dirName) +{ + // Adapted code from: + // http://john.nachtimwald.com/2010/06/08/qt-remove-directory-and-its-contents/ + + bool result = true; + QDir dir(dirName); + + if (dir.exists(dirName)) + { + for (QFileInfo info : dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) + { + if (info.isDir()) + result = recursiveRemoveDir(info.absoluteFilePath()); + else + result = QFile::remove(info.absoluteFilePath()); + + if (!result) + return result; + } + result = dir.rmdir(dirName); + } + + return result; +} + +TemporaryDir::TemporaryDir(): + mAutoRemove(true) +{} + +TemporaryDir::~TemporaryDir() +{ + if (!mPath.isEmpty() && mAutoRemove) + { + if (!recursiveRemoveDir(mPath)) + { + // We don't throw on destruction! The worst thing that can happen + // is leftover files which is not a big deal anyway. + + std::cerr << "Failed to remove temporary directory '" << mPath.toUtf8().constData() << "'." << std::endl; + } + } +} + +void TemporaryDir::setAutoRemove(const bool autoRemove) +{ + mAutoRemove = autoRemove; +} + +bool TemporaryDir::getAutoRemove() const +{ + return mAutoRemove; +} + +const QString& TemporaryDir::getPath() const +{ + ensurePath(); + return mPath; +} + +// nextRand adapted from from QTemporaryDir from Qt5, licensed under LGPL2.1+ + +// Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +// Contact: http://www.qt-project.org/legal +// +// GNU Lesser General Public License Usage +// Alternatively, this file may be used under the terms of the GNU Lesser +// General Public License version 2.1 or version 3 as published by the Free +// Software Foundation and appearing in the file LICENSE.LGPLv21 and +// LICENSE.LGPLv3 included in the packaging of this file. Please review the +// following information to ensure the GNU Lesser General Public License +// requirements will be met: https://www.gnu.org/licenses/lgpl.html and +// http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +// +// In addition, as a special exception, Digia gives you certain additional +// rights. These rights are described in the Digia Qt LGPL Exception +// version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + +static int nextRand(int& v) +{ + int r = v % 62; + v /= 62; + if (v < 62) + v = qrand(); + return r; +} + +void TemporaryDir::ensurePath() const +{ + static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + + if (mPath.isEmpty()) + { + QString dirName; + while (true) + { + dirName = ""; + + int v = qrand(); + dirName += letters[nextRand(v)]; + dirName += letters[nextRand(v)]; + dirName += letters[nextRand(v)]; + dirName += letters[nextRand(v)]; + dirName += letters[nextRand(v)]; + dirName += letters[nextRand(v)]; + + if (QDir::temp().mkdir(dirName)) + break; + } + + const QDir dir(QDir::temp().absoluteFilePath(dirName)); + + if (!dir.exists()) + throw TemporaryDirException( + QString("Failed to create temporary directory. mkdir succeeded " + "but the directory does not exist!") + ); + + mPath = dir.absolutePath(); + } +} diff --git a/src/Utils.cpp b/src/Utils.cpp index fbeb809..802cd50 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -217,9 +217,3 @@ const QString& getSetSidPath() return ret; #endif } - -SpacelessQTemporaryFile::SpacelessQTemporaryFile () : - QTemporaryFile(QDir::cleanPath(QDir::tempPath() + "/" + "scap-workbench-XXXXXX")) {} - -SpacelessQTemporaryDir::SpacelessQTemporaryDir () : - QTemporaryDir(QDir::cleanPath(QDir::tempPath() + "/" + "scap-workbench-XXXXXX")) {} diff --git a/src/main.cpp b/src/main.cpp index 5f6480a..ebc7b4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,9 @@ int main(int argc, char** argv) FreeConsole(); #endif + // Needed for TemporaryDir + qsrand(QTime::currentTime().msec()); + Application app(argc, argv); return app.exec(); } diff --git a/ui/SSGIntegrationDialog.ui b/ui/SSGIntegrationDialog.ui index 519773f..2e61374 100644 --- a/ui/SSGIntegrationDialog.ui +++ b/ui/SSGIntegrationDialog.ui @@ -7,7 +7,7 @@ 0 0 889 - 330 + 288 @@ -28,9 +28,6 @@ - - 12 - @@ -55,19 +52,15 @@ - - QLayout::SetMinimumSize - 0 - - - 0 - 0 - + + + 11 + <html><head/><body><p>SCAP Security Guide was found installed on this machine.</p><p>The content provided by SCAP Security Guide allows you to quickly scan your machine according to well stablished security baselines.</p><p>Also, these guides are a good starting point if you'd like to customize a policy or profile for your own needs.</p><p>Select one of the default guides to load, or select Other SCAP Content option to load your own content.</p></body></html> @@ -118,13 +111,10 @@ Qt::Vertical - - QSizePolicy::MinimumExpanding - 20 - 20 + 40 @@ -167,6 +157,24 @@ + + + + + 0 + 0 + + + + + 6 + + + 0 + + + +