feat: add dp4 project

add dp4 project

Log:
Influence: dp4
master
donghualin 2 years ago
parent 0a9d408a78
commit 9c88f3c1ca
  1. 8
      CMakeLists.txt
  2. 97
      cmake/modules/FindPAM.cmake
  3. 5
      debian/changelog
  4. 1
      debian/compat
  5. 30
      debian/control
  6. 20
      debian/copyright
  7. 4
      debian/deepin-service-plugin-dp4.install
  8. 2
      debian/login-dp4-plugin.install
  9. 1
      debian/pam-dp4.install
  10. 6
      debian/rules
  11. 1
      debian/source/format
  12. 57
      dp4-dbus-service/CMakeLists.txt
  13. 23
      dp4-dbus-service/dp4service.cpp
  14. 34
      dp4-dbus-service/dp4service.h
  15. 8
      dp4-dbus-service/lupdate.sh
  16. 19
      dp4-dbus-service/org.deepin.service.dp4.conf
  17. 13
      dp4-dbus-service/plugin-dp4-service.json
  18. 34
      dp4-dbus-service/plugin.cpp
  19. 37
      dp4-login-plugin/CMakeLists.txt
  20. 97
      dp4-login-plugin/dp4_login_module.cpp
  21. 44
      dp4-login-plugin/dp4_login_module.h
  22. 4
      dp4-login-plugin/login.json
  23. 8
      dp4-login-plugin/lupdate.sh
  24. 20
      dp4-pam/CMakeLists.txt
  25. 41
      dp4-pam/pam_login.c
  26. 61
      example/CMakeLists.txt
  27. 18
      example/loginwidget.cpp
  28. 24
      example/loginwidget.h
  29. 47
      example/main.cpp
  30. 57
      example/pamtest.cpp
  31. 23
      example/pamtest.h
  32. 542
      example/service/policy.cpp
  33. 145
      example/service/policy.h
  34. 276
      example/service/qtdbushook.cpp
  35. 28
      example/service/qtdbushook.h
  36. 77
      example/service/servicebase.cpp
  37. 54
      example/service/servicebase.h
  38. 106
      example/service/serviceqtdbus.cpp
  39. 33
      example/service/serviceqtdbus.h
  40. 6
      lupdate.sh

@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.7)
project(dp4-uos)
add_subdirectory(dp4-login-plugin)
add_subdirectory(dp4-pam)
add_subdirectory(dp4-dbus-service)
add_subdirectory(example)

@ -0,0 +1,97 @@
# - Try to find the PAM libraries
# Once done this will define
#
# PAM_FOUND - system has pam
# PAM_INCLUDE_DIR - the pam include directory
# PAM_LIBRARIES - libpam library
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
# Already in cache, be silent
set(PAM_FIND_QUIETLY TRUE)
endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h)
find_library(PAM_LIBRARY pam)
find_library(DL_LIBRARY dl)
if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
set(PAM_FOUND TRUE)
if (DL_LIBRARY)
set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY})
else (DL_LIBRARY)
set(PAM_LIBRARIES ${PAM_LIBRARY})
endif (DL_LIBRARY)
if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
# darwin claims to be something special
set(HAVE_PAM_PAM_APPL_H 1)
endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
if (NOT DEFINED PAM_MESSAGE_CONST)
include(CheckCXXSourceCompiles)
# XXX does this work with plain c?
check_cxx_source_compiles("
#if ${HAVE_PAM_PAM_APPL_H}+0
# include <pam/pam_appl.h>
#else
# include <security/pam_appl.h>
#endif
static int PAM_conv(
int num_msg,
const struct pam_message **msg, /* this is the culprit */
struct pam_response **resp,
void *ctx)
{
return 0;
}
int main(void)
{
struct pam_conv PAM_conversation = {
&PAM_conv, /* this bombs out if the above does not match */
0
};
return 0;
}
" PAM_MESSAGE_CONST)
endif (NOT DEFINED PAM_MESSAGE_CONST)
set(PAM_MESSAGE_CONST ${PAM_MESSAGE_CONST} CACHE BOOL "PAM expects a conversation function with const pam_message")
endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
if (PAM_FOUND)
if (NOT PAM_FIND_QUIETLY)
message(STATUS "Found PAM: ${PAM_LIBRARIES}")
endif (NOT PAM_FIND_QUIETLY)
else (PAM_FOUND)
if (PAM_FIND_REQUIRED)
message(FATAL_ERROR "PAM was not found")
endif(PAM_FIND_REQUIRED)
endif (PAM_FOUND)
mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY PAM_MESSAGE_CONST)

5
debian/changelog vendored

@ -0,0 +1,5 @@
dp4-uos (1.0.0) unstable; urgency=medium
* support single sign-on
-- donghualin <donghualin@uniontech.com> Thu, 16 May 2024 11:07:48 +0800

1
debian/compat vendored

@ -0,0 +1 @@
9

30
debian/control vendored

@ -0,0 +1,30 @@
Source: dp4-uos
Section: libs
Priority: optional
Maintainer: Deepin Packages Builder <packages@deepin.com>
Build-Depends: debhelper (>= 8.0.0),
cmake,
qt5-default,
pkg-config,
dde-session-shell-dev,
qtbase5-dev,
qtbase5-private-dev,
qttools5-dev,
libpam0g-dev
Standards-Version: 4.5.1
Homepage: https://www.deepin.org
Package: deepin-service-plugin-dp4
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: the service of dp4.
Package: login-dp4-plugin
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: the plugin of login by dp4.
Package: pam-dp4
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: the package of dp4 for login by pam.

20
debian/copyright vendored

@ -0,0 +1,20 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: deepin-passkey
Files: *
Copyright: 2023 Deepin Technology Co., Ltd.
License: GPL-3+
This package is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
.
This package 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 General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".

@ -0,0 +1,4 @@
usr/lib/*/deepin-service-manager/libdp4-service.so
usr/share/deepin-service-manager/user/plugin-dp4-service.json
usr/share/dbus-1/system.d/org.deepin.service.dp4.conf
usr/share/dp4-service/translations

@ -0,0 +1,2 @@
usr/lib/dde-session-shell/modules/libdp4-login-plugin.so
usr/share/dp4-login-plugin/translations

@ -0,0 +1 @@
usr/lib/*/security

6
debian/rules vendored

@ -0,0 +1,6 @@
#!/usr/bin/make -f
include /usr/share/dpkg/default.mk
%:
dh $@ --buildsystem=cmake

@ -0,0 +1 @@
3.0 (native)

@ -0,0 +1,57 @@
cmake_minimum_required(VERSION 3.7)
set(PLUGIN_NAME "dp4-service")
project(${PLUGIN_NAME})
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
include(GNUInstallDirs)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
ADD_DEFINITIONS(-DQM_FILES_DIR="${CMAKE_BINARY_DIR}/dp4-service-plugin")
else()
ADD_DEFINITIONS(-DQM_FILES_DIR="/usr/share/deepin-service-manager/dp4-service-plugin/translations")
endif()
find_package(Qt5 COMPONENTS Core Widgets DBus Network LinguistTools REQUIRED)
file(GLOB TS_FILES "translations/*.ts")
qt5_add_translation(QM_FILES ${TS_FILES})
add_custom_target(${PLUGIN_NAME}_language ALL DEPENDS ${QM_FILES})
file(GLOB_RECURSE SRCS "*.h" "*.cpp")
add_library(${PLUGIN_NAME} MODULE
${SRCS}
)
target_include_directories(${PLUGIN_NAME} PUBLIC
Qt5::Core
Qt5::DBus
${Qt5Widget_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${DtkCore_INCLUDE_DIRS}
.
)
target_link_libraries(${PLUGIN_NAME} PRIVATE
Qt5::Core
Qt5::DBus
${Qt5Network_LIBRARIES}
${Qt5Widgets_LIBRARIES}
)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
EXECUTE_PROCESS(COMMAND mkdir -p ${CMAKE_BINARY_DIR}/share/system/)
EXECUTE_PROCESS(COMMAND mkdir -p ${CMAKE_BINARY_DIR}/share/user/)
EXECUTE_PROCESS(COMMAND cp -f ${CMAKE_CURRENT_SOURCE_DIR}/plugin-dp4-service.json ${CMAKE_BINARY_DIR}/share/user/)
else()
# just need this in your project
install(TARGETS ${PLUGIN_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/deepin-service-manager/)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/plugin-dp4-service.json DESTINATION share/deepin-service-manager/user/)
endif()
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.deepin.service.dp4.conf DESTINATION share/dbus-1/system.d/)
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/${PLUGIN_NAME}/translations)

@ -0,0 +1,23 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "dp4service.h"
using namespace dp4::service;
Dp4Service::Dp4Service(QObject *parent)
: QObject(parent)
{
QString test = tr("test");
}
QString Dp4Service::getToken()
{
return m_token;
}
void Dp4Service::setToken(const QString &token)
{
m_token = token;
}

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef SESSIONSERVICE_H
#define SESSIONSERVICE_H
#include <QObject>
namespace dp4 {
namespace service {
class Dp4Service : public QObject
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.deepin.service.dp4")
Q_PROPERTY(QString token READ getToken WRITE setToken)
public:
explicit Dp4Service(QObject *parent = nullptr);
private:
QString getToken();
void setToken(const QString &token);
private:
QString m_token;
};
}
}
#endif // SERVICE_H

@ -0,0 +1,8 @@
#!/bin/bash
if [ ! -d "translations/" ];then
mkdir translations
fi
cd ./translations
rm -f dp4-dbus-service.ts
lupdate ../ -ts -no-ui-lines -locations none -no-obsolete dp4-dbus-service.ts
cd ../

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- -->
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- Only root can own the service -->
<policy user="root">
<allow own="org.deepin.service.dp4"/>
<allow send_destination="org.deepin.service.dp4"/>
</policy>
<!-- Allow anyone to invoke methods on the interfaces -->
<policy context="default">
<allow send_destination="org.deepin.service.dp4"/>
</policy>
</busconfig>

@ -0,0 +1,13 @@
{
"name": "org.deepin.service.dp4",
"libPath": "libdp4-service.so",
"group": "core",
"policyStartType": "Resident",
"pluginType": "qt",
"startDelay": 0,
"policy": [
{
"path": "/org/deepin/service/dp4"
}
]
}

@ -0,0 +1,34 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "dp4service.h"
#include <QDBusConnection>
#include <QDebug>
#include <unistd.h>
static dp4::service::Dp4Service *serviceObject = nullptr;
extern "C" int DSMRegister(const char *name, void *data)
{
QDBusConnection::RegisterOptions opts = QDBusConnection::ExportAllSlots
| QDBusConnection::ExportAllSignals | QDBusConnection::ExportAllProperties;
QString path = name;
path = QString("/%1").arg(path.replace(".", "/"));
auto connection = reinterpret_cast<QDBusConnection *>(data);
serviceObject = new dp4::service::Dp4Service;
connection->registerObject(path, serviceObject, opts);
return 0;
}
// 该函数用于资源释放
// 非常驻插件必须实现该函数,以防内存泄漏
extern "C" int DSMUnRegister(const char *name, void *data)
{
(void)name;
(void)data;
serviceObject->deleteLater();
return 0;
}

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 3.7)
set(LIB_NAME "dp4-login-plugin")
project(${LIB_NAME})
include(GNUInstallDirs)
# qt moc
set(CMAKE_AUTOMOC ON)
find_package(Qt5 COMPONENTS Core Widgets DBus Network LinguistTools REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(DdeSessionShell REQUIRED)
pkg_check_modules(SSL REQUIRED IMPORTED_TARGET libcrypto libssl openssl)
file(GLOB TS_FILES "translations/*.ts")
qt5_add_translation(QM_FILES ${TS_FILES})
add_custom_target(${BIN_NAME}_language ALL DEPENDS ${QM_FILES})
file(GLOB_RECURSE SRCS "*.h" "*.cpp")
add_library(${LIB_NAME} SHARED ${SRCS})
target_include_directories(${LIB_NAME} PUBLIC
${Qt5DBus_INCLUDE_DIRS}
${DDESESSIONSHELL_INCLUDE_DIR}
)
target_link_libraries(${LIB_NAME} PRIVATE
${Qt5Widgets_LIBRARIES}
${Qt5DBus_LIBRARIES}
)
install(TARGETS ${LIB_NAME} LIBRARY DESTINATION lib/dde-session-shell/modules)# .qm
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/${LIB_NAME}/translations/)

@ -0,0 +1,97 @@
// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dp4_login_module.h"
namespace dss
{
namespace module
{
Dp4LoginModule::Dp4LoginModule(QObject *parent)
: QObject(parent)
, m_callbackFun(nullptr)
, m_callbackData(new AuthCallbackData)
, m_messageCallbackFunc(nullptr)
{
setObjectName(QStringLiteral("AssistLoginModule"));
QString testText = tr("test");
// 以下部分爲測試代碼,具體如何寫請根據實際的業務來處理即可
/*m_callbackData->account = "uos";
m_callbackData->token = "1";
m_callbackData->result = 0;
*/
}
Dp4LoginModule::~Dp4LoginModule()
{
if (m_callbackData) {
delete m_callbackData;
}
}
void Dp4LoginModule::init()
{
}
QWidget *Dp4LoginModule::content()
{
return nullptr;
}
void Dp4LoginModule::reset()
{
init();
}
void Dp4LoginModule::setCallback(LoginCallBack *callback)
{
m_callback = callback;
m_callbackFun = callback->authCallbackFun;
}
std::string Dp4LoginModule::onMessage(const std::string &message)
{
qInfo() << Q_FUNC_INFO;
QJsonParseError jsonParseError;
const QJsonDocument messageDoc = QJsonDocument::fromJson(QByteArray::fromStdString(message), &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError || messageDoc.isEmpty()) {
qWarning() << Q_FUNC_INFO << "Failed to obtain message from shell!: " << QString::fromStdString(message);
return "";
}
QJsonObject retObj;
retObj["Code"] = 0;
retObj["Message"] = "Success";
QJsonObject msgObj = messageDoc.object();
QString cmdType = msgObj.value("CmdType").toString();
QJsonObject data = msgObj.value("Data").toObject();
qInfo() << "Cmd type: " << cmdType;
if (cmdType == "CurrentUserChanged") {
} else if (cmdType == "GetConfigs") {
QJsonObject retDataObj;
retDataObj["ShowAvatar"] = false;
retDataObj["ShowUserName"] = false;
retDataObj["ShowSwitchButton"] = false;
retDataObj["ShowLockButton"] = false;
retDataObj["DefaultAuthLevel"] = DefaultAuthLevel::StrongDefault;
retDataObj["AuthType"] = AuthType::AT_Custom;
retObj["Data"] = retDataObj;
} else if (cmdType == "StartAuth") {
} else if (cmdType == "AuthState") {
} else if (cmdType == "LimitsInfo") {
}
QJsonDocument doc;
doc.setObject(retObj);
return doc.toJson().toStdString();
}
}
}

@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: 2015 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef ASSIST_LOGIN_MODULE_H
#define ASSIST_LOGIN_MODULE_H
#include "login_module_interface.h"
namespace dss
{
namespace module
{
class Dp4LoginModule: public QObject, public LoginModuleInterface
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.deepin.dde.shell.Modules.Login" FILE "login.json")
Q_INTERFACES(dss::module::LoginModuleInterface)
public:
explicit Dp4LoginModule(QObject *parent = nullptr);
~Dp4LoginModule() override;
void init() override;
ModuleType type() const override
{ return IpcAssistLoginType; }
inline QString key() const override
{ return "dp4-login"; }
QWidget *content() override;
void setCallback(LoginCallBack *callback) override;
std::string onMessage(const std::string &) override;
void reset() override;
private:
LoginCallBack *m_callback;
AuthCallbackFun m_callbackFun;
AuthCallbackData *m_callbackData;
MessageCallbackFun m_messageCallbackFunc;
};
}
}
#endif //ASSIST_LOGIN_MODULE_H

@ -0,0 +1,4 @@
{
"api": "1.2.0",
"pluginType": "Login"
}

@ -0,0 +1,8 @@
#!/bin/bash
if [ ! -d "translations/" ];then
mkdir translations
fi
cd ./translations
rm -f dp4-login-plugin.ts
lupdate ../ -ts -no-ui-lines -locations none -no-obsolete dp4-login-plugin.ts
cd ../

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.7)
set(PAM_LIB_NAME "dp4-login-pam")
file(GLOB_RECURSE LIB_SRCS
"*.h"
"*.c"
)
add_library(${PAM_LIB_NAME} MODULE
${LIB_SRCS}
)
set_target_properties(${PAM_LIB_NAME} PROPERTIES PREFIX "")
target_link_libraries(${PAM_LIB_NAME} PRIVATE
-lpam
)
install(TARGETS ${PAM_LIB_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}/security)

@ -0,0 +1,41 @@
#include <stdio.h>
#include <security/_pam_types.h>
#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include <sys/syslog.h>
// PAM 认证函数
PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
// 获取用户名
const char *username = NULL;
int nret = pam_get_item(pamh, PAM_USER, (const void **)&username);
pam_syslog(pamh, LOG_WARNING, "in test login,get username..%s..ret:%d.....", username, nret);
if (nret != PAM_SUCCESS) {
return PAM_AUTH_ERR;
}
// 获取密码
const char *password = NULL;
int retpwd = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password);
if (retpwd == PAM_SUCCESS) {
pam_syslog(pamh, LOG_WARNING, "password: %s", password);
} else {
pam_syslog(pamh, LOG_WARNING, "get password failed: %d", retpwd);
}
for (int i = 0; i < argc; i++) {
pam_syslog(pamh, LOG_WARNING, "input parameter: %s", argv[i]);
}
return PAM_SUCCESS;
}
PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
pam_syslog(pamh, LOG_WARNING, "in test login,pam_sm_setcred...., flags:%d, argc:%d", flags, argc);
for (int i = 0; i < argc; i++) {
pam_syslog(pamh, LOG_WARNING, "input parameter: %s", argv[i]);
}
return PAM_SUCCESS;
}

@ -0,0 +1,61 @@
cmake_minimum_required(VERSION 3.7)
project(example)
include(GNUInstallDirs)
if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX /usr)
endif ()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_PREFIX_PATH $ENV{Qt5_DIR})
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall")
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
ADD_DEFINITIONS(-DSERVICE_CONFIG_DIR="${CMAKE_BINARY_DIR}/share/")
ADD_DEFINITIONS(-DSERVICE_LIB_DIR="${CMAKE_BINARY_DIR}/network-service-plugin/")
else()
ADD_DEFINITIONS(-DSERVICE_CONFIG_DIR="${CMAKE_INSTALL_PREFIX}/share/deepin-service-manager/")
ADD_DEFINITIONS(-DSERVICE_LIB_DIR="${CMAKE_INSTALL_FULL_LIBDIR}/deepin-service-manager/")
endif()
file(GLOB_RECURSE COMPILEFILES *.h *.cpp ../dp4-login-plugin/*.h ../dp4-login-plugin/*.cpp)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -fsanitize=address -O0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fsanitize=address -O0")
endif()
add_executable(${PROJECT_NAME} ${COMPILEFILES})
find_package(PAM REQUIRED)
find_package(Qt5 COMPONENTS Core Widgets DBus Network REQUIRED)
find_package(DdeSessionShell REQUIRED)
set(TRY_KF5_LIBRARIES
IMPORTED_LOCATION_DEBIAN
IMPORTED_LOCATION_NOCONFIG
)
target_include_directories(${PROJECT_NAME} PUBLIC
./service
../dp4-login-plugin/
${PAM_INCLUDE_DIR}
${Qt5Widget_INCLUDE_DIRS}
${Qt5Core_INCLUDE_DIRS}
${Qt5DBus_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${DDESESSIONSHELL_INCLUDE_DIR}
)
target_link_libraries(${PROJECT_NAME} PRIVATE
${PAM_LIBRARIES}
${Qt5Widgets_LIBRARIES}
${Qt5Core_LIBRARIES}
${Qt5DBus_LIBRARIES}
${Qt5Network_LIBRARIES}
)

@ -0,0 +1,18 @@
#include "loginwidget.h"
#include "dp4_login_module.h"
#include <QHBoxLayout>
LoginWidget::LoginWidget(QWidget *parent)
: QWidget (parent)
, m_dp4module(new dss::module::Dp4LoginModule(this))
{
QHBoxLayout *layout = new QHBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(0);
layout->addWidget(m_dp4module->content());
}
LoginWidget::~LoginWidget()
{
}

@ -0,0 +1,24 @@
#ifndef LOGINWIDGET_H
#define LOGINWIDGET_H
#include <QWidget>
namespace dss {
namespace module {
class Dp4LoginModule;
}
}
class LoginWidget : public QWidget
{
Q_OBJECT
public:
LoginWidget(QWidget *parent = nullptr);
~LoginWidget();
private:
dss::module::Dp4LoginModule *m_dp4module;
};
#endif

@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include "serviceqtdbus.h"
#include "loginwidget.h"
#include "pamtest.h"
#include <QApplication>
#include <QDesktopWidget>
#include <QTranslator>
#include <unistd.h>
using namespace std;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
if (QString(argv[1]) == "service") {
// 新建deepinServiceManager的服务
QString fileName = QString("%1user/plugin-dp4-service.json").arg(SERVICE_CONFIG_DIR);
Policy policy(nullptr);
policy.parseConfig(fileName);
ServiceQtDBus service;
service.init(QDBusConnection::BusType::SessionBus, &policy);
return app.exec();
}
if (QString(argv[1]) == "login") {
// 登錄插件的demo
LoginWidget widget;
widget.show();
widget.resize(500, 500);
return app.exec();
}
if (QString(argv[1]) == "pam") {
// 測試pam
PamTest test;
test.startPam();
return app.exec();
}
return app.exec();
}

@ -0,0 +1,57 @@
#include "pamtest.h"
#include <QDebug>
#include <security/pam_appl.h>
PamTest::PamTest(QObject *parent)
: QObject (parent)
{
}
PamTest::~PamTest()
{
}
void PamTest::startPam()
{
pam_handle_t *pamHandle = nullptr;
pam_conv conv = {PAMConversation, static_cast<void *>(this)};
// pam_start第一個參數對應於/etc/pam.d下面的指定的PAM文件,第二個參數對應於登錄的賬戶名
int ret = pam_start("test_pam", "uos", &conv, &pamHandle);
if (ret != PAM_SUCCESS) {
//qCritical() << "ERROR: PAM start failed, error: " << pam_strerror(pamHandle, ret) << ", start infomation: " << ret;
} else {
qDebug() << "PAM start...";
}
// 可以在此處設置密碼
//const char *password = "testpassword";
//pam_set_item(pamHandle, PAM_AUTHTOK, password);
int rc = pam_authenticate(pamHandle, 0);
if (rc != PAM_SUCCESS) {
//qWarning() << "PAM authenticate failed, error: " << pam_strerror(pamHandle, rc) << ", PAM authenticate: " << rc;
} else {
qDebug() << "PAM authenticate finished.";
}
int tmpRt = pam_acct_mgmt(pamHandle, 1);
if (tmpRt != PAM_SUCCESS) {
//qCritical() << "PAM acct failed:" << pam_strerror(pamHandle, tmpRt) << "PAM end infomation: " << tmpRt;
} else {
qDebug() << "PAM acct finished.";
}
int re = pam_end(pamHandle, rc);
if (re != PAM_SUCCESS) {
//qCritical() << "PAM end failed:" << pam_strerror(pamHandle, re) << "PAM end infomation: " << re;
} else {
qDebug() << "PAM end...";
}
}
int PamTest::PAMConversation(int num_msg, const pam_message **msg, pam_response **resp, void *app_data)
{
return PAM_SUCCESS;
}

@ -0,0 +1,23 @@
#ifndef PAMTEST_H
#define PAMTEST_H
#include <QObject>
#include <security/_pam_types.h>
class PamTest : public QObject
{
Q_OBJECT
public:
PamTest(QObject *parent = nullptr);
~PamTest();
void startPam();
private:
static int PAMConversation(int num_msg, const pam_message **msg, pam_response **resp, void *app_data);
};
#endif

@ -0,0 +1,542 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "policy.h"
#include <QDebug>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QLoggingCategory>
Q_LOGGING_CATEGORY(dsm_policy, "[Policy]")
Policy::Policy(QObject *parent)
: QObject(parent)
{
}
bool Policy::checkPathHide(const QString &path)
{
QMapPathHide::iterator iter = mapPathHide.find(path);
if (iter == mapPathHide.end()) {
return false;
}
return iter.value();
}
bool Policy::checkMethodPermission(const QString &process,
const QString &path,
const QString &interface,
const QString &method)
{
return checkPermission(process, path, interface, method, CallDestType::Method);
}
bool Policy::checkPropertyPermission(const QString &process,
const QString &path,
const QString &interface,
const QString &property)
{
return checkPermission(process, path, interface, property, CallDestType::Property);
}
bool Policy::checkPermission(const QString &process,
const QString &path,
const QString &interface,
const QString &dest,
const CallDestType &type)
{
qCInfo(dsm_policy) << "check permission:"
<< QString("process=%1, path=%2, interface=%3, dest=%4")
.arg(process, path, interface, dest);
// 时间复杂度跟权限配置复杂度正相关,简单的配置就会校验很快
QMapPath::const_iterator iterPath = mapPath.find(path);
if (iterPath == mapPath.end()) {
// 默认不校验,即有权限
return true;
}
// PATH权限
const PolicyPath &policyPath = iterPath.value();
QMapInterface::const_iterator iterInterface = policyPath.interfaces.find(interface);
if (iterInterface == policyPath.interfaces.end()) {
// 没有配置interface权限,则用path权限
if (!policyPath.needPermission) {
return true;
}
return policyPath.processes.contains(process);
}
if (type == CallDestType::Method) {
// INTERFACE权限
const PolicyInterface &policyInterface = iterInterface.value();
QMapMethod::const_iterator iterMethod = policyInterface.methods.find(dest);
if (iterMethod == policyInterface.methods.end()) {
if (!policyInterface.needPermission) {
return true;
}
return policyInterface.processes.contains(process);
}
// METHOD权限
const PolicyMethod &policyMethod = iterMethod.value();
if (!policyMethod.needPermission) {
return true;
}
return policyMethod.processes.contains(process);
} else if (type == CallDestType::Property) {
// INTERFACE权限
const PolicyInterface &policyInterface = iterInterface.value();
QMapProperty::const_iterator iterProp = policyInterface.properties.find(dest);
if (iterProp == policyInterface.properties.end()) {
if (!policyInterface.needPermission) {
return true;
}
return policyInterface.processes.contains(process);
}
// PROPERTY权限
const PolicyProperty &policyProp = iterProp.value();
if (!policyProp.needPermission) {
return true;
}
return policyProp.processes.contains(process);
}
qCWarning(dsm_policy) << "check permission error!";
return false;
}
QStringList Policy::paths() const
{
return mapSubPath.keys();
}
bool Policy::allowSubPath(const QString &path) const
{
auto iter = mapSubPath.find(path);
if (iter != mapSubPath.end()) {
return iter.value();
}
return false;
}
bool Policy::isResident() const
{
return startType == "Resident";
}
void Policy::print()
{
qInfo() << "-------------------------------------";
qInfo() << "DBUS POLICY CONFIG";
qInfo() << "- name:" << name;
qInfo() << "- path hide";
for (QMapPathHide::iterator iter = mapPathHide.begin(); iter != mapPathHide.end(); iter++) {
qInfo() << "-- path hide:" << iter.key() << iter.value();
}
qInfo() << "- whitelist";
for (QMapWhitelists::iterator iter = mapWhitelist.begin(); iter != mapWhitelist.end(); iter++) {
qInfo() << "-- whitelist:" << iter.key() << iter.value().name << iter.value().process;
}
qInfo() << "- policy";
for (QMapPath::iterator iter = mapPath.begin(); iter != mapPath.end(); iter++) {
qInfo() << "-- path:" << iter.key() << iter.value().path;
qInfo() << "-- permission:" << iter.value().needPermission;
qInfo() << "-- whitelist:" << iter.value().processes;
for (QMapInterface::iterator iterInterface = iter.value().interfaces.begin();
iterInterface != iter.value().interfaces.end();
iterInterface++) {
qInfo() << "---- interface:" << iterInterface.key() << iterInterface.value().interface;
qInfo() << "---- permission:" << iterInterface.value().needPermission;
qInfo() << "---- whitelist:" << iterInterface.value().processes;
for (QMapMethod::iterator iterMethod = iterInterface.value().methods.begin();
iterMethod != iterInterface.value().methods.end();
iterMethod++) {
qInfo() << "------ method:" << iterMethod.key() << iterMethod.value().method;
qInfo() << "------ permission:" << iterMethod.value().needPermission;
qInfo() << "------ whitelist:" << iterMethod.value().processes;
}
for (QMapProperty::iterator iterProp = iterInterface.value().properties.begin();
iterProp != iterInterface.value().properties.end();
iterProp++) {
qInfo() << "------ property:" << iterProp.key() << iterProp.value().property;
qInfo() << "------ permission:" << iterProp.value().needPermission;
qInfo() << "------ whitelist:" << iterProp.value().processes;
}
}
}
qInfo() << "-------------------------------------";
}
void Policy::parseConfig(const QString &path)
{
qCInfo(dsm_policy) << "parse config:" << path;
if (path.isEmpty()) {
qCWarning(dsm_policy) << "path is empty!";
return;
}
QJsonDocument jsonDoc;
if (!readJsonFile(jsonDoc, path)) {
qCWarning(dsm_policy) << "read json file failed!";
return;
}
QJsonObject rootObj = jsonDoc.object();
jsonGetString(rootObj, "name", name);
jsonGetString(rootObj, "group", group, "app");
jsonGetString(rootObj, "libPath", pluginPath); // 兼容
jsonGetString(rootObj, "pluginPath", pluginPath, pluginPath);
jsonGetString(rootObj, "policyVersion", version, "1.0"); // 兼容
jsonGetString(rootObj, "version", version, version);
jsonGetString(rootObj, "policyStartType", startType, "Resident"); // 兼容
jsonGetString(rootObj, "startType", startType, startType);
jsonGetStringList(rootObj, "dependencies", dependencies);
jsonGetInt(rootObj, "startDelay", startDelay, 0);
jsonGetInt(rootObj, "idleTime", idleTime, 10);
// get SDKType
QString sdkTypeString;
jsonGetString(rootObj, "pluginType", sdkTypeString, "qt");
if (sdkTypeString == "qt")
sdkType = SDKType::QT;
if (sdkTypeString == "sd")
sdkType = SDKType::SD;
if (name.isEmpty()) {
qCWarning(dsm_policy) << "json error, name is empty.";
return;
}
if (!parseWhitelist(rootObj)) {
qCWarning(dsm_policy) << "json error, parse whitelist error.";
return;
}
if (!parsePolicy(rootObj)) {
qCWarning(dsm_policy) << "json error, parse policy error.";
return;
}
}
bool Policy::readJsonFile(QJsonDocument &outDoc, const QString &fileName)
{
QFile jsonFIle(fileName);
if (!jsonFIle.open(QIODevice::ReadOnly)) {
qCWarning(dsm_policy) << QString("open file: %1 error!").arg(fileName);
return false;
}
QJsonParseError jsonParserError;
outDoc = QJsonDocument::fromJson(jsonFIle.readAll(), &jsonParserError);
jsonFIle.close();
if (jsonParserError.error != QJsonParseError::NoError) {
qCWarning(dsm_policy) << "to json document error: " << jsonParserError.errorString();
return false;
}
if (outDoc.isNull()) {
qCWarning(dsm_policy) << "json document is null!";
return false;
}
return true;
}
// typedef QMap<QString, PolicyWhitelist> QMapWhitelists;
bool Policy::parseWhitelist(const QJsonObject &obj)
{
mapWhitelist.clear();
if (!obj.contains("whitelists")) {
// 为空,不是出错
return true;
}
QJsonValue listsValue = obj.value("whitelists");
if (!listsValue.isArray()) {
qCWarning(dsm_policy) << "parse whitelist error, must be json array!";
return false;
}
QJsonArray lists = listsValue.toArray();
for (int i = 0; i < lists.size(); ++i) {
QJsonValue whitelistValue = lists.at(i);
if (whitelistValue.isObject()) {
PolicyWhitelist whitelist;
QJsonObject whitelistObj = whitelistValue.toObject();
QString name;
jsonGetString(whitelistObj, "name", name);
if (name.isEmpty()) {
continue;
}
if (!whitelistObj.contains("process")) {
continue;
}
QJsonArray processes = whitelistObj.value("process").toArray();
if (processes.size() <= 0) {
continue;
}
whitelist.name = name;
for (int j = 0; j < processes.size(); ++j) {
if (processes.at(j).isString()) {
whitelist.process.append(processes.at(j).toString());
}
}
mapWhitelist.insert(name, whitelist);
}
}
return true;
}
bool Policy::parsePolicy(const QJsonObject &obj)
{
mapPathHide.clear();
mapPath.clear();
if (!obj.contains("policy")) {
// 为空,不是出错
return true;
}
QJsonValue policyValue = obj.value("policy");
if (!policyValue.isArray()) {
qCWarning(dsm_policy) << "parse policy error, must be json array!";
return false;
}
QJsonArray policyList = policyValue.toArray();
for (int i = 0; i < policyList.size(); ++i) {
QJsonValue policy = policyList.at(i);
if (!policy.isObject())
continue;
if (!parsePolicyPath(policy.toObject())) {
return false;
}
}
return true;
}
bool Policy::parsePolicyPath(const QJsonObject &obj)
{
QString path;
jsonGetString(obj, "path", path);
if (path.isEmpty()) {
qCWarning(dsm_policy) << "parse policy-path error, must be a string!";
return false;
}
bool pathHide;
jsonGetBool(obj, "pathhide", pathHide, false);
mapPathHide.insert(path, pathHide);
bool subpath;
jsonGetBool(obj, "subpath", subpath, false);
mapSubPath.insert(path, pathHide);
PolicyPath policyPath;
policyPath.path = path;
jsonGetBool(obj, "permission", policyPath.needPermission, false);
QString whitelist;
jsonGetString(obj, "whitelist", whitelist);
if (!whitelist.isEmpty()) {
QMapWhitelists::const_iterator iterWhitelist = mapWhitelist.find(whitelist);
if (iterWhitelist != mapWhitelist.end() && iterWhitelist.value().name == whitelist) {
policyPath.processes = iterWhitelist.value().process;
}
}
if (obj.contains("interfaces")) {
QJsonValue interfaces = obj.value("interfaces");
if (interfaces.isArray()) {
QJsonArray interfaceList = interfaces.toArray();
for (int i = 0; i < interfaceList.size(); ++i) {
QJsonValue interface = interfaceList.at(i);
if (interface.isObject()) {
bool ret = parsePolicyInterface(interface.toObject(), policyPath);
if (!ret) {
return false;
}
}
}
}
}
mapPath.insert(path, policyPath);
return true;
}
bool Policy::parsePolicyInterface(const QJsonObject &obj, PolicyPath &policyPath)
{
QString interface;
jsonGetString(obj, "interface", interface);
if (interface.isEmpty()) {
qCWarning(dsm_policy) << "parse policy-interface error, must be a string!";
return false;
}
PolicyInterface policyInterface;
policyInterface.interface = interface;
// interface没有指定permission,则使用上级path的permission
jsonGetBool(obj, "permission", policyInterface.needPermission, policyPath.needPermission);
QString whitelist;
jsonGetString(obj, "whitelist", whitelist);
if (!whitelist.isEmpty()) {
QMapWhitelists::const_iterator iterWhitelist = mapWhitelist.find(whitelist);
if (iterWhitelist != mapWhitelist.end() && iterWhitelist.value().name == whitelist) {
policyInterface.processes = iterWhitelist.value().process;
} // esle 错误的whitelist认为是空值
} else {
// interface没有指定whitelist,则使用上级path的whitelist
policyInterface.processes = policyPath.processes;
}
if (obj.contains("methods")) {
QJsonValue methods = obj.value("methods");
if (methods.isArray()) {
QJsonArray methodList = methods.toArray();
for (int i = 0; i < methodList.size(); ++i) {
QJsonValue method = methodList.at(i);
if (method.isObject()) {
bool ret = parsePolicyMethod(method.toObject(), policyInterface);
if (!ret) {
return false;
}
}
}
}
}
if (obj.contains("properties")) {
QJsonValue properties = obj.value("properties");
if (properties.isArray()) {
QJsonArray propertiesList = properties.toArray();
for (int i = 0; i < propertiesList.size(); ++i) {
QJsonValue property = propertiesList.at(i);
if (property.isObject()) {
bool ret = parsePolicyProperties(property.toObject(), policyInterface);
if (!ret) {
return false;
}
}
}
}
}
policyPath.interfaces.insert(interface, policyInterface);
return true;
}
bool Policy::parsePolicyMethod(const QJsonObject &obj, PolicyInterface &policyInterface)
{
QString method;
jsonGetString(obj, "method", method);
if (method.isEmpty()) {
qCWarning(dsm_policy) << "parse policy-method error, must be a string!";
return false;
}
PolicyMethod policyMethod;
policyMethod.method = method;
// method没有指定permission,则使用上级interface的permission
jsonGetBool(obj, "permission", policyMethod.needPermission, policyInterface.needPermission);
QString whitelist;
jsonGetString(obj, "whitelist", whitelist);
if (!whitelist.isEmpty()) {
QMapWhitelists::const_iterator iterWhitelist = mapWhitelist.find(whitelist);
if (iterWhitelist != mapWhitelist.end() && iterWhitelist.value().name == whitelist) {
policyMethod.processes = iterWhitelist.value().process;
}
} else {
// method没有指定whitelist,则使用上级interface的whitelist
policyMethod.processes = policyInterface.processes;
}
policyInterface.methods.insert(method, policyMethod);
return true;
}
bool Policy::parsePolicyProperties(const QJsonObject &obj, PolicyInterface &policyInterface)
{
QString property;
jsonGetString(obj, "property", property);
if (property.isEmpty()) {
qCWarning(dsm_policy) << "parse policy-property error, must be a string!";
return false;
}
PolicyProperty policyproperty;
policyproperty.property = property;
jsonGetBool(obj, "permission", policyproperty.needPermission, policyInterface.needPermission);
QString whitelist;
jsonGetString(obj, "whitelist", whitelist);
if (!whitelist.isEmpty()) {
QMapWhitelists::const_iterator iterWhitelist = mapWhitelist.find(whitelist);
if (iterWhitelist != mapWhitelist.end() && iterWhitelist.value().name == whitelist) {
policyproperty.processes = iterWhitelist.value().process;
}
} else {
policyproperty.processes = policyInterface.processes;
}
policyInterface.properties.insert(property, policyproperty);
return true;
}
bool Policy::jsonGetString(const QJsonObject &obj,
const QString &key,
QString &value,
QString defaultValue)
{
if (obj.contains(key)) {
const QJsonValue &v = obj.value(key);
if (v.isString()) {
value = v.toString();
return true;
}
}
value = defaultValue;
return false;
}
bool Policy::jsonGetStringList(const QJsonObject &obj,
const QString &key,
QStringList &value,
QStringList defaultValue)
{
value = defaultValue;
if (!obj.contains(key))
return false;
const QJsonValue &v = obj.value(key);
if (v.isString()) {
value.append(v.toString());
return true;
}
if (v.isArray()) {
const QJsonArray &array = v.toArray();
for (auto &&a : array) {
if (a.isString())
value.append(a.toString());
}
}
return true;
}
bool Policy::jsonGetBool(const QJsonObject &obj, const QString &key, bool &value, bool defaultValue)
{
if (obj.contains(key)) {
const QJsonValue &v = obj.value(key);
if (v.isBool()) {
value = v.toBool();
return true;
}
}
value = defaultValue;
return false;
}
bool Policy::jsonGetInt(const QJsonObject &obj, const QString &key, int &value, int defaultValue)
{
if (obj.contains(key)) {
const QJsonValue &v = obj.value(key);
if (v.isDouble()) {
value = v.toInt();
return true;
}
}
value = defaultValue;
return false;
}

@ -0,0 +1,145 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef POLICY_H
#define POLICY_H
#include <QMap>
#include <QObject>
enum class SDKType { QT, SD };
struct PolicyWhitelist
{
QString name;
QStringList process;
};
typedef QMap<QString, PolicyWhitelist> QMapWhitelists;
// hide, default:false
typedef QMap<QString, bool> QMapPathHide;
// subpath, default:false
typedef QMap<QString, bool> QMapSubPath;
// typedef QMap<QString, bool> QMapProcess;
struct PolicyMethod
{
QString method;
bool needPermission;
QStringList processes;
};
typedef QMap<QString, PolicyMethod> QMapMethod;
struct PolicyProperty
{
QString property;
bool needPermission;
QStringList processes;
};
typedef QMap<QString, PolicyProperty> QMapProperty;
struct PolicyInterface
{
QString interface;
bool needPermission;
QStringList processes;
QMapMethod methods;
QMapProperty properties;
};
typedef QMap<QString, PolicyInterface> QMapInterface;
struct PolicyPath
{
QString path;
bool needPermission;
QStringList processes;
QMapInterface interfaces;
};
typedef QMap<QString, PolicyPath> QMapPath;
enum CallDestType { Method, Property };
class Policy : public QObject
{
Q_OBJECT
public:
explicit Policy(QObject *parent = nullptr);
void parseConfig(const QString &path);
bool checkPathHide(const QString &path);
bool checkMethodPermission(const QString &process,
const QString &path,
const QString &interface,
const QString &method);
bool checkPropertyPermission(const QString &process,
const QString &path,
const QString &interface,
const QString &property);
bool checkPermission(const QString &process,
const QString &path,
const QString &interface,
const QString &dest,
const CallDestType &type);
QStringList paths() const;
bool allowSubPath(const QString &path) const;
bool isResident() const;
// void Check(); // TODO
void print();
private:
bool readJsonFile(QJsonDocument &outDoc, const QString &fileName);
bool parseWhitelist(const QJsonObject &obj);
bool parsePolicy(const QJsonObject &obj);
bool parsePolicyPath(const QJsonObject &obj);
bool parsePolicyInterface(const QJsonObject &obj, PolicyPath &policyPath);
bool parsePolicyMethod(const QJsonObject &obj, PolicyInterface &policyInterface);
bool parsePolicyProperties(const QJsonObject &obj, PolicyInterface &policyInterface);
bool jsonGetString(const QJsonObject &obj,
const QString &key,
QString &value,
QString defaultValue = "");
bool jsonGetStringList(const QJsonObject &obj,
const QString &key,
QStringList &value,
QStringList defaultValue = {});
bool
jsonGetBool(const QJsonObject &obj, const QString &key, bool &value, bool defaultValue = false);
bool jsonGetInt(const QJsonObject &obj, const QString &key, int &value, int defaultValue = 0);
public:
// 数据定义
// 插入速度要求不高,查询速度要求很高,因此解析json的结果会通过冗余、预处理来提高查询速度,即以查询的速度角度来定义结构
// 配置文件和此处数据没有一一对应,解析文件时,需要为此处数据服务,填充相关数据
// 如果文件没有配置某参数,那也不允许空值,根据数据向下继承,比如某个参数interface没有设置,则interface的这个参数继承path
// 不允许空值的作用是减少查询时的逻辑判断,把这些逻辑处理转移到解析文件阶段
// 隐藏 - In:path,Out:pathhide
// 权限PATH - In:process、path,Out:bool
// 权限INTERFACE - In:process、path、interface,Out:bool
// 权限METHOD - In:process、path、interface、method,Out:bool
// 权限PROPERTIES - In:process、path、interface,Out:bool
QMapWhitelists mapWhitelist;
QMapPathHide mapPathHide;
QMapSubPath mapSubPath;
QMapPath mapPath;
public:
QString name;
QString group;
QString pluginPath;
QString version;
QString startType;
QStringList dependencies;
SDKType sdkType;
int startDelay;
int idleTime;
};
#endif // POLICY_H

@ -0,0 +1,276 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "qtdbushook.h"
#include "policy.h"
#include "serviceqtdbus.h"
#include <QDBusConnectionInterface>
#include <QDBusMessage>
#include <QDebug>
#include <QFile>
#include <QLoggingCategory>
#include <QTimer>
#ifdef Q_DBUS_EXPORT
extern Q_DBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
extern Q_DBUS_EXPORT void qDBusAddFilterHook(int (*)(const QString &, const QDBusMessage &));
#else
extern QDBUS_EXPORT void qDBusAddSpyHook(void (*)(const QDBusMessage &));
extern QDBUS_EXPORT void qDBusAddFilterHook(int (*)(const QString &, const QDBusMessage &));
#endif
#ifdef Q_DBUS_HOOK_FILTER
# define HOOK_RESULT_TYPE int
# define HOOK_RESULT_SUCCESS 0
# define HOOK_RESULT_FAILED -1
#else
# define HOOK_RESULT_TYPE void
# define HOOK_RESULT_SUCCESS
# define HOOK_RESULT_FAILED
#endif
Q_LOGGING_CATEGORY(dsm_hook_qt, "[QDBusHook]")
QString getCMD(ServiceBase *obj, QString dbusService)
{
ServiceQtDBus *srv = qobject_cast<ServiceQtDBus *>(obj);
if (!srv) {
return "";
}
const unsigned int &pid = srv->qDbusConnection().interface()->servicePid(dbusService).value();
qCDebug(dsm_hook_qt) << "--pid:" << pid;
QFile procCmd("/proc/" + QString::number(pid) + "/cmdline");
QString cmd;
if (procCmd.open(QIODevice::ReadOnly)) {
QList<QByteArray> cmds = procCmd.readAll().split('\0');
cmd = QString(cmds.first());
qCDebug(dsm_hook_qt) << "--cmd:" << cmd;
}
return cmd;
}
// if it is not a local message, hook exec at main thread
void QTDBusSpyHook(const QDBusMessage &msg)
{
qCInfo(dsm_hook_qt) << "--msg=" << msg;
// qInfo() << "--Handler ThreadID:" << QThread::currentThreadId();
ServiceBase *serviceObj = nullptr;
bool isSubPath;
QString realPath; // 子PATH可能没有配置,使用父PATH的配置
bool findRet = QTDbusHook::instance()->getServiceObject("",
msg.path(),
&serviceObj,
isSubPath,
realPath);
if (!findRet) {
qCWarning(dsm_hook_qt) << "--can not find hook object: " << msg.path();
return;
}
if (!serviceObj->isRegister()) {
qCInfo(dsm_hook_qt) << "--to register dbus object: " << msg.path();
serviceObj->registerService();
}
if (!serviceObj->policy->isResident() && !serviceObj->isLockTimer()) {
qCInfo(dsm_hook_qt) << QString("--service: %1 will unregister in %2 minutes!")
.arg(serviceObj->policy->name)
.arg(serviceObj->policy->idleTime);
QTimer::singleShot(0, serviceObj, SLOT(restartTimer()));
}
if (msg.member() == "Introspect" && msg.interface() == "org.freedesktop.DBus.Introspectable") {
if (serviceObj->policy->checkPathHide(realPath)) {
qCDebug(dsm_hook_qt) << "--call Introspect" << msg.path() << " ,is hided!";
QList<QVariant> arguments;
arguments << "";
QDBusMessage reply = msg.createReply(arguments);
ServiceQtDBus *srv = qobject_cast<ServiceQtDBus *>(serviceObj);
if (srv) {
srv->qDbusConnection().send(reply);
}
// ((ServiceQtDBus*)serviceObj)->qDbusConnection().send(reply);
}
} else if (msg.member() == "Set" && msg.interface() == "org.freedesktop.DBus.Properties") {
const QList<QVariant> &args = msg.arguments();
if (args.size() >= 2) {
if (!serviceObj->policy->checkPropertyPermission(getCMD(serviceObj, msg.service()),
realPath,
args.at(0).toString(),
args.at(1).toString())) {
QDBusMessage reply = msg.createErrorReply("com.deepin.service.Permission.Deny",
"The call is deny");
ServiceQtDBus *srv = qobject_cast<ServiceQtDBus *>(serviceObj);
if (srv) {
srv->qDbusConnection().send(reply);
return;
}
}
}
} else if (msg.interface() != "org.freedesktop.DBus.Properties"
&& msg.interface() != "org.freedesktop.DBus.Introspectable"
&& msg.interface() != "org.freedesktop.DBus.Peer") {
if (!serviceObj->policy->checkMethodPermission(getCMD(serviceObj, msg.service()),
realPath,
msg.interface(),
msg.member())) {
QDBusMessage reply =
msg.createErrorReply("com.deepin.service.Permission.Deny", "The call is deny2");
ServiceQtDBus *srv = qobject_cast<ServiceQtDBus *>(serviceObj);
if (srv) {
// srv->qDbusConnection().send(reply);
// QDBusConnection::sessionBus().send(reply);
QDBusConnection::connectToBus(QDBusConnection::SessionBus,
QString("org.dsdsf.dsfsdf"))
.send(reply);
return;
}
}
}
return;
}
// if it is not a local message, hook exec at main thread
int QTDBusHook(const QString &baseService, const QDBusMessage &msg)
{
qCInfo(dsm_hook_qt) << "--baseService=" << baseService;
qCInfo(dsm_hook_qt) << "--msg=" << msg;
// qInfo() << "--Handler ThreadID:" << QThread::currentThreadId();
ServiceBase *serviceObj = nullptr;
bool isSubPath;
QString realPath; // 子PATH可能没有配置,使用父PATH的配置
bool findRet = QTDbusHook::instance()->getServiceObject("",
msg.path(),
&serviceObj,
isSubPath,
realPath);
if (!findRet) {
qCWarning(dsm_hook_qt) << "--can not find hook object:" << msg.path();
return 0;
}
if (!serviceObj->isRegister()) {
qCInfo(dsm_hook_qt) << "--to register dbus object: " << msg.path();
serviceObj->registerService();
}
if (!serviceObj->policy->isResident() && !serviceObj->isLockTimer()) {
qCInfo(dsm_hook_qt) << QString("--service: %1 will unregister in 10 minutes!")
.arg(serviceObj->policy->name);
QTimer::singleShot(0, serviceObj, SLOT(restartTimer()));
}
if (msg.member() == "Introspect" && msg.interface() == "org.freedesktop.DBus.Introspectable") {
if (serviceObj->policy->checkPathHide(realPath)) {
qCDebug(dsm_hook_qt) << "--call Introspect" << msg.path() << " ,is hided!";
QList<QVariant> arguments;
arguments << "";
QDBusMessage reply = msg.createReply(arguments);
ServiceQtDBus *srv = qobject_cast<ServiceQtDBus *>(serviceObj);
if (srv) {
srv->qDbusConnection().send(reply);
}
// ((ServiceQtDBus*)serviceObj)->qDbusConnection().send(reply);
}
} else if (msg.member() == "Set" && msg.interface() == "org.freedesktop.DBus.Properties") {
const QList<QVariant> &args = msg.arguments();
if (args.size() >= 2) {
if (!serviceObj->policy->checkPropertyPermission(getCMD(serviceObj, msg.service()),
realPath,
args.at(0).toString(),
args.at(1).toString())) {
QDBusMessage reply = msg.createErrorReply("com.deepin.service.Permission.Deny",
"The call is deny");
ServiceQtDBus *srv = qobject_cast<ServiceQtDBus *>(serviceObj);
if (srv) {
srv->qDbusConnection().send(reply);
return -1;
}
}
}
} else if (msg.interface() != "org.freedesktop.DBus.Properties"
&& msg.interface() != "org.freedesktop.DBus.Introspectable"
&& msg.interface() != "org.freedesktop.DBus.Peer") {
if (!serviceObj->policy->checkMethodPermission(getCMD(serviceObj, msg.service()),
realPath,
msg.interface(),
msg.member())) {
QDBusMessage reply =
msg.createErrorReply("com.deepin.service.Permission.Deny", "The call is deny2");
ServiceQtDBus *srv = qobject_cast<ServiceQtDBus *>(serviceObj);
if (srv) {
srv->qDbusConnection().send(reply);
return -1;
}
}
}
return 0;
// test TODO
// if (msg.member() == "Register") {
// Policy ppp;
// ppp.Test();
// QList<QVariant> arguments;
// arguments << true << "sdvvv";
// QDBusMessage reply =
// msg.createErrorReply("com.deepin.services.Nooooooo", "The method
// call 'Register()' is not supported");
// QDBusConnection::connectToBus(QDBusConnection::SessionBus,
// "org.deepin.services.demo2").send(reply);
// }
}
Q_GLOBAL_STATIC(QTDbusHook, qtDBusHook)
QTDbusHook::QTDbusHook()
{
qCDebug(dsm_hook_qt) << "qt hook register.";
#ifdef Q_DBUS_HOOK_FILTER
qDBusAddFilterHook(QTDBusHook);
#else
qDBusAddSpyHook(QTDBusSpyHook);
#endif
}
QTDbusHook *QTDbusHook::instance()
{
return qtDBusHook;
}
bool QTDbusHook::getServiceObject(
QString name, QString path, ServiceBase **service, bool &isSubPath, QString &realPath)
{
Q_UNUSED(name) // TODO:QtDBus Hook 无法获取到name
ServiceObjectMap::iterator iterService = m_serviceMap.find(path);
if (iterService != m_serviceMap.end()) {
*service = iterService.value();
isSubPath = true;
realPath = iterService.key();
return true;
}
for (auto iter = m_serviceMap.begin(); iter != m_serviceMap.end(); ++iter) {
if (path.startsWith(iter.key()) && iter.value()->policy->allowSubPath(iter.key())) {
*service = iter.value();
isSubPath = false;
realPath = iter.key();
return true;
}
}
return false;
}
bool QTDbusHook::setServiceObject(ServiceBase *obj)
{
QStringList paths = obj->policy->paths();
for (auto path : paths) {
ServiceObjectMap::iterator iterService = m_serviceMap.find(path);
if (iterService != m_serviceMap.end()) {
qCWarning(dsm_hook_qt) << "set service path failed, the object is existed: " << path;
continue;
}
m_serviceMap[path] = obj;
}
return true;
}

@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef QTDBUSHOOK_H
#define QTDBUSHOOK_H
#include "servicebase.h"
typedef QMap<QString, ServiceBase *> ServiceObjectMap;
class QTDbusHook
{
public:
explicit QTDbusHook();
bool getServiceObject(
QString name, QString path, ServiceBase **service, bool &isSubPath, QString &realPath);
bool setServiceObject(ServiceBase *obj);
static QTDbusHook *instance();
private:
ServiceObjectMap m_serviceMap;
};
#endif // QTDBUSHOOK_H

@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "servicebase.h"
#include "policy.h"
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDebug>
#include <QThread>
#include <QTimer>
ServiceBase::ServiceBase(QObject *parent)
: QObject(parent)
, policy(nullptr)
, m_isRegister(false)
, m_isLockTimer(false)
, m_timer(new QTimer(this))
{
m_timer->setSingleShot(true);
m_timer->setInterval(10 * 60 * 1000); // 设置超时时间, 默认10分钟
connect(m_timer, &QTimer::timeout, this, &ServiceBase::idleSignal);
}
ServiceBase::~ServiceBase() { }
void ServiceBase::init(const QDBusConnection::BusType &busType, Policy *p)
{
m_sessionType = busType;
policy = p;
p->setParent(this);
// p->Print();
m_timer->setInterval(policy->idleTime * 60 * 1000); // 设置超时时间
connect(this, &ServiceBase::idleSignal, this, &ServiceBase::unregisterService);
initService();
}
void ServiceBase::initService()
{
QThread *th = new QThread();
setParent(nullptr);
moveToThread(th);
connect(th, &QThread::started, this, &ServiceBase::initThread);
th->start();
}
void ServiceBase::initThread() { }
bool ServiceBase::isRegister() const
{
return m_isRegister;
}
bool ServiceBase::isLockTimer() const
{
return m_isLockTimer;
}
void ServiceBase::restartTimer()
{
m_timer->start();
}
bool ServiceBase::registerService()
{
m_isRegister = true;
return true;
}
bool ServiceBase::unregisterService()
{
m_isRegister = false;
return true;
}

@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef SERVICEBASE_H
#define SERVICEBASE_H
#include "policy.h"
#include <QDBusConnection>
#include <QObject>
typedef void *(*ServiceObject)(const char *path, const int len);
typedef int (*DSMRegister)(const char *name, void *data);
typedef int (*DSMUnRegister)(const char *name, void *data);
class QTimer;
class ServiceBase : public QObject
{
Q_OBJECT
public:
explicit ServiceBase(QObject *parent = nullptr);
virtual ~ServiceBase();
bool isRegister() const;
bool isLockTimer() const;
virtual bool registerService();
virtual bool unregisterService();
Q_SIGNALS:
void idleSignal();
public Q_SLOTS:
void init(const QDBusConnection::BusType &busType, Policy *p);
void restartTimer();
protected:
virtual void initService();
virtual void initThread();
public:
Policy *policy;
protected:
bool m_isRegister;
bool m_isLockTimer;
QDBusConnection::BusType m_sessionType;
SDKType m_SDKType; // qdbus、sdbus
QTimer *m_timer;
};
#endif // SERVICEBASE_H

@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "serviceqtdbus.h"
#include "policy.h"
#include "qtdbushook.h"
#include <QDBusAbstractAdaptor>
#include <QDebug>
#include <QFileInfo>
#include <QLibrary>
#include <QLoggingCategory>
#include <QMetaClassInfo>
#include <QThread>
Q_LOGGING_CATEGORY(dsm_service_qt, "[QDBusService]")
ServiceQtDBus::ServiceQtDBus(QObject *parent)
: ServiceBase(parent)
, m_library(nullptr)
{
m_SDKType = SDKType::QT;
}
QDBusConnection ServiceQtDBus::qDbusConnection()
{
if (policy->name.isEmpty()) {
return m_sessionType == QDBusConnection::SystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus();
}
return m_sessionType == QDBusConnection::SystemBus ? QDBusConnection::connectToBus(QDBusConnection::SystemBus, policy->name)
: QDBusConnection::connectToBus(QDBusConnection::SessionBus, policy->name);
}
void ServiceQtDBus::initThread()
{
qCInfo(dsm_service_qt) << "init service: " << policy->name << "paths: " << policy->paths();
qDbusConnection().registerService(policy->name);
// TODO: 无权限、隐藏、按需启动需求的service,不应该注册,避免触发hook,提高效率
QTDbusHook::instance()->setServiceObject(this);
QFileInfo fileInfo(QString(SERVICE_LIB_DIR) + policy->pluginPath);
if (QLibrary::isLibrary(fileInfo.absoluteFilePath())) {
qCInfo(dsm_service_qt) << "init library: " << fileInfo.absoluteFilePath();
m_library = new QLibrary(fileInfo.absoluteFilePath());
}
if (!registerService()) {
qCWarning(dsm_service_qt) << "register service failed: " << policy->name;
}
ServiceBase::initThread();
}
bool ServiceQtDBus::registerService()
{
qCInfo(dsm_service_qt) << "service register: " << policy->name;
if (libFuncCall("DSMRegister", true)) {
ServiceBase::registerService();
return true;
}
return false;
}
bool ServiceQtDBus::unregisterService()
{
qCInfo(dsm_service_qt) << "service unregister: " << policy->name;
if (libFuncCall("DSMUnRegister", false)) {
ServiceBase::unregisterService();
return true;
}
return false;
}
bool ServiceQtDBus::libFuncCall(const QString &funcName, bool isRegister)
{
if (m_library == nullptr) {
return false;
}
auto objFunc = isRegister ? DSMRegister(m_library->resolve(funcName.toStdString().c_str()))
: DSMUnRegister(m_library->resolve(funcName.toStdString().c_str()));
if (!objFunc) {
qCWarning(dsm_service_qt)
<< QString("failed to resolve the method: %1\n file: %2\n error message: %3")
.arg(funcName)
.arg(m_library->fileName())
.arg(m_library->errorString());
if (m_library->isLoaded())
m_library->unload();
m_library->deleteLater();
return false;
}
auto connection = qDbusConnection();
int ret = objFunc(policy->name.toStdString().c_str(), (void *)&connection);
if (ret) {
return false;
}
return true;
}

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef SERVICEQTDBUS_H
#define SERVICEQTDBUS_H
#include "servicebase.h"
class QLibrary;
class ServiceQtDBus : public ServiceBase
{
Q_OBJECT
public:
explicit ServiceQtDBus(QObject *parent = nullptr);
QDBusConnection qDbusConnection();
virtual bool registerService() override;
virtual bool unregisterService() override;
protected:
virtual void initThread() override;
private:
bool libFuncCall(const QString &funcName, bool isRegister);
private:
QLibrary *m_library;
};
#endif // SERVICEQTDBUS_H

@ -0,0 +1,6 @@
cd dp4-dbus-service
sh ./lupdate.sh
cd ..
cd dp4-login-plugin
sh ./lupdate.sh
cd ..
Loading…
Cancel
Save