From 8c537a13cd97be574126e6a8ba327cc83ebed4af 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/QPCSC.cpp | 6 ++-- client/QSigner.cpp | 2 +- client/QSmartCard.cpp | 2 +- client/dialogs/FileDialog.cpp | 35 ++++++++++++++++++++ client/dialogs/FileDialog.h | 4 +++ client/dialogs/MobileProgress.cpp | 5 +-- client/dialogs/SettingsDialog.cpp | 12 +++---- client/dialogs/SmartIDProgress.cpp | 6 ++-- 12 files changed, 92 insertions(+), 44 deletions(-) diff --git a/client/Application.cpp b/client/Application.cpp index 66b4107d5..22049849b 100644 --- a/client/Application.cpp +++ b/client/Application.cpp @@ -36,6 +36,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" @@ -51,10 +52,12 @@ class MacMenuBar {}; #include #include +#include #include #include #include #include +#include #include #include #include @@ -84,6 +87,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"), @@ -93,10 +102,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); }); #ifndef Q_OS_DARWIN setTSLOnlineDigest(true); @@ -222,7 +230,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(); @@ -321,8 +329,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 ); @@ -458,10 +474,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->signer = new QSigner(this); updateTSLCache(QDateTime::currentDateTimeUtc().addDays(-7)); @@ -667,7 +683,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 ) @@ -788,8 +804,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) @@ -800,13 +816,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 19cf47812..16892fd33 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -36,6 +36,7 @@ #include "Settings.h" #include "TokenData.h" #include "Utils.h" +#include "dialogs/FileDialog.h" #include "effects/FadeInNotification.h" #include #include @@ -392,8 +393,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 a83b10d91..5b9cf72e6 100644 --- a/client/CryptoDoc.cpp +++ b/client/CryptoDoc.cpp @@ -30,7 +30,6 @@ #include "dialogs/FileDialog.h" #include "dialogs/WarningDialog.h" -#include #include #include #include @@ -46,7 +45,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); @@ -223,8 +222,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 7da8c4b1a..d541e63d6 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/QPCSC.cpp b/client/QPCSC.cpp index 3999ecd5e..f7ffe96ac 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) { @@ -86,8 +86,6 @@ QHash QPCSCReader::Private::features() 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/QSigner.cpp b/client/QSigner.cpp index 0ccd17fc0..1a89acdbf 100644 --- a/client/QSigner.cpp +++ b/client/QSigner.cpp @@ -41,7 +41,7 @@ #include #include -static Q_LOGGING_CATEGORY(SLog, "qdigidoc4.QSigner") +static Q_LOGGING_CATEGORY(SLog, "qdigidoc4.signer", QtWarningMsg) class QSigner::Private final { diff --git a/client/QSmartCard.cpp b/client/QSmartCard.cpp index 764cc0ef4..d61a1d61f 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 f47151c36..76815d8ab 100644 --- a/client/dialogs/FileDialog.cpp +++ b/client/dialogs/FileDialog.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -48,6 +49,10 @@ class CPtr #include #endif +#ifndef Q_OS_WIN +#include +#endif + #include QString FileDialog::createNewFileName(const QString &file, bool signature, QWidget *parent) @@ -104,6 +109,36 @@ bool FileDialog::fileIsWritable( const QString &filename ) return result; } +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 cb87355ff..6dc7e3d34 100644 --- a/client/dialogs/FileDialog.h +++ b/client/dialogs/FileDialog.h @@ -21,6 +21,8 @@ #include +class QFile; + class FileDialog : public QFileDialog { Q_OBJECT @@ -37,6 +39,8 @@ class FileDialog : public QFileDialog static FileType detect(const QString &filename); static bool fileIsWritable( const QString &filename ); 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/MobileProgress.cpp b/client/dialogs/MobileProgress.cpp index d78131636..bde850279 100644 --- a/client/dialogs/MobileProgress.cpp +++ b/client/dialogs/MobileProgress.cpp @@ -28,7 +28,6 @@ #include -#include #include #include #include @@ -39,7 +38,7 @@ #include #include -Q_LOGGING_CATEGORY(MIDLog,"RIA.MID") +Q_LOGGING_CATEGORY(MIDLog, "qdigidoc4.mobileid", QtWarningMsg) using namespace digidoc; @@ -65,8 +64,6 @@ class MobileProgress::Private final: public QDialog, public Ui::MobileProgress MobileProgress::MobileProgress(QWidget *parent) : d(new Private(parent)) { - const_cast(MIDLog()).setEnabled(QtDebugMsg, - QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), QApplication::applicationName()))); d->setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint); d->setupUi(d); d->code->setBuddy(d->signProgressBar); diff --git a/client/dialogs/SettingsDialog.cpp b/client/dialogs/SettingsDialog.cpp index 9ad9e6a4f..0e1e0cd87 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())) SettingsDialog::SettingsDialog(int page, QWidget *parent) : QDialog(parent) @@ -294,13 +294,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")) @@ -367,7 +363,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); @@ -581,7 +577,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/dialogs/SmartIDProgress.cpp b/client/dialogs/SmartIDProgress.cpp index 97afb60fd..058ffa353 100644 --- a/client/dialogs/SmartIDProgress.cpp +++ b/client/dialogs/SmartIDProgress.cpp @@ -29,7 +29,7 @@ #include #include -#include +#include #include #include #include @@ -42,7 +42,7 @@ #include -Q_LOGGING_CATEGORY(SIDLog,"RIA.SmartID") +Q_LOGGING_CATEGORY(SIDLog, "qdigidoc4.smartid", QtWarningMsg) using namespace digidoc; @@ -77,8 +77,6 @@ class SmartIDProgress::Private final: public QDialog, public Ui::MobileProgress SmartIDProgress::SmartIDProgress(QWidget *parent) : d(new Private(parent)) { - const_cast(SIDLog()).setEnabled(QtDebugMsg, - QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), QApplication::applicationName()))); d->setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint); d->setupUi(d); d->signProgressBar->setMaximum(100);