From 25379e147d8f4c0dfc93fda79506412c784bd6a9 Mon Sep 17 00:00:00 2001 From: fuleyi Date: Thu, 13 Aug 2026 17:12:33 +0800 Subject: [PATCH] feat: integrate security loader into session startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Launch managed sessions through the security loader wrapper. 2. Validate loader pipes and authorize Power1 system bus access. 3. Update service startup, package dependencies, and helper tests. Log: Secure Power1 access during managed session startup Influence: 1. Verify startup succeeds with the security loader configured. 2. Verify fallback startup when the security loader is unavailable. 3. Run securityloaderhelper_test and exercise power mode recovery. feat: 集成会话启动安全加载器 1. 通过安全加载器包装脚本启动受管理的会话进程。 2. 校验加载器管道并授权访问 Power1 系统总线服务。 3. 更新服务启动方式、软件包依赖及辅助函数测试。 Log: 受管理会话启动时安全访问 Power1 服务 Influence: 1. 验证已配置安全加载器时会话可正常启动。 2. 验证安全加载器不可用时回退启动正常。 3. 运行 securityloaderhelper_test 并验证电源模式恢复。 PMS: TASK-393313 --- CMakeLists.txt | 2 + debian/control | 3 +- misc/dde-session-loader-wrapper.in | 32 ++ src/dde-session/CMakeLists.txt | 22 ++ src/dde-session/main.cpp | 16 +- src/dde-session/securityloaderhelper.cpp | 293 ++++++++++++++++++ src/dde-session/securityloaderhelper.h | 63 ++++ src/dde-session/securityloaderhelper_test.cpp | 151 +++++++++ systemd/dde-session-manager.service.in | 7 +- 9 files changed, 583 insertions(+), 6 deletions(-) create mode 100755 misc/dde-session-loader-wrapper.in create mode 100644 src/dde-session/securityloaderhelper.cpp create mode 100644 src/dde-session/securityloaderhelper.h create mode 100644 src/dde-session/securityloaderhelper_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c5d45d..28854d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ set(DTK_VERSION_MAJOR 6) find_package(DtkBuildHelper REQUIRED) find_package(Dtk${DTK_VERSION_MAJOR} REQUIRED COMPONENTS Tools) +include(CTest) + macro(install_symlink filepath wantsdir) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/link/${wantsdir}/) execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/lib/systemd/user/${filepath} ${PROJECT_BINARY_DIR}/link/${wantsdir}/${filepath}) diff --git a/debian/control b/debian/control index ff9279d..d05cfc6 100644 --- a/debian/control +++ b/debian/control @@ -25,7 +25,8 @@ Architecture: any Depends: jq, sed (>= 4), - dde-daemon (>= 6.1.26), + dde-daemon (>= 6.1.102), + deepin-security-loader, libdtkdata (>=5.7.18), libdtk6core (>=6.0.38), systemd, diff --git a/misc/dde-session-loader-wrapper.in b/misc/dde-session-loader-wrapper.in new file mode 100755 index 0000000..e4cda31 --- /dev/null +++ b/misc/dde-session-loader-wrapper.in @@ -0,0 +1,32 @@ +#!/bin/bash +# Log to systemd journal with tag "dde-session" +log_to_journal() { + logger -t dde-session -p user.info -- "$1" +} + +set -u +PATH=/usr/sbin:/usr/bin:/sbin:/bin +export PATH + +REAL_BINARY="@CMAKE_INSTALL_FULL_LIBEXECDIR@/deepin/dde-session" +LOADER="/usr/bin/deepin-security-loader" +LOADER_EXEC="/usr/bin/deepin-security-loader-exec" + +systemd_service=false +for argument in "$@"; do + if [ "$argument" = "--systemd-service" ]; then + systemd_service=true + break + fi +done +log_to_journal "dde-session launched with args: $*" + +if [ -x "$LOADER" ] && [ -x "$LOADER_EXEC" ] && \ + getcap "$LOADER_EXEC" 2>/dev/null | grep -q 'cap_setgid'; then + log_to_journal "Using deepin-security-loader with authorization groups" + exec "$LOADER" --group deepin-daemon -- "$REAL_BINARY" "$@" +fi + +# Fallback: direct launch without loader (no polkit-free authorization) +log_to_journal "Fallback: launching directly without security loader" +exec "$REAL_BINARY" "$@" diff --git a/src/dde-session/CMakeLists.txt b/src/dde-session/CMakeLists.txt index 2a01988..0213635 100644 --- a/src/dde-session/CMakeLists.txt +++ b/src/dde-session/CMakeLists.txt @@ -55,6 +55,8 @@ file(GLOB_RECURSE DBUS_TYPES ${PROJECT_SOURCE_DIR}/dbus/types/*) add_executable(dde-session main.cpp + securityloaderhelper.h + securityloaderhelper.cpp environmentsmanager.h environmentsmanager.cpp othersmanager.h @@ -100,3 +102,23 @@ target_include_directories(dde-session PUBLIC ) install(TARGETS dde-session DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS dde-session DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/deepin) + +configure_file(${PROJECT_SOURCE_DIR}/misc/dde-session-loader-wrapper.in + ${PROJECT_BINARY_DIR}/misc/dde-session-loader-wrapper + @ONLY) + +install(PROGRAMS ${PROJECT_BINARY_DIR}/misc/dde-session-loader-wrapper + DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/deepin) + +if(BUILD_TESTING) + add_executable(securityloaderhelper_test + securityloaderhelper_test.cpp + securityloaderhelper.cpp + ) + target_link_libraries(securityloaderhelper_test + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::DBus + ) + add_test(NAME securityloaderhelper_test COMMAND securityloaderhelper_test) +endif() diff --git a/src/dde-session/main.cpp b/src/dde-session/main.cpp index 16bb474..db37a7c 100644 --- a/src/dde-session/main.cpp +++ b/src/dde-session/main.cpp @@ -13,7 +13,6 @@ #include -#include #include #include #include @@ -28,6 +27,7 @@ #include "impl/iowait/iowaitwatcher.h" #include "environmentsmanager.h" #include "othersmanager.h" +#include "securityloaderhelper.h" DCORE_USE_NAMESPACE @@ -77,7 +77,11 @@ int main(int argc, char *argv[]) parser.addVersionOption(); QCommandLineOption systemd(QStringList{"d", "systemd-service", "wait for systemd services"}); + QCommandLineOption fd1(QStringLiteral("fd1"), QStringLiteral("security loader request pipe"), QStringLiteral("fd")); + QCommandLineOption fd2(QStringLiteral("fd2"), QStringLiteral("security loader response pipe"), QStringLiteral("fd")); parser.addOption(systemd); + parser.addOption(fd1); + parser.addOption(fd2); parser.process(app); DLogManager::registerJournalAppender(); @@ -112,6 +116,15 @@ int main(int argc, char *argv[]) } /* ---systemd-service--- */ + QString securityLoaderError; + const SecurityLoaderInfo loaderInfo = parseSecurityLoaderFds(parser.isSet(fd1), parser.value(fd1), + parser.isSet(fd2), parser.value(fd2), + &securityLoaderError); + const QString systemBusUniqueName = systemBusConnection().baseService(); + if (loaderInfo.loaded && !authorizePowerCaller(loaderInfo, systemBusUniqueName, &securityLoaderError)) { + qCritical() << "Security loader handshake failed:" << securityLoaderError; + return EXIT_FAILURE; + } auto* session = new Session(&app); new Session1Adaptor(session); @@ -156,7 +169,6 @@ int main(int argc, char *argv[]) qInfo() << "pipe read finish, app exit."; QMetaObject::invokeMethod(qApp, &QCoreApplication::quit, Qt::QueuedConnection); }); - sd_notify(0, "READY=1"); return app.exec(); } diff --git a/src/dde-session/securityloaderhelper.cpp b/src/dde-session/securityloaderhelper.cpp new file mode 100644 index 0000000..e6525aa --- /dev/null +++ b/src/dde-session/securityloaderhelper.cpp @@ -0,0 +1,293 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "securityloaderhelper.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace { +constexpr qsizetype MaxResponseSize = 1024 * 1024; +constexpr int HandshakeTimeoutMs = 5000; +void setErrorMessage(QString *errorMessage, const QString &message) +{ + if (errorMessage) + *errorMessage = message; +} + + +bool parseFd(const QString &value, int *fd) +{ + bool ok = false; + const qlonglong parsed = value.toLongLong(&ok, 10); + if (!ok || parsed < 3 || parsed > INT_MAX) + return false; + *fd = static_cast(parsed); + return true; +} + +bool validatePipeFd(int fd, int expectedAccessMode, QString *errorMessage) +{ + struct stat status; + if (fstat(fd, &status) != 0) { + setErrorMessage(errorMessage, QStringLiteral("cannot inspect loader pipe: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + return false; + } + if (!S_ISFIFO(status.st_mode)) { + setErrorMessage(errorMessage, QStringLiteral("loader file descriptor is not a pipe")); + return false; + } + + const int flags = fcntl(fd, F_GETFL); + if (flags < 0) { + setErrorMessage(errorMessage, QStringLiteral("cannot inspect loader pipe access mode: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + return false; + } + if ((flags & O_ACCMODE) != expectedAccessMode) { + setErrorMessage(errorMessage, QStringLiteral("loader pipe has an unexpected access mode")); + return false; + } + return true; +} + +bool closeFd(int fd, QString *errorMessage) +{ + if (close(fd) == 0 || errno == EINTR) + return true; + + setErrorMessage(errorMessage, QStringLiteral("cannot close loader pipe: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + return false; +} + +bool writeRequest(int fd, const QByteArray &request, QElapsedTimer *timer, QString *errorMessage) +{ + const int flags = fcntl(fd, F_GETFL); + if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + setErrorMessage(errorMessage, QStringLiteral("cannot make loader request pipe non-blocking: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + closeFd(fd, nullptr); + return false; + } + + qsizetype written = 0; + while (written < request.size()) { + const int remaining = HandshakeTimeoutMs - static_cast(timer->elapsed()); + if (remaining <= 0) { + setErrorMessage(errorMessage, QStringLiteral("timed out writing security loader request")); + closeFd(fd, nullptr); + return false; + } + + pollfd descriptor = {fd, POLLOUT, 0}; + int result; + do { + result = poll(&descriptor, 1, remaining); + } while (result < 0 && errno == EINTR); + if (result == 0) { + setErrorMessage(errorMessage, QStringLiteral("timed out writing security loader request")); + closeFd(fd, nullptr); + return false; + } + if (result < 0) { + setErrorMessage(errorMessage, QStringLiteral("cannot poll loader request: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + closeFd(fd, nullptr); + return false; + } + + const ssize_t count = write(fd, request.constData() + written, + static_cast(request.size() - written)); + if (count > 0) { + written += count; + continue; + } + if (count < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) + continue; + setErrorMessage(errorMessage, QStringLiteral("cannot write loader request: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + closeFd(fd, nullptr); + return false; + } + return closeFd(fd, errorMessage); +} + +bool readResponse(int fd, QByteArray *response, QElapsedTimer *timer, QString *errorMessage) +{ + const int flags = fcntl(fd, F_GETFL); + if (flags < 0 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + setErrorMessage(errorMessage, QStringLiteral("cannot make loader response pipe non-blocking: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + closeFd(fd, nullptr); + return false; + } + + char buffer[4096]; + while (true) { + const int remaining = HandshakeTimeoutMs - static_cast(timer->elapsed()); + if (remaining <= 0) { + setErrorMessage(errorMessage, QStringLiteral("timed out waiting for security loader response")); + closeFd(fd, nullptr); + return false; + } + + pollfd descriptor = {fd, POLLIN | POLLHUP, 0}; + int result; + do { + result = poll(&descriptor, 1, remaining); + } while (result < 0 && errno == EINTR); + if (result == 0) { + setErrorMessage(errorMessage, QStringLiteral("timed out waiting for security loader response")); + closeFd(fd, nullptr); + return false; + } + if (result < 0) { + setErrorMessage(errorMessage, QStringLiteral("cannot poll loader response: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + closeFd(fd, nullptr); + return false; + } + + while (true) { + const ssize_t count = read(fd, buffer, sizeof(buffer)); + if (count > 0) { + if (response->size() + count > MaxResponseSize) { + setErrorMessage(errorMessage, QStringLiteral("security loader response exceeds size limit")); + closeFd(fd, nullptr); + return false; + } + response->append(buffer, count); + continue; + } + if (count == 0) + return closeFd(fd, errorMessage); + if (errno == EINTR) + continue; + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + setErrorMessage(errorMessage, QStringLiteral("cannot read loader response: %1") + .arg(QString::fromLocal8Bit(strerror(errno)))); + closeFd(fd, nullptr); + return false; + } + } +} +} + +const QDBusConnection &systemBusConnection() +{ + static const QDBusConnection connection = QDBusConnection::systemBus(); + return connection; +} + +SecurityLoaderInfo parseSecurityLoaderFds(bool hasRequestFd, + const QString &requestFd, + bool hasResponseFd, + const QString &responseFd, + QString *errorMessage) +{ + SecurityLoaderInfo info; + info.loaded = hasRequestFd || hasResponseFd; + if (!info.loaded) + return info; + if (!hasRequestFd || !hasResponseFd) { + setErrorMessage(errorMessage, QStringLiteral("security loader requires both --fd1 and --fd2")); + return info; + } + if (!parseFd(requestFd, &info.requestFd) || !parseFd(responseFd, &info.responseFd)) { + setErrorMessage(errorMessage, QStringLiteral("security loader file descriptors are invalid")); + info.requestFd = -1; + info.responseFd = -1; + return info; + } + if (info.requestFd == info.responseFd) { + setErrorMessage(errorMessage, QStringLiteral("security loader file descriptors must be distinct")); + closeFd(info.requestFd, nullptr); + info.requestFd = -1; + info.responseFd = -1; + } + return info; +} + +QByteArray buildPowerAuthorizationRequest(const QString &uniqueName) +{ + QJsonObject destination; + destination.insert(QStringLiteral("DbusName"), QStringLiteral("org.deepin.dde.Power1")); + destination.insert(QStringLiteral("DbusPath"), QStringLiteral("/org/deepin/dde/Power1")); + destination.insert(QStringLiteral("DbusInterface"), QStringLiteral("org.deepin.dde.Power1")); + + QJsonObject request; + request.insert(QStringLiteral("UniqueName"), uniqueName); + request.insert(QStringLiteral("DestList"), QJsonArray{destination}); + return QJsonDocument(request).toJson(QJsonDocument::Compact); +} + +bool authorizePowerCaller(const SecurityLoaderInfo &info, const QString &uniqueName, QString *errorMessage) +{ + if (!info.loaded) + return true; + if (info.requestFd < 0 || info.responseFd < 0) { + setErrorMessage(errorMessage, QStringLiteral("invalid file descriptors")); + if (info.requestFd >= 0) + closeFd(info.requestFd, nullptr); + if (info.responseFd >= 0) + closeFd(info.responseFd, nullptr); + return false; + } + if (!validatePipeFd(info.requestFd, O_WRONLY, errorMessage) + || !validatePipeFd(info.responseFd, O_RDONLY, errorMessage)) { + closeFd(info.requestFd, nullptr); + closeFd(info.responseFd, nullptr); + return false; + } + + if (uniqueName.isEmpty() || !uniqueName.startsWith(QLatin1Char(':'))) { + setErrorMessage(errorMessage, QStringLiteral("system bus unique name is unavailable")); + closeFd(info.requestFd, nullptr); + closeFd(info.responseFd, nullptr); + return false; + } + + QElapsedTimer timer; + timer.start(); + if (!writeRequest(info.requestFd, buildPowerAuthorizationRequest(uniqueName), &timer, errorMessage)) { + closeFd(info.responseFd, nullptr); + return false; + } + + QByteArray response; + if (!readResponse(info.responseFd, &response, &timer, errorMessage)) + return false; + + QJsonParseError parseError; + const QJsonDocument document = QJsonDocument::fromJson(response, &parseError); + if (parseError.error != QJsonParseError::NoError || !document.isObject()) { + setErrorMessage(errorMessage, QStringLiteral("invalid security loader response")); + return false; + } + const QJsonObject result = document.object(); + if (!result.value(QStringLiteral("Result")).toBool(false)) { + QString msg = result.value(QStringLiteral("Message")).toString(QStringLiteral("authorization denied")); + for (QChar &character : msg) { + if (!character.isPrint()) + character = QLatin1Char(' '); + } + setErrorMessage(errorMessage, msg); + return false; + } + return true; +} diff --git a/src/dde-session/securityloaderhelper.h b/src/dde-session/securityloaderhelper.h new file mode 100644 index 0000000..ad3be85 --- /dev/null +++ b/src/dde-session/securityloaderhelper.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef SECURITYLOADERHELPER_H +#define SECURITYLOADERHELPER_H + +#include +#include +#include + +/*! + * @brief 安全加载器管道信息,用于与 deepin-security-loader 进程通信 + * + * 当 dde-session 通过 deepin-security-loader 启动时,加载器会传入两个 + * 文件描述符(--fd1, --fd2),分别用于向加载器发送鉴权请求和接收响应。 + * 该结构体由 parseSecurityLoaderFds() 填充,生命周期由调用者管理。 + */ +struct SecurityLoaderInfo +{ + /*! 是否通过安全加载器启动(即命令行包含 --fd1/--fd2 参数) */ + bool loaded = false; + /*! 写入端管道 fd,用于向加载器发送鉴权请求,loaded 为 false 时无效 */ + int requestFd = -1; + /*! 读取端管道 fd,用于接收加载器鉴权响应,loaded 为 false 时无效 */ + int responseFd = -1; +}; +/*! + * @brief 获取系统总线连接(单例) + * @return 系统总线 QDBusConnection 引用,生命周期与进程一致 + */ +const QDBusConnection &systemBusConnection(); +/*! + * @brief 解析命令行 --fd1/--fd2 参数为 SecurityLoaderInfo + * @param hasRequestFd 是否包含 --fd1 参数 + * @param requestFd --fd1 的参数值,文件描述符的数字字符串 + * @param hasResponseFd 是否包含 --fd2 参数 + * @param responseFd --fd2 的参数值,文件描述符的数字字符串 + * @param errorMessage 可选的错误输出;传入 nullptr 时忽略错误文本 + * @return 解析后的 SecurityLoaderInfo;loaded 字段指示是否成功 + */ +SecurityLoaderInfo parseSecurityLoaderFds(bool hasRequestFd, + const QString &requestFd, + bool hasResponseFd, + const QString &responseFd, + QString *errorMessage); +/*! + * @brief 构建发送给安全加载器的 Power1 鉴权请求 JSON + * @param uniqueName D-Bus 系统总线唯一名称(如 ":1.42") + * @return Compact JSON 字节数组,包含 UniqueName 和 DestList 字段 + */ +QByteArray buildPowerAuthorizationRequest(const QString &uniqueName); +/*! + * @brief 通过安全加载器鉴权 org.deepin.dde.Power1 接口的调用者 + * @param info 安全加载器管道信息 + * @param uniqueName D-Bus 系统总线唯一名称 + * @param errorMessage 可选的错误输出;传入 nullptr 时忽略错误文本 + * @return true 鉴权通过(或未通过安全加载器启动);false 鉴权失败 + * @note 当 info.loaded 为 false 时直接返回 true(非安全加载器启动路径) + */ +bool authorizePowerCaller(const SecurityLoaderInfo &info, const QString &uniqueName, QString *errorMessage); + +#endif // SECURITYLOADERHELPER_H diff --git a/src/dde-session/securityloaderhelper_test.cpp b/src/dde-session/securityloaderhelper_test.cpp new file mode 100644 index 0000000..b67d329 --- /dev/null +++ b/src/dde-session/securityloaderhelper_test.cpp @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "securityloaderhelper.h" + +#include +#include +#include +#include + +#include +#include +#include + +namespace { +bool require(bool condition, const char *message) +{ + if (condition) + return true; + std::cerr << message << std::endl; + return false; +} +} + +int main(int argc, char *argv[]) +{ + QCoreApplication application(argc, argv); + QString error; + + const SecurityLoaderInfo direct = parseSecurityLoaderFds(false, {}, false, {}, &error); + if (!require(!direct.loaded && error.isEmpty(), "direct invocation was treated as loader invocation")) + return 1; + + error.clear(); + const SecurityLoaderInfo incomplete = parseSecurityLoaderFds(true, QStringLiteral("7"), false, {}, &error); + if (!require(incomplete.loaded && incomplete.requestFd < 0 && !error.isEmpty(), + "incomplete loader arguments were accepted")) + return 1; + + const SecurityLoaderInfo incompleteWithoutError = + parseSecurityLoaderFds(true, QStringLiteral("7"), false, {}, nullptr); + if (!require(incompleteWithoutError.loaded && incompleteWithoutError.requestFd < 0, + "incomplete loader arguments crashed without an error output")) + return 1; + + SecurityLoaderInfo invalidHandshake; + invalidHandshake.loaded = true; + if (!require(!authorizePowerCaller(invalidHandshake, QStringLiteral(":1.1"), nullptr), + "invalid descriptors were accepted without an error output")) + return 1; + + error.clear(); + const SecurityLoaderInfo valid = parseSecurityLoaderFds(true, QStringLiteral("7"), + true, QStringLiteral("8"), &error); + if (!require(valid.loaded && valid.requestFd == 7 && valid.responseFd == 8 && error.isEmpty(), + "valid loader arguments were rejected")) + return 1; + + const QString uniqueName = QStringLiteral(":1.42"); + const QJsonDocument document = QJsonDocument::fromJson(buildPowerAuthorizationRequest(uniqueName)); + if (!require(document.isObject(), "authorization request is not a JSON object")) + return 1; + const QJsonObject request = document.object(); + if (!require(request.value(QStringLiteral("UniqueName")).toString() == uniqueName, + "authorization request has the wrong unique name")) + return 1; + const QJsonArray destinations = request.value(QStringLiteral("DestList")).toArray(); + if (!require(destinations.size() == 1, "authorization request has an unexpected destination count")) + return 1; + const QJsonObject power = destinations.at(0).toObject(); + if (!require(power.value(QStringLiteral("DbusName")).toString() == QStringLiteral("org.deepin.dde.Power1") + && power.value(QStringLiteral("DbusPath")).toString() == QStringLiteral("/org/deepin/dde/Power1") + && power.value(QStringLiteral("DbusInterface")).toString() == QStringLiteral("org.deepin.dde.Power1"), + "authorization request has the wrong Power1 destination")) + return 1; + + int requestPipe[2]; + int responsePipe[2]; + if (!require(pipe2(requestPipe, O_CLOEXEC) == 0 && pipe2(responsePipe, O_CLOEXEC) == 0, + "cannot create handshake test pipes")) + return 1; + + const QByteArray acceptedResponse = QByteArrayLiteral("{\"Result\":true,\"Message\":\"\"}"); + if (!require(write(responsePipe[1], acceptedResponse.constData(), acceptedResponse.size()) == acceptedResponse.size(), + "cannot seed loader response")) + return 1; + close(responsePipe[1]); + + SecurityLoaderInfo handshake; + handshake.loaded = true; + handshake.requestFd = requestPipe[1]; + handshake.responseFd = responsePipe[0]; + error.clear(); + const QString handshakeUniqueName = QStringLiteral(":1.99"); + if (!require(authorizePowerCaller(handshake, handshakeUniqueName, &error), "valid pipe handshake was rejected")) + return 1; + + const QByteArray sentRequest = [&requestPipe] { + QByteArray data; + char buffer[1024]; + ssize_t count; + while ((count = read(requestPipe[0], buffer, sizeof(buffer))) > 0) + data.append(buffer, count); + close(requestPipe[0]); + return data; + }(); + const QJsonObject sentObject = QJsonDocument::fromJson(sentRequest).object(); + const QString sentUniqueName = sentObject.value(QStringLiteral("UniqueName")).toString(); + if (!require(sentUniqueName == handshakeUniqueName, + "handshake did not send the supplied system bus unique name")) + return 1; + + int deniedRequestPipe[2]; + int deniedResponsePipe[2]; + if (!require(pipe2(deniedRequestPipe, O_CLOEXEC) == 0 + && pipe2(deniedResponsePipe, O_CLOEXEC) == 0, + "cannot create denied handshake test pipes")) + return 1; + + const QByteArray deniedResponse = QByteArrayLiteral( + "{\"Result\":false,\"Message\":\"denied\\n\\tesc\\u001b[31m\\u0007\"}"); + if (!require(write(deniedResponsePipe[1], deniedResponse.constData(), deniedResponse.size()) + == deniedResponse.size(), + "cannot seed denied loader response")) + return 1; + close(deniedResponsePipe[1]); + + SecurityLoaderInfo deniedHandshake; + deniedHandshake.loaded = true; + deniedHandshake.requestFd = deniedRequestPipe[1]; + deniedHandshake.responseFd = deniedResponsePipe[0]; + error.clear(); + if (!require(!authorizePowerCaller(deniedHandshake, handshakeUniqueName, &error), + "denied pipe handshake was accepted")) + return 1; + close(deniedRequestPipe[0]); + + bool errorIsPrintable = true; + for (const QChar character : error) { + if (!character.isPrint()) { + errorIsPrintable = false; + break; + } + } + if (!require(errorIsPrintable && error.contains(QStringLiteral("denied")), + "loader denial message retained control characters")) + return 1; + + return 0; +} diff --git a/systemd/dde-session-manager.service.in b/systemd/dde-session-manager.service.in index 972ad6d..4d2db4e 100644 --- a/systemd/dde-session-manager.service.in +++ b/systemd/dde-session-manager.service.in @@ -9,9 +9,10 @@ CollectMode=inactive-or-failed Before=dde-session-pre.target [Service] -Type=notify -NotifyAccess=main -ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/dde-session --systemd-service +Type=forking +GuessMainPID=yes +ExecStart=@CMAKE_INSTALL_FULL_LIBEXECDIR@/deepin/dde-session-loader-wrapper --systemd-service +ExecStartPost=-/usr/bin/gdbus wait --session --timeout=30 org.deepin.dde.Session1 ExecStopPost=@CMAKE_INSTALL_FULL_LIBEXECDIR@/dde-session-ctl --shutdown ExecStopPost=-/bin/sh -c 'test "$SERVICE_RESULT" != "exec-condition" && systemctl --user unset-environment XDG_CURRENT_DESKTOP' ExecStopPost=-/bin/sh -c 'test "$SERVICE_RESULT" != "exec-condition" && systemctl --user unset-environment DISPLAY'