Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:lomiri-download-manager
User: [email protected]
Usertags: pu

Please accept upload of lomiri-download-manager 0.2.1-3+deb13u1 to trixie.

[ Reason ]
Security fixes, preventing arbitrary filesystem access as root:

  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.

[ Impact ]
Security issues will not get fixed.

[ Tests ]
Manual tests in Lomiri on Debian 13.

[ Risks ]
Only for Lomiri users.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]

+  * 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.

[ Other info ]
Security team requested to upload this via trixie-pu.
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 
06:24:51.000000000 +0200
+++ lomiri-download-manager-0.2.1/debian/changelog      2026-11-03 
18:23:16.000000000 +0100
@@ -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 <[email protected]>  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 
17:40:41.000000000 +0200
+++ lomiri-download-manager-0.2.1/debian/control        2026-11-03 
18:23:16.000000000 +0100
@@ -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 01:00:00.000000000 +0100
+++ 
lomiri-download-manager-0.2.1/debian/patches/0001_src-downloads-dont-run-commands-when-privileged.patch
     2026-11-03 18:17:15.000000000 +0100
@@ -0,0 +1,428 @@
+From 5fd50939a3cacef4181f6bcb58796bdcbc17984d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Maciej=20Sopy=C5=82o?= <[email protected]>
+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 <lomiri/transfers/system/logger.h>
+ #include <sys/apparmor.h>
+ #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 <unistd.h>
++#include <sys/capability.h>
++
++namespace Lomiri {
++
++namespace Transfers {
++
++namespace System {
++
++std::shared_ptr<Security> Security::_instance = nullptr;
++
++std::shared_ptr<Security>
++Security::instance() {
++    if (!_instance) {
++        _instance = std::make_shared<Security>();
++    }
++    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<Security> 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 <memory>
++
++namespace Lomiri {
++
++namespace Transfers {
++
++namespace System {
++
++class Security {
++ public:
++    static std::shared_ptr<Security> instance();
++
++    virtual bool isPrivileged();
++
++    // Only for testing
++    static void setInstance(std::shared_ptr<Security> instance);
++
++ private:
++    static std::shared_ptr<Security> _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 <map>
+ 
+ #include <glog/logging.h>
++#include <sys/capability.h>
+ 
+ #include <QBuffer>
+ #include <QCryptographicHash>
+@@ -42,6 +43,7 @@
+ #include <lomiri/transfers/system/filename_mutex.h>
+ #include <lomiri/transfers/system/uuid_factory.h>
+ #include <lomiri/transfers/system/uuid_utils.h>
++#include <lomiri/transfers/system/security.h>
+ 
+ #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 <lomiri/transfers/system/security.h>
++#include <gmock/gmock.h>
++
++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 <lomiri/transfers/metadata.h>
+ #include <lomiri/transfers/system/hash_algorithm.h>
+ #include <lomiri/transfers/system/uuid_utils.h>
++#include <lomiri/transfers/system/security.h>
+ #include <network_reply.h>
+ #include "filename_mutex.h"
+ #include "matchers.h"
+@@ -60,6 +61,8 @@
+     FileManager::setInstance(_fileManager);
+     _cryptoFactory = new MockCryptographicHashFactory();
+     CryptographicHashFactory::setInstance(_cryptoFactory);
++    _security = std::make_shared<MockSecurity>();
++    Security::setInstance(_security);
+ }
+ 
+ void
+@@ -2844,6 +2847,86 @@
+ }
+ 
+ void
++TestDownload::testProcessNotExecutedWhenPrivileged() {
++    QVariantMap metadata;
++    metadata["post-download-command"] = QStringList { "wont", "execute" };
++
++    QStringList args;
++
++    QScopedPointer<MockFile> file(new MockFile("test"));
++    QScopedPointer<MockNetworkReply> 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 <process_factory.h>
+ #include <request_factory.h>
+ #include <network_session.h>
++#include <security.h>
+ 
+ #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<MockSecurity> _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 01:00:00.000000000 +0100
+++ 
lomiri-download-manager-0.2.1/debian/patches/0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch
     2026-11-03 18:18:05.000000000 +0100
@@ -0,0 +1,46 @@
+From 779dfd5bbeb5fe8877627b772e5024e40eda38aa Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Maciej=20Sopy=C5=82o?= <[email protected]>
+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 @@
+        <allow own="com.lomiri.applications.Downloader"/>
+        <allow send_destination="com.lomiri.applications.Downloader"/>
+     </policy>
+-
+-    <policy context="default">
+-       <allow send_destination="com.lomiri.applications.Downloader"/>
+-       <allow send_destination="com.lomiri.applications.Downloader"
+-              send_interface="org.freedesktop.DBus.Introspectable"/>
+-    </policy>
+-
+ </busconfig>
+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 @@
+        <allow own="com.lomiri.applications.Uploader"/>
+        <allow send_destination="com.lomiri.applications.Uploader"/>
+     </policy>
+-
+-    <policy context="default">
+-       <allow send_destination="com.lomiri.applications.Uploader"/>
+-       <allow send_destination="com.lomiri.applications.Uploader"
+-              send_interface="org.freedesktop.DBus.Introspectable"/>
+-    </policy>
+-
+ </busconfig>
+-- 
+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 
01:00:00.000000000 +0100
+++ lomiri-download-manager-0.2.1/debian/patches/series 2026-11-03 
18:18:39.000000000 +0100
@@ -0,0 +1,2 @@
+0001_src-downloads-dont-run-commands-when-privileged.patch
+0002_src-uploads-downloads-daemon-conf-deny-access-to-system.patch

Reply via email to