Version in base suite: 0.2.1-3 Base version: lomiri-download-manager_0.2.1-3 Target version: lomiri-download-manager_0.2.1-3+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/l/lomiri-download-manager/lomiri-download-manager_0.2.1-3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/l/lomiri-download-manager/lomiri-download-manager_0.2.1-3+deb13u1.dsc changelog | 15 control | 1 patches/0001_src-downloads-dont-run-commands-when-privileged.patch | 428 ++++++++++ patches/0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch | 46 + patches/series | 2 5 files changed, 492 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpyoohynse/lomiri-download-manager_0.2.1-3.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpyoohynse/lomiri-download-manager_0.2.1-3+deb13u1.dsc: no acceptable signature found diff -Nru lomiri-download-manager-0.2.1/debian/changelog lomiri-download-manager-0.2.1/debian/changelog --- lomiri-download-manager-0.2.1/debian/changelog 2025-04-10 04:24:51.000000000 +0000 +++ lomiri-download-manager-0.2.1/debian/changelog 2026-11-03 17:23:16.000000000 +0000 @@ -1,3 +1,18 @@ +lomiri-download-manager (0.2.1-3+deb13u1) trixie; urgency=medium + + * debian/patches: + + Add 0001_src-downloads-dont-run-commands-when-privileged.patch and + 0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch. + CVE-2026-96424: Fix incorrect DBus policy for lomiri-upload-manager + (part of lomiri-download-manager) that allows arbitrary user to read + arbitrary file as root over DBus Source. + CVE-2026-96425: Fix incorrect DBus policy for lomiri-download-manager + that allows arbitrary user to run arbitrary command as root over DBus. + * debian/control: + + Add to B-D: libcap-dev. + + -- Mike Gabriel Tue, 03 Nov 2026 18:23:16 +0100 + lomiri-download-manager (0.2.1-3) unstable; urgency=medium * debian/rules: diff -Nru lomiri-download-manager-0.2.1/debian/control lomiri-download-manager-0.2.1/debian/control --- lomiri-download-manager-0.2.1/debian/control 2025-04-03 15:40:41.000000000 +0000 +++ lomiri-download-manager-0.2.1/debian/control 2026-11-03 17:23:16.000000000 +0000 @@ -13,6 +13,7 @@ libapparmor-dev, libboost-log-dev, libboost-program-options-dev, + libcap-dev, libdbus-1-dev, libqt5sql5-sqlite, libqt6sql6-sqlite, diff -Nru lomiri-download-manager-0.2.1/debian/patches/0001_src-downloads-dont-run-commands-when-privileged.patch lomiri-download-manager-0.2.1/debian/patches/0001_src-downloads-dont-run-commands-when-privileged.patch --- lomiri-download-manager-0.2.1/debian/patches/0001_src-downloads-dont-run-commands-when-privileged.patch 1970-01-01 00:00:00.000000000 +0000 +++ lomiri-download-manager-0.2.1/debian/patches/0001_src-downloads-dont-run-commands-when-privileged.patch 2026-11-03 17:17:15.000000000 +0000 @@ -0,0 +1,428 @@ +From 5fd50939a3cacef4181f6bcb58796bdcbc17984d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Maciej=20Sopy=C5=82o?= +Date: Thu, 10 Sep 2026 14:24:43 +0200 +Subject: [PATCH] src/downloads: don't run commands when privileged + +--- + CMakeLists.txt | 1 + + src/common/priv/CMakeLists.txt | 4 + + .../priv/lomiri/transfers/system/apparmor.cpp | 3 +- + .../priv/lomiri/transfers/system/security.cpp | 64 ++++++++++++++ + .../priv/lomiri/transfers/system/security.h | 46 ++++++++++ + src/downloads/daemon/CMakeLists.txt | 1 + + src/downloads/priv/CMakeLists.txt | 1 + + .../priv/lomiri/downloads/file_download.cpp | 8 ++ + tests/common/security.h | 41 +++++++++ + tests/downloads/daemon/test_download.cpp | 83 +++++++++++++++++++ + tests/downloads/daemon/test_download.h | 3 + + 12 files changed, 255 insertions(+), 1 deletion(-) + create mode 100644 src/common/priv/lomiri/transfers/system/security.cpp + create mode 100644 src/common/priv/lomiri/transfers/system/security.h + create mode 100644 tests/common/security.h + +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -62,6 +62,7 @@ + pkg_check_modules(DBUS REQUIRED dbus-1) + pkg_check_modules(LOMIRI_API REQUIRED liblomiri-api) + pkg_check_modules(APPARMOR REQUIRED libapparmor) ++pkg_check_modules(CAP REQUIRED libcap) + + if(USE_SYSTEMD) + pkg_check_modules(SYSTEMD systemd) +--- a/src/common/priv/CMakeLists.txt ++++ b/src/common/priv/CMakeLists.txt +@@ -21,6 +21,7 @@ + lomiri/transfers/system/process.cpp + lomiri/transfers/system/process_factory.cpp + lomiri/transfers/system/request_factory.cpp ++ lomiri/transfers/system/security.cpp + lomiri/transfers/system/timer.cpp + lomiri/transfers/system/uuid_factory.cpp + lomiri/transfers/system/uuid_utils.cpp +@@ -50,6 +51,7 @@ + lomiri/transfers/system/process.h + lomiri/transfers/system/process_factory.h + lomiri/transfers/system/request_factory.h ++ lomiri/transfers/system/security.h + lomiri/transfers/system/timer.h + lomiri/transfers/system/uuid_factory.h + lomiri/transfers/system/uuid_utils.h +@@ -57,6 +59,7 @@ + + include_directories(${APPARMOR_INCLUDE_DIRS}) + include_directories(${DBUS_INCLUDE_DIRS}) ++include_directories(${CAP_INCLUDE_DIRS}) + include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + include_directories(${CMAKE_SOURCE_DIR}/src/common/public) +@@ -83,6 +86,7 @@ + Qt::Core + Qt::DBus + ldm-common${QT_SUFFIX} ++ ${CAP_LIBRARIES} + ) + + install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) +--- a/src/common/priv/lomiri/transfers/system/apparmor.cpp ++++ b/src/common/priv/lomiri/transfers/system/apparmor.cpp +@@ -28,6 +28,7 @@ + #include + #include + #include "apparmor.h" ++#include "security.h" + #include "dbus_proxy_factory.h" + #include "pending_reply.h" + #include "uuid_utils.h" +@@ -192,7 +193,7 @@ + AppArmor::getLocalPath(const QString& appId) { + // if the service is running as root we will always return /tmp + // as the local path root +- if (getuid() == 0){ ++ if (Security::instance()->isPrivileged()){ + LOG(INFO) << "Running as system bus using /tmp for downloads"; + return QStandardPaths::writableLocation( + QStandardPaths::TempLocation); +--- /dev/null ++++ b/src/common/priv/lomiri/transfers/system/security.cpp +@@ -0,0 +1,64 @@ ++/* ++ * Copyright (C) 2026 UBports Foundation. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of version 3 of the GNU Lesser General Public ++ * License as published by the Free Software Foundation. ++ * ++ * 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 Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#include "security.h" ++ ++#include ++#include ++ ++namespace Lomiri { ++ ++namespace Transfers { ++ ++namespace System { ++ ++std::shared_ptr Security::_instance = nullptr; ++ ++std::shared_ptr ++Security::instance() { ++ if (!_instance) { ++ _instance = std::make_shared(); ++ } ++ return _instance; ++} ++ ++bool ++Security::isPrivileged() { ++ if (geteuid() == 0) { ++ return true; ++ } ++ ++ cap_t cap_current = cap_get_proc(); ++ cap_t cap_zero = cap_init(); ++ bool has_capabilities = cap_compare(cap_current, cap_zero) != 0; ++ cap_free(cap_current); ++ cap_free(cap_zero); ++ ++ return has_capabilities; ++} ++ ++void ++Security::setInstance(std::shared_ptr instance) { ++ _instance = std::move(instance); ++} ++ ++} // System ++ ++} // Transfers ++ ++} // Lomiri +--- /dev/null ++++ b/src/common/priv/lomiri/transfers/system/security.h +@@ -0,0 +1,46 @@ ++/* ++ * Copyright (C) 2026 UBports Foundation. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of version 3 of the GNU Lesser General Public ++ * License as published by the Free Software Foundation. ++ * ++ * 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 Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#pragma once ++ ++#include ++ ++namespace Lomiri { ++ ++namespace Transfers { ++ ++namespace System { ++ ++class Security { ++ public: ++ static std::shared_ptr instance(); ++ ++ virtual bool isPrivileged(); ++ ++ // Only for testing ++ static void setInstance(std::shared_ptr instance); ++ ++ private: ++ static std::shared_ptr _instance; ++}; ++ ++} // System ++ ++} // Transfers ++ ++} // Lomiri +--- a/src/downloads/daemon/CMakeLists.txt ++++ b/src/downloads/daemon/CMakeLists.txt +@@ -22,6 +22,7 @@ + ldm-priv-common + lomiri-download-manager-common + lomiri-download-manager-priv ++ ${CAP_LIBRARIES} + ) + + configure_file( +--- a/src/downloads/priv/CMakeLists.txt ++++ b/src/downloads/priv/CMakeLists.txt +@@ -71,6 +71,7 @@ + ldm-common${QT_SUFFIX} + ldm-priv-common${QT_SUFFIX} + lomiri-download-manager-common${QT_SUFFIX} ++ ${CAP_LIBRARIES} + ) + + if (ENABLE_QT6) +--- a/src/downloads/priv/lomiri/downloads/file_download.cpp ++++ b/src/downloads/priv/lomiri/downloads/file_download.cpp +@@ -19,6 +19,7 @@ + #include + + #include ++#include + + #include + #include +@@ -42,6 +43,7 @@ + #include + #include + #include ++#include + + #include "header_parser.h" + #include "file_download.h" +@@ -1031,6 +1033,12 @@ + postDownloadProcess->start(command, args); + return; + } else if (_metadata.contains(Metadata::COMMAND_KEY)) { ++ if (Security::instance()->isPrivileged()) { ++ DOWN_LOG(ERROR) << "Post processing commands are unavailable on the priviledged download manager"; ++ emitError(COMMAND_ERROR); ++ return; ++ } ++ + if (isConfined()) { + DOWN_LOG(ERROR) << "Post processing commands are unavailable to confined applications"; + emitError(COMMAND_ERROR); +--- /dev/null ++++ b/tests/common/security.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (C) 2026 UBports Foundation. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of version 3 of the GNU Lesser General Public ++ * License as published by the Free Software Foundation. ++ * ++ * 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 Lesser General Public ++ * License along with this library; if not, write to the ++ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ++ * Boston, MA 02110-1301, USA. ++ */ ++ ++#pragma once ++ ++#include ++#include ++ ++namespace Lomiri { ++ ++namespace Transfers { ++ ++using namespace System; ++ ++namespace Tests { ++ ++class MockSecurity : public Security { ++ public: ++ MOCK_METHOD0(isPrivileged, bool()); ++}; ++ ++} // System ++ ++} // Transfers ++ ++} // Lomir +--- a/tests/downloads/daemon/test_download.cpp ++++ b/tests/downloads/daemon/test_download.cpp +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include "filename_mutex.h" + #include "matchers.h" +@@ -60,6 +61,8 @@ + FileManager::setInstance(_fileManager); + _cryptoFactory = new MockCryptographicHashFactory(); + CryptographicHashFactory::setInstance(_cryptoFactory); ++ _security = std::make_shared(); ++ Security::setInstance(_security); + } + + void +@@ -2844,6 +2847,86 @@ + } + + void ++TestDownload::testProcessNotExecutedWhenPrivileged() { ++ QVariantMap metadata; ++ metadata["post-download-command"] = QStringList { "wont", "execute" }; ++ ++ QStringList args; ++ ++ QScopedPointer file(new MockFile("test")); ++ QScopedPointer reply(new MockNetworkReply()); ++ ++ // write the expectations of the reply which is what we are ++ // really testing ++ ++ EXPECT_CALL(*_networkSession, isOnline()) ++ .WillRepeatedly(Return(true)); ++ ++ EXPECT_CALL(*_reqFactory, get(_)) ++ .Times(1) ++ .WillOnce(Return(reply.data())); ++ ++ EXPECT_CALL(*reply.data(), setReadBufferSize(_)) ++ .Times(1); ++ ++ EXPECT_CALL(*reply.data(), attribute(_)) ++ .Times(1) ++ .WillOnce(Return(QVariant(200))); ++ ++ EXPECT_CALL(*reply.data(), hasRawHeader(_)) ++ .Times(2) ++ .WillOnce(Return(false)) ++ .WillOnce(Return(false)); ++ ++ // file system expectations ++ EXPECT_CALL(*_fileManager, createFile(_)) ++ .Times(1) ++ .WillOnce(Return(file.data())); ++ ++ EXPECT_CALL(*file.data(), open(QIODevice::ReadWrite | QFile::Append)) ++ .Times(1) ++ .WillOnce(Return(true)); ++ ++ EXPECT_CALL(*file, flush()) ++ .Times(1) ++ .WillOnce(Return(true)); ++ ++ EXPECT_CALL(*file.data(), remove()) ++ .Times(1) ++ .WillOnce(Return(true)); ++ ++ EXPECT_CALL(*_cryptoFactory, createCryptographicHash(_, _)) ++ .Times(0); ++ ++ EXPECT_CALL(*_security, isPrivileged()) ++ .WillRepeatedly(Return(true)); ++ ++ auto download = new FileDownload(_id, _appId, _path, ++ _isConfined, _rootPath, _url, metadata, _headers); ++ ++ SignalBarrier spy(download, SIGNAL(error(QString))); ++ SignalBarrier startedSpy(download, SIGNAL(started(bool))); ++ ++ download->start(); // change state ++ download->startTransfer(); ++ QVERIFY(startedSpy.ensureSignalEmitted()); ++ ++ // emit the finish signal and expect it to be raised ++ emit reply->finished(); ++ ++ QVERIFY(spy.ensureSignalEmitted()); ++ QCOMPARE(spy.count(), 1); ++ QCOMPARE(download->state(), Download::ERROR); ++ ++ delete download; ++ ++ QVERIFY(Mock::VerifyAndClearExpectations(file.data())); ++ QVERIFY(Mock::VerifyAndClearExpectations(reply.data())); ++ verifyMocks(); ++} ++ ++ ++void + TestDownload::testSetRawHeaderAcceptEncoding() { + auto file = new MockFile("test"); + auto reply = new MockNetworkReply(); +--- a/tests/downloads/daemon/test_download.h ++++ b/tests/downloads/daemon/test_download.h +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + + #include "base_testcase.h" + #include "cryptographic_hash.h" +@@ -128,6 +129,7 @@ + void testProcessError_data(); + void testProcessError(); + void testProcessFinishedCrash(); ++ void testProcessNotExecutedWhenPrivileged(); + + // test related to bug #1224678 + void testSetRawHeaderAcceptEncoding(); +@@ -210,6 +212,7 @@ + MockProcessFactory* _processFactory = nullptr; + MockFileManager* _fileManager = nullptr; + MockCryptographicHashFactory* _cryptoFactory = nullptr; ++ std::shared_ptr _security = nullptr; + }; + + #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) diff -Nru lomiri-download-manager-0.2.1/debian/patches/0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch lomiri-download-manager-0.2.1/debian/patches/0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch --- lomiri-download-manager-0.2.1/debian/patches/0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch 1970-01-01 00:00:00.000000000 +0000 +++ lomiri-download-manager-0.2.1/debian/patches/0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch 2026-11-03 17:18:05.000000000 +0000 @@ -0,0 +1,46 @@ +From 779dfd5bbeb5fe8877627b772e5024e40eda38aa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Maciej=20Sopy=C5=82o?= +Date: Fri, 11 Sep 2026 12:32:20 +0200 +Subject: [PATCH] src/{uploads,downloads}/daemon/*.conf: deny access to system + bus services + +--- + .../daemon/com.lomiri.applications.Downloader.conf | 7 ------- + src/uploads/daemon/com.lomiri.applications.Uploader.conf | 7 ------- + 2 files changed, 14 deletions(-) + +diff --git a/src/downloads/daemon/com.lomiri.applications.Downloader.conf b/src/downloads/daemon/com.lomiri.applications.Downloader.conf +index 766214bc..cd7be264 100644 +--- a/src/downloads/daemon/com.lomiri.applications.Downloader.conf ++++ b/src/downloads/daemon/com.lomiri.applications.Downloader.conf +@@ -8,11 +8,4 @@ + + + +- +- +- +- +- +- + +diff --git a/src/uploads/daemon/com.lomiri.applications.Uploader.conf b/src/uploads/daemon/com.lomiri.applications.Uploader.conf +index 132e272a..b696d3b9 100644 +--- a/src/uploads/daemon/com.lomiri.applications.Uploader.conf ++++ b/src/uploads/daemon/com.lomiri.applications.Uploader.conf +@@ -8,11 +8,4 @@ + + + +- +- +- +- +- +- + +-- +GitLab + diff -Nru lomiri-download-manager-0.2.1/debian/patches/series lomiri-download-manager-0.2.1/debian/patches/series --- lomiri-download-manager-0.2.1/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ lomiri-download-manager-0.2.1/debian/patches/series 2026-11-03 17:18:39.000000000 +0000 @@ -0,0 +1,2 @@ +0001_src-downloads-dont-run-commands-when-privileged.patch +0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch