From 570fcbf0970a930b237e90b77e050d2a2939dab3 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Thu, 13 Aug 2026 22:00:47 +0300 Subject: [PATCH] Harden application log file handling IB-9045 Signed-off-by: Raul Metsma --- client/Application.cpp | 53 ++++++++++++++++++++++--------- client/CDocSupport.cpp | 4 +-- client/CryptoDoc.cpp | 5 +-- client/QCNG.cpp | 2 +- client/QCryptoBackend.cpp | 2 +- client/QPCSC.cpp | 6 ++-- client/QSmartCard.cpp | 2 +- client/dialogs/FileDialog.cpp | 35 ++++++++++++++++++++ client/dialogs/FileDialog.h | 2 ++ client/dialogs/SettingsDialog.cpp | 12 +++---- client/signer/MobileProgress.cpp | 5 +-- client/signer/SmartIDProgress.cpp | 6 ++-- 12 files changed, 90 insertions(+), 44 deletions(-) diff --git a/client/Application.cpp b/client/Application.cpp index 69ec96d52..ee4f8c360 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -34,6 +34,7 @@ class MacMenuBar {}; #endif #include "TokenData.h" #include "Utils.h" +#include "dialogs/FileDialog.h" #include "dialogs/FirstRun.h" #include "dialogs/SettingsDialog.h" #include "dialogs/WaitDialog.h" @@ -47,11 +48,13 @@ class MacMenuBar {}; #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -81,6 +84,12 @@ class MacMenuBar {}; using namespace std::chrono; +static QFile& applicationLog() +{ + static QFile file; + return file; +} + const QStringList Application::CONTAINER_EXT { QStringLiteral("asice"), QStringLiteral("sce"), QStringLiteral("asics"), QStringLiteral("scs"), @@ -90,10 +99,9 @@ const QStringList Application::CONTAINER_EXT { class DigidocConf final: public digidoc::XmlConfCurrent { public: - DigidocConf() + explicit DigidocConf(bool debugLogging) { - enableLog(Settings::LIBDIGIDOCPP_DEBUG); - Settings::LIBDIGIDOCPP_DEBUG = false; + enableLog(debugLogging); Settings::LIBDIGIDOCPP_DEBUG.registerCallback([this](const bool &value) { enableLog(value); }); #ifdef Q_OS_MACOS if(Settings::PROXY_HOST.isSet() || Settings::PROXY_PORT.isSet() @@ -253,7 +261,7 @@ class DigidocConf final: public digidoc::XmlConfCurrent void enableLog(bool enable) { if(enable) { - log = QStringLiteral("%1/libdigidocpp.log").arg(QDir::tempPath()).toStdString(); + log = FileDialog::logPath(QStringLiteral("libdigidocpp.log")).toStdString(); DDCDocLogger::setLogLevel(libcdoc::LEVEL_DEBUG); } else { log.reset(); @@ -349,8 +357,16 @@ Application::Application( int &argc, char **argv ) setOrganizationDomain(QStringLiteral("ria.ee")); setOrganizationName(QStringLiteral("RIA")); setWindowIcon(QIcon(QStringLiteral(":/images/Icon.svg"))); - if(QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), applicationName()))) + const bool debugLogging = Settings::LIBDIGIDOCPP_DEBUG; + Settings::LIBDIGIDOCPP_DEBUG = false; + if(debugLogging && FileDialog::openLogFile(applicationLog(), + FileDialog::logPath(QStringLiteral("%1.log").arg(applicationName())))) + { + QLoggingCategory::setFilterRules(QStringLiteral( + "qdigidoc4.*.debug=true\n" + "qdigidoc4.pcsc.apdu.debug=false")); qInstallMessageHandler(msgHandler); + } #if defined(Q_OS_WIN) AllowSetForegroundWindow( ASFW_ANY ); @@ -496,10 +512,10 @@ Application::Application( int &argc, char **argv ) d->helpAction = d->bar->helpMenu()->addAction(tr("DigiDoc4 Client Help"), this, &Application::openHelp); #endif - DDCDocLogger::setUpLogger(QStringLiteral("%1/libcdoc.log").arg(QDir::tempPath())); + DDCDocLogger::setUpLogger(FileDialog::logPath(QStringLiteral("libcdoc.log"))); try { - digidoc::Conf::init( new DigidocConf ); + digidoc::Conf::init(new DigidocConf(debugLogging)); d->cryptoManager = new QCryptoManager(); updateTSLCache(QDateTime::currentDateTimeUtc().addDays(-7)); @@ -676,7 +692,7 @@ bool Application::event(QEvent *event) void Application::initDiagnosticConf() { - digidoc::Conf::init(new DigidocConf); + digidoc::Conf::init(new DigidocConf(false)); } void Application::loadTranslation( const QString &lang ) @@ -797,8 +813,8 @@ QWidget* Application::mainWindow() void Application::msgHandler(QtMsgType type, const QMessageLogContext &ctx, const QString &msg) { - QFile f(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), applicationName())); - if(!f.open( QFile::Append )) + QFile &f = applicationLog(); + if(!f.isOpen()) return; f.write(QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd hh:mm:ss ")).toUtf8()); switch(type) @@ -809,13 +825,20 @@ void Application::msgHandler(QtMsgType type, const QMessageLogContext &ctx, cons case QtFatalMsg: f.write("F"); break; default: f.write("I"); break; } - f.write(QStringLiteral(" %1 ").arg(QLatin1String(ctx.category)).toUtf8()); + f.write(" "); + f.write(ctx.category ? ctx.category : "default"); + f.write(" "); if(ctx.line > 0) { - f.write(QStringLiteral("%1:%2 \"%3\" ") - .arg(QFileInfo(QString::fromLatin1(ctx.file)).fileName()) - .arg(ctx.line) - .arg(QLatin1String(ctx.function)).toUtf8()); + QByteArrayView fileName(ctx.file ? ctx.file : ""); + const qsizetype separator = qMax(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\')); + fileName = fileName.sliced(separator + 1); + f.write(fileName.data(), fileName.size()); + f.write(":"); + f.write(QByteArray::number(ctx.line)); + f.write(" \""); + f.write(ctx.function ? ctx.function : ""); + f.write("\" "); } f.write(msg.toUtf8()); f.write("\n"); diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 727566adf..615947739 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -35,6 +35,7 @@ #include "Settings.h" #include "TokenData.h" #include "Utils.h" +#include "dialogs/FileDialog.h" #include "effects/FadeInNotification.h" #include #include @@ -401,8 +402,7 @@ void DDCDocLogger::setUpLogger(const QString &path) { DDCDocLogger *logger = getLogger(); logger->setMinLogLevel(libcdoc::LEVEL_WARNING); - logger->ofs.setFileName(path); - if(logger->ofs.open(QFile::WriteOnly|QFile::Append)) + if(FileDialog::openLogFile(logger->ofs, path)) libcdoc::setLogger(logger); } diff --git a/client/CryptoDoc.cpp b/client/CryptoDoc.cpp index d3d4a0720..5dc74af26 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -29,7 +29,6 @@ #include "dialogs/FileDialog.h" #include "dialogs/WarningDialog.h" -#include #include #include #include @@ -45,7 +44,7 @@ using namespace ria::qdigidoc4; -Q_LOGGING_CATEGORY(CRYPTO, "CRYPTO") +Q_LOGGING_CATEGORY(CRYPTO, "qdigidoc4.crypto", QtWarningMsg) CKey::CKey(QSslCertificate _rcpt_cert) : lock(libcdoc::Lock::PUBLIC_KEY), rcpt_cert(_rcpt_cert) { SslCertificate ssl(rcpt_cert); @@ -215,8 +214,6 @@ CryptoDoc::CryptoDoc( QObject *parent ) : QObject(parent) , d(new Private) { - const_cast(CRYPTO()).setEnabled(QtDebugMsg, - QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), Application::applicationName()))); } CryptoDoc::~CryptoDoc() { diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 819ae47ab..55460bb70 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -30,7 +30,7 @@ using namespace Qt::Literals::StringLiterals; -Q_LOGGING_CATEGORY(CNG, "qdigidoc4.QCNG") +Q_LOGGING_CATEGORY(CNG, "qdigidoc4.cng", QtWarningMsg) template struct SCOPE diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index dd15c5c93..d8bc022ae 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -34,7 +34,7 @@ #include #include -static Q_LOGGING_CATEGORY(CryptoLog, "qdigidoc4.QCryptoManager") +static Q_LOGGING_CATEGORY(CryptoLog, "qdigidoc4.cryptomanager", QtWarningMsg) // TODO: Port everything to the new OpenSSL API #define OPENSSL_SUPPRESS_DEPRECATED diff --git a/client/QPCSC.cpp b/client/QPCSC.cpp index 42aa83445..321e71c5d 100644 --- a/client/QPCSC.cpp +++ b/client/QPCSC.cpp @@ -27,8 +27,8 @@ #include #include -static Q_LOGGING_CATEGORY(APDU,"QPCSC.APDU") -static Q_LOGGING_CATEGORY(SCard,"QPCSC.SCard") +static Q_LOGGING_CATEGORY(APDU, "qdigidoc4.pcsc.apdu", QtWarningMsg) +static Q_LOGGING_CATEGORY(SCard, "qdigidoc4.pcsc", QtWarningMsg) static quint16 toUInt16(const QByteArray &data, int size) { @@ -89,8 +89,6 @@ quint32 QPCSCReader::Private::feature(DRIVER_FEATURES key) QPCSC::QPCSC() : d(new Private) { - const_cast(SCard()).setEnabled(QtDebugMsg, qEnvironmentVariableIsSet("PCSC_DEBUG")); - const_cast(APDU()).setEnabled(QtDebugMsg, qEnvironmentVariableIsSet("APDU_DEBUG")); Q_UNUSED(serviceRunning()) } diff --git a/client/QSmartCard.cpp b/client/QSmartCard.cpp index 46c9b5f8f..72bd0cd2f 100644 --- a/client/QSmartCard.cpp +++ b/client/QSmartCard.cpp @@ -31,7 +31,7 @@ #include #include -static Q_LOGGING_CATEGORY(CLog, "qdigidoc4.QSmartCard") +static Q_LOGGING_CATEGORY(CLog, "qdigidoc4.smartcard", QtWarningMsg) QSmartCardData::QSmartCardData(): d(new QSmartCardDataPrivate) {} QSmartCardData::QSmartCardData(const QSmartCardData &other) = default; diff --git a/client/dialogs/FileDialog.cpp b/client/dialogs/FileDialog.cpp index db38ddafd..7c37262ed 100644 --- a/client/dialogs/FileDialog.cpp +++ b/client/dialogs/FileDialog.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,10 @@ class CPtr #include #endif +#ifndef Q_OS_WIN +#include +#endif + QString FileDialog::createNewFileName(const QString &file, bool signature, QWidget *parent) { const QString extension = signature ? QStringLiteral(".asice") : @@ -125,6 +130,36 @@ std::unique_ptr FileDialog::keepAccessAlive(const QString &path, QIODevic #endif } +QString FileDialog::logPath(const QString &name) +{ + QString dir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); + QDir().mkpath(dir); +#ifndef Q_OS_WIN + QFile::setPermissions(dir, QFileDevice::ReadOwner|QFileDevice::WriteOwner|QFileDevice::ExeOwner); +#endif + return dir + QLatin1Char('/') + name; +} + +bool FileDialog::openLogFile(QFile &f, const QString &path) +{ + static constexpr qint64 maxBytes = 5LL * 1024 * 1024; + QFileInfo info(path); + if(info.exists() && (info.isSymLink() || !info.isFile() +#ifndef Q_OS_WIN + || info.ownerId() != geteuid() +#endif + )) + return false; + + f.setFileName(path); + QIODevice::OpenMode mode = QIODevice::WriteOnly|QIODevice::Text| + (info.size() >= maxBytes ? QIODevice::Truncate : QIODevice::Append); + if(!f.open(mode)) + return false; + f.setPermissions(QFileDevice::ReadOwner|QFileDevice::WriteOwner); + return true; +} + bool FileDialog::isSignedPDF(const QString &path) { if(!path.endsWith(QLatin1String("pdf"), Qt::CaseInsensitive)) diff --git a/client/dialogs/FileDialog.h b/client/dialogs/FileDialog.h index 463e44f61..f1a6a63c1 100644 --- a/client/dialogs/FileDialog.h +++ b/client/dialogs/FileDialog.h @@ -42,6 +42,8 @@ class FileDialog : public QFileDialog static std::unique_ptr keepAccessAlive(const QString &path, QIODevice::OpenMode mode = QIODevice::WriteOnly|QIODevice::Append); static bool isSignedPDF(const QString &path); + static QString logPath(const QString &name); + static bool openLogFile(QFile &f, const QString &path); static void setFileZone(const QString &target, const QString &source); static void setReadOnly(const QString &path, bool readonly = true); static QString normalized(const QString &file); diff --git a/client/dialogs/SettingsDialog.cpp b/client/dialogs/SettingsDialog.cpp index f40c998c0..8fcdee5e8 100644 --- a/client/dialogs/SettingsDialog.cpp +++ b/client/dialogs/SettingsDialog.cpp @@ -55,7 +55,7 @@ using namespace Qt::StringLiterals; #endif -#define qdigidoc4log QStringLiteral("%1/%2.log").arg(QDir::tempPath(), QApplication::applicationName()) +#define qdigidoc4log FileDialog::logPath(QStringLiteral("%1.log").arg(QApplication::applicationName())) namespace { @@ -337,13 +337,9 @@ SettingsDialog::SettingsDialog(int page, QWidget *parent) ui->chkLibdigidocppDebug->setChecked(Settings::LIBDIGIDOCPP_DEBUG); connect(ui->chkLibdigidocppDebug, &QCheckBox::toggled, this, [this](bool checked) { Settings::LIBDIGIDOCPP_DEBUG = checked; + QFile::remove(qdigidoc4log); if(!checked) - { - QFile::remove(qdigidoc4log); return; - } - if(QFile f(qdigidoc4log); f.open(QFile::WriteOnly|QFile::Truncate)) - f.write({}); #ifdef Q_OS_MACOS WarningDialog::create(this) ->withTitle(tr("Restart DigiDoc4 Client to activate logging")) @@ -410,7 +406,7 @@ SettingsDialog::SettingsDialog(int page, QWidget *parent) }); connect(ui->btnNavSaveLibdigidocpp, &QPushButton::clicked, this, [this]{ Settings::LIBDIGIDOCPP_DEBUG = false; - QString log = QStringLiteral("%1/libdigidocpp.log").arg(QDir::tempPath()); + QString log = FileDialog::logPath(QStringLiteral("libdigidocpp.log")); saveFile(QStringLiteral("libdigidocpp.txt"), log); saveFile(QStringLiteral("qdigidoc4.txt"), qdigidoc4log); QFile::remove(log); @@ -630,7 +626,7 @@ void SettingsDialog::showPage(int page) ui->btnCheckConnection->setVisible(page == NetworkSettings); ui->btnNavSaveReport->setVisible(page == DiagnosticsSettings); ui->btnNavSaveLibdigidocpp->setVisible(page == DiagnosticsSettings && - QFile::exists(QStringLiteral("%1/libdigidocpp.log").arg(QDir::tempPath()))); + QFile::exists(FileDialog::logPath(QStringLiteral("libdigidocpp.log")))); #ifdef Q_OS_WIN ui->btnNavFromHistory->setVisible(page == GeneralSettings); #else diff --git a/client/signer/MobileProgress.cpp b/client/signer/MobileProgress.cpp index 3dd473984..b3035b905 100644 --- a/client/signer/MobileProgress.cpp +++ b/client/signer/MobileProgress.cpp @@ -27,7 +27,6 @@ #include -#include #include #include #include @@ -42,7 +41,7 @@ #include #include -Q_LOGGING_CATEGORY(MIDLog,"RIA.MID") +Q_LOGGING_CATEGORY(MIDLog, "qdigidoc4.mobileid", QtWarningMsg) using namespace digidoc; @@ -73,8 +72,6 @@ class MobileProgress::Private final: public QObject MobileProgress::MobileProgress(const SigningProgressUI &pui) : d(new Private(pui)) { - const_cast(MIDLog()).setEnabled(QtDebugMsg, - QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), QApplication::applicationName()))); d->ui.bar->setMaximum(75); d->ui.bar->setValue(0); d->ui.code->setBuddy(d->ui.bar); diff --git a/client/signer/SmartIDProgress.cpp b/client/signer/SmartIDProgress.cpp index a00128328..56f07a7af 100644 --- a/client/signer/SmartIDProgress.cpp +++ b/client/signer/SmartIDProgress.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include @@ -43,7 +43,7 @@ #include #include -Q_LOGGING_CATEGORY(SIDLog,"RIA.SmartID") +Q_LOGGING_CATEGORY(SIDLog, "qdigidoc4.smartid", QtWarningMsg) using namespace digidoc; @@ -74,8 +74,6 @@ class SmartIDProgress::Private final: public QObject SmartIDProgress::SmartIDProgress(const SigningProgressUI &pui) : d(new Private(pui)) { - const_cast(SIDLog()).setEnabled(QtDebugMsg, - QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), QApplication::applicationName()))); d->ui.bar->setMaximum(100); d->ui.bar->setValue(0); d->ui.code->setBuddy(d->ui.bar);