Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ set(LOCK_SRCS
src/dde-lock/lockframe.cpp
src/dde-lock/lockworker.cpp
src/dde-lock/updateworker.cpp
src/dde-lock/securityloaderhelper.cpp
src/dde-lock/dbus/dbuslockagent.cpp
src/dde-lock/dbus/dbuslockfrontservice.cpp
src/dde-lock/dbus/dbusshutdownagent.cpp
Expand Down Expand Up @@ -426,7 +427,13 @@ else () # snipe
install(PROGRAMS ${SCRIPTS} DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/deepin/greeters.d)
endif ()

install(TARGETS dde-lock lightdm-deepin-greeter greeter-display-setting DESTINATION ${CMAKE_INSTALL_BINDIR})
# dde-lock binary installed to libexec/deepin for security-loader
install(TARGETS dde-lock DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/deepin)
install(TARGETS lightdm-deepin-greeter greeter-display-setting DESTINATION ${CMAKE_INSTALL_BINDIR})
# Install loader wrapper script as /usr/bin/dde-lock
install(PROGRAMS files/dde-lock-loader-wrapper DESTINATION ${CMAKE_INSTALL_BINDIR}
RENAME dde-lock
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)

# 指定greeter,优先级最低,允许被其他应用以更高的配置文件覆盖
install(FILES files/50-deepin.conf DESTINATION ${CMAKE_INSTALL_DATADIR}/lightdm/lightdm.conf.d)
Expand Down
1 change: 1 addition & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Package: dde-session-shell
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends},
deepin-desktop-schemas (>=5.9.14),
deepin-security-loader,
dde-daemon (>=6.1.26),
x11-xserver-utils,
deepin-authenticate(>=1.2.27),
Expand Down
3 changes: 2 additions & 1 deletion debian/dde-session-shell.install
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
etc
usr/bin
usr/share
usr/lib/*/security
usr/lib/*/security
usr/libexec/deepin
29 changes: 29 additions & 0 deletions files/dde-lock-loader-wrapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/bin/bash
# dde-lock wrapper for deepin-security-loader
# Launches dde-lock through security loader to get authorization
# for calling protected system services without polkit popup.

REAL_BINARY="/usr/libexec/deepin/dde-lock"
LOADER="/usr/bin/deepin-security-loader"
LOADER_EXEC="/usr/bin/deepin-security-loader-exec"

# Log to systemd journal with tag "dde-lock"
log_to_journal() {
logger -t dde-lock -p user.info "$1"
}

# Log startup
log_to_journal "dde-lock launched with args: $*"

# Check if loader is available and has proper capabilities
if [ -x "$LOADER" ] && [ -x "$LOADER_EXEC" ]; then
if getcap "$LOADER_EXEC" 2>/dev/null | grep -q cap_setgid; then
# Launch via loader with authorization groups
log_to_journal "Using deepin-security-loader with authorization groups"
exec "$LOADER" --group deepin-daemon -- "$REAL_BINARY" "$@"
fi
fi

# Fallback: direct launch without loader (no polkit-free authorization)
log_to_journal "Fallback: launching directly without security loader"
exec "$REAL_BINARY" "$@"
14 changes: 14 additions & 0 deletions files/permission-interfaces/auth.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"DestList": [
{
"DbusName": "org.deepin.dde.Lastore1",
"DbusPath": "/org/deepin/dde/Lastore1",
"DbusInterface": "org.deepin.dde.Lastore1.Manager"
},
{
"DbusName": "org.deepin.dde.Authenticate1",
"DbusPath": "/org/deepin/dde/Authenticate1",
"DbusInterface": "org.deepin.dde.Authenticate1"
}
]
}
1 change: 1 addition & 0 deletions resources.qrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>files/permission-interfaces/auth.json</file>
<file>misc/images/caps_lock.svg</file>
<file>misc/images/login_check.svg</file>
<file>misc/images/login_lock.svg</file>
Expand Down
12 changes: 11 additions & 1 deletion src/app/dde-lock.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd.
// SPDX-FileCopyrightText: 2015 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

Expand All @@ -13,12 +13,13 @@
#include "lockworker.h"
#include "modules_loader.h"
#include "multiscreenmanager.h"
#include "sessionbasemodel.h"

Check warning on line 16 in src/app/dde-lock.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "sessionbasemodel.h" not found.
#include "warningcontent.h"

Check warning on line 17 in src/app/dde-lock.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "warningcontent.h" not found.
#include "plugin_manager.h"

Check warning on line 18 in src/app/dde-lock.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "plugin_manager.h" not found.
#include "securityloaderhelper.h"

Check warning on line 19 in src/app/dde-lock.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "securityloaderhelper.h" not found.
#include "dbusconstant.h"

Check warning on line 20 in src/app/dde-lock.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "dbusconstant.h" not found.

#include <DApplication>

Check warning on line 22 in src/app/dde-lock.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <DApplication> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <DGuiApplicationHelper>
#include <DLog>
#include <DPlatformTheme>
Expand Down Expand Up @@ -109,9 +110,18 @@
QCommandLineOption quickLoginProcess(QStringList() << "q" << "quicklogin", "show for quick login");
cmdParser.addOption(quickLoginProcess);

QCommandLineOption fd1Opt("fd1", "fd1 from security loader", "fd1");
cmdParser.addOption(fd1Opt);
QCommandLineOption fd2Opt("fd2", "fd2 from security loader", "fd2");
cmdParser.addOption(fd2Opt);

QStringList xddd = app->arguments();
cmdParser.process(*app);

int fd1 = cmdParser.isSet(fd1Opt) ? cmdParser.value(fd1Opt).toInt() : -1;
int fd2 = cmdParser.isSet(fd2Opt) ? cmdParser.value(fd2Opt).toInt() : -1;
SecurityLoaderHelper::instance().doSecurityLoader(fd1, fd2);

bool runDaemon = cmdParser.isSet(backend);
bool showUserList = cmdParser.isSet(switchUser);
bool showShutdown = cmdParser.isSet(shutdown);
Expand Down
231 changes: 231 additions & 0 deletions src/dde-lock/securityloaderhelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "securityloaderhelper.h"

#include <QFile>

Check warning on line 7 in src/dde-lock/securityloaderhelper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QFile> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonDocument>

Check warning on line 8 in src/dde-lock/securityloaderhelper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QJsonDocument> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonObject>

Check warning on line 9 in src/dde-lock/securityloaderhelper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QJsonObject> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QJsonParseError>

Check warning on line 10 in src/dde-lock/securityloaderhelper.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <QJsonParseError> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <QLoggingCategory>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusReply>
#include <unistd.h>

Q_LOGGING_CATEGORY(secLoader, "org.deepin.dde.lock.securityloader")

const QString SecurityLoaderHelper::DEFAULT_CONFIG_PATH = ":/files/permission-interfaces/auth.json";

SecurityLoaderHelper::SecurityLoaderHelper(QObject *parent)
: QObject(parent)
{
}

SecurityLoaderHelper::~SecurityLoaderHelper() {}

SecurityLoaderHelper &SecurityLoaderHelper::instance()
{
static SecurityLoaderHelper instance;
return instance;
}

void SecurityLoaderHelper::doSecurityLoader(int fd1, int fd2)
{
if (fd1 < 0 || fd2 < 0) {
qCWarning(secLoader) << "Not loaded by loader, skipping handshake";
return;
}

qCInfo(secLoader) << "Detected loader injection: fd1=" << fd1 << "fd2=" << fd2;

loadConfig();
// appendCurrentUserAccountsUserDest();
if (!performHandshake(fd1, fd2)) {
qCWarning(secLoader) << "Security loader handshake failed";
}
}

void SecurityLoaderHelper::loadConfig(const QString &configPath)
{
QString path = configPath.isEmpty() ? DEFAULT_CONFIG_PATH : configPath;

qCInfo(secLoader) << "Loading permission config from:" << path;

m_destList = QJsonArray();
parseJsonFile(path);

qCInfo(secLoader) << "Loaded" << m_destList.size() << "D-Bus interfaces to authorize";
}

void SecurityLoaderHelper::parseJsonFile(const QString &filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly)) {
qCWarning(secLoader) << "Cannot open file:" << filePath;
return;
}

QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
file.close();

if (error.error != QJsonParseError::NoError) {
qCWarning(secLoader) << "JSON parse error in" << filePath << ":" << error.errorString();
return;
}

QJsonObject root = doc.object();
if (!root.contains("DestList") || !root["DestList"].isArray()) {
qCWarning(secLoader) << "Invalid config format in" << filePath << ": missing DestList";
return;
}

for (const auto &item : root["DestList"].toArray()) {
appendDest(item.toObject());
}
}

void SecurityLoaderHelper::appendDest(const QJsonObject &dest)
{
const QString dbusName = dest["DbusName"].toString();
const QString dbusPath = dest["DbusPath"].toString();
const QString dbusInterface = dest["DbusInterface"].toString();

if (dbusName.isEmpty() || dbusPath.isEmpty() || dbusInterface.isEmpty()) {
qCWarning(secLoader) << "Skip invalid D-Bus destination:" << dest;
return;
}

for (const auto &existing : m_destList) {
const QJsonObject current = existing.toObject();
if (current["DbusName"] == dbusName &&
current["DbusPath"] == dbusPath &&
current["DbusInterface"] == dbusInterface) {
return;
}
}

m_destList.append(dest);
qCInfo(secLoader) << " Added:" << dbusName << dbusPath << dbusInterface;
}

void SecurityLoaderHelper::appendCurrentUserAccountsUserDest()
{
const QString userPath = currentUserAccountsPath();
if (userPath.isEmpty()) {
qCWarning(secLoader) << "Cannot resolve current user's Accounts.User path";
return;
}

QJsonObject dest;
#ifdef ENABLE_DSS_SNIPE
dest["DbusName"] = QStringLiteral("org.deepin.dde.Accounts1");
dest["DbusPath"] = userPath;
dest["DbusInterface"] = QStringLiteral("org.deepin.dde.Accounts1.User");
#else
dest["DbusName"] = QStringLiteral("com.deepin.daemon.Accounts");
dest["DbusPath"] = userPath;
dest["DbusInterface"] = QStringLiteral("com.deepin.daemon.Accounts.User");
#endif
appendDest(dest);
}

QString SecurityLoaderHelper::currentUserAccountsPath() const
{
QDBusConnection systemBus = QDBusConnection::systemBus();
if (!systemBus.isConnected()) {
qCWarning(secLoader) << "Cannot connect to system bus when resolving current user path";
return {};
}

#ifdef ENABLE_DSS_SNIPE
QDBusInterface accountsInterface(QStringLiteral("org.deepin.dde.Accounts1"),
QStringLiteral("/org/deepin/dde/Accounts1"),
QStringLiteral("org.deepin.dde.Accounts1"),
systemBus);
#else
QDBusInterface accountsInterface(QStringLiteral("com.deepin.daemon.Accounts"),
QStringLiteral("/com/deepin/daemon/Accounts"),
QStringLiteral("com.deepin.daemon.Accounts"),
systemBus);
#endif
if (!accountsInterface.isValid()) {
qCWarning(secLoader) << "Accounts interface invalid when resolving current user path:"
<< accountsInterface.lastError().message();
return {};
}

QDBusReply<QString> reply = accountsInterface.call(QStringLiteral("FindUserById"), QString::number(getuid()));
if (!reply.isValid()) {
qCWarning(secLoader) << "FindUserById failed when resolving current user path:"
<< reply.error().message();
return {};
}

return reply.value();
}

bool SecurityLoaderHelper::performHandshake(int fd1, int fd2)
{
if (m_destList.isEmpty()) {
qCInfo(secLoader) << "No D-Bus interfaces loaded, skipping handshake";
return true;
}

qCInfo(secLoader) << "Performing loader handshake...";

QDBusConnection systemBus = QDBusConnection::systemBus();
if (!systemBus.isConnected()) {
qCWarning(secLoader) << "Cannot connect to system bus";
return false;
}
systemBus.interface()->isServiceRegistered("org.freedesktop.DBus");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这段代码是不是没有必要

QString uniqueName = systemBus.baseService();
qCInfo(secLoader) << "System Bus UniqueName:" << uniqueName;

QJsonObject request;
request["UniqueName"] = uniqueName;
request["DestList"] = m_destList;

QJsonDocument doc(request);
QByteArray jsonData = doc.toJson(QJsonDocument::Compact);

qCInfo(secLoader) << "Sending request with" << m_destList.size() << "interfaces";

QFile fd1File;
if (!fd1File.open(fd1, QIODevice::WriteOnly)) {
qCWarning(secLoader) << "Cannot open fd1 for writing";
return false;
}
fd1File.write(jsonData);
fd1File.close();
qCInfo(secLoader) << "Sent authorization request to loader";

QFile fd2File;
if (!fd2File.open(fd2, QIODevice::ReadOnly)) {
qCWarning(secLoader) << "Cannot open fd2 for reading";
return false;
}

QByteArray response = fd2File.readAll();
fd2File.close();

QJsonParseError parseError;
QJsonDocument responseDoc = QJsonDocument::fromJson(response, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qCWarning(secLoader) << "Invalid JSON response from loader:" << parseError.errorString();
return false;
}

QJsonObject result = responseDoc.object();
if (result["Result"].toBool()) {
qCInfo(secLoader) << "Loader handshake completed successfully";
return true;
} else {
qCWarning(secLoader) << "Loader authorization response:" << result["Message"].toString();
return false;
}
}
38 changes: 38 additions & 0 deletions src/dde-lock/securityloaderhelper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2024 - 2026 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef SECURITYLOADERHELPER_H
#define SECURITYLOADERHELPER_H

#include <QObject>
#include <QJsonArray>

class SecurityLoaderHelper : public QObject
{
Q_OBJECT

public:
static SecurityLoaderHelper &instance();
void doSecurityLoader(int fd1, int fd2);

private:
explicit SecurityLoaderHelper(QObject *parent = nullptr);
~SecurityLoaderHelper();

SecurityLoaderHelper(const SecurityLoaderHelper &) = delete;
SecurityLoaderHelper &operator=(const SecurityLoaderHelper &) = delete;

void loadConfig(const QString &configPath = QString());
void parseJsonFile(const QString &filePath);
void appendDest(const QJsonObject &dest);
void appendCurrentUserAccountsUserDest();
QString currentUserAccountsPath() const;
bool performHandshake(int fd1, int fd2);

private:
QJsonArray m_destList;
static const QString DEFAULT_CONFIG_PATH;
};

#endif // SECURITYLOADERHELPER_H
Loading