diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..64da364 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/cmake/modules/FindPAM.cmake b/cmake/modules/FindPAM.cmake new file mode 100644 index 0000000..99bdf11 --- /dev/null +++ b/cmake/modules/FindPAM.cmake @@ -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 +#else +# include +#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) diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..6d556ac --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +dp4-uos (1.0.0) unstable; urgency=medium + + * support single sign-on + + -- donghualin Thu, 16 May 2024 11:07:48 +0800 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..f11c82a --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 \ No newline at end of file diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..17c934f --- /dev/null +++ b/debian/control @@ -0,0 +1,30 @@ +Source: dp4-uos +Section: libs +Priority: optional +Maintainer: Deepin Packages Builder +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. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..fd8fb83 --- /dev/null +++ b/debian/copyright @@ -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 + . + On Debian systems, the complete text of the GNU General + Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". diff --git a/debian/deepin-service-plugin-dp4.install b/debian/deepin-service-plugin-dp4.install new file mode 100644 index 0000000..5a76a20 --- /dev/null +++ b/debian/deepin-service-plugin-dp4.install @@ -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 \ No newline at end of file diff --git a/debian/login-dp4-plugin.install b/debian/login-dp4-plugin.install new file mode 100644 index 0000000..3f84b79 --- /dev/null +++ b/debian/login-dp4-plugin.install @@ -0,0 +1,2 @@ +usr/lib/dde-session-shell/modules/libdp4-login-plugin.so +usr/share/dp4-login-plugin/translations \ No newline at end of file diff --git a/debian/pam-dp4.install b/debian/pam-dp4.install new file mode 100644 index 0000000..300d761 --- /dev/null +++ b/debian/pam-dp4.install @@ -0,0 +1 @@ +usr/lib/*/security \ No newline at end of file diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..c7b00d0 --- /dev/null +++ b/debian/rules @@ -0,0 +1,6 @@ +#!/usr/bin/make -f + +include /usr/share/dpkg/default.mk + +%: + dh $@ --buildsystem=cmake \ No newline at end of file diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/dp4-dbus-service/CMakeLists.txt b/dp4-dbus-service/CMakeLists.txt new file mode 100644 index 0000000..7bf0030 --- /dev/null +++ b/dp4-dbus-service/CMakeLists.txt @@ -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) diff --git a/dp4-dbus-service/dp4service.cpp b/dp4-dbus-service/dp4service.cpp new file mode 100644 index 0000000..c4d588b --- /dev/null +++ b/dp4-dbus-service/dp4service.cpp @@ -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; +} diff --git a/dp4-dbus-service/dp4service.h b/dp4-dbus-service/dp4service.h new file mode 100644 index 0000000..73107f5 --- /dev/null +++ b/dp4-dbus-service/dp4service.h @@ -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 + +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 diff --git a/dp4-dbus-service/lupdate.sh b/dp4-dbus-service/lupdate.sh new file mode 100755 index 0000000..78c07a1 --- /dev/null +++ b/dp4-dbus-service/lupdate.sh @@ -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 ../ diff --git a/dp4-dbus-service/org.deepin.service.dp4.conf b/dp4-dbus-service/org.deepin.service.dp4.conf new file mode 100644 index 0000000..59888f4 --- /dev/null +++ b/dp4-dbus-service/org.deepin.service.dp4.conf @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/dp4-dbus-service/plugin-dp4-service.json b/dp4-dbus-service/plugin-dp4-service.json new file mode 100644 index 0000000..62a9f61 --- /dev/null +++ b/dp4-dbus-service/plugin-dp4-service.json @@ -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" + } + ] +} diff --git a/dp4-dbus-service/plugin.cpp b/dp4-dbus-service/plugin.cpp new file mode 100644 index 0000000..a2c6de2 --- /dev/null +++ b/dp4-dbus-service/plugin.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "dp4service.h" + +#include +#include + +#include + +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(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; +} diff --git a/dp4-login-plugin/CMakeLists.txt b/dp4-login-plugin/CMakeLists.txt new file mode 100644 index 0000000..185a547 --- /dev/null +++ b/dp4-login-plugin/CMakeLists.txt @@ -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/) diff --git a/dp4-login-plugin/dp4_login_module.cpp b/dp4-login-plugin/dp4_login_module.cpp new file mode 100644 index 0000000..c588473 --- /dev/null +++ b/dp4-login-plugin/dp4_login_module.cpp @@ -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(); +} + +} +} diff --git a/dp4-login-plugin/dp4_login_module.h b/dp4-login-plugin/dp4_login_module.h new file mode 100644 index 0000000..38fe628 --- /dev/null +++ b/dp4-login-plugin/dp4_login_module.h @@ -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 diff --git a/dp4-login-plugin/login.json b/dp4-login-plugin/login.json new file mode 100644 index 0000000..d7d25a6 --- /dev/null +++ b/dp4-login-plugin/login.json @@ -0,0 +1,4 @@ +{ + "api": "1.2.0", + "pluginType": "Login" +} \ No newline at end of file diff --git a/dp4-login-plugin/lupdate.sh b/dp4-login-plugin/lupdate.sh new file mode 100755 index 0000000..44016cc --- /dev/null +++ b/dp4-login-plugin/lupdate.sh @@ -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 ../ diff --git a/dp4-pam/CMakeLists.txt b/dp4-pam/CMakeLists.txt new file mode 100644 index 0000000..601abc2 --- /dev/null +++ b/dp4-pam/CMakeLists.txt @@ -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) diff --git a/dp4-pam/pam_login.c b/dp4-pam/pam_login.c new file mode 100644 index 0000000..ef0af9f --- /dev/null +++ b/dp4-pam/pam_login.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include + +// 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; +} diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..8db5fb6 --- /dev/null +++ b/example/CMakeLists.txt @@ -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} +) diff --git a/example/loginwidget.cpp b/example/loginwidget.cpp new file mode 100644 index 0000000..54eba96 --- /dev/null +++ b/example/loginwidget.cpp @@ -0,0 +1,18 @@ +#include "loginwidget.h" +#include "dp4_login_module.h" + +#include + +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() +{ +} diff --git a/example/loginwidget.h b/example/loginwidget.h new file mode 100644 index 0000000..1a1c3b9 --- /dev/null +++ b/example/loginwidget.h @@ -0,0 +1,24 @@ +#ifndef LOGINWIDGET_H +#define LOGINWIDGET_H + +#include + +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 diff --git a/example/main.cpp b/example/main.cpp new file mode 100644 index 0000000..bec264e --- /dev/null +++ b/example/main.cpp @@ -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 +#include +#include + +#include + +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(); +} diff --git a/example/pamtest.cpp b/example/pamtest.cpp new file mode 100644 index 0000000..534f889 --- /dev/null +++ b/example/pamtest.cpp @@ -0,0 +1,57 @@ +#include "pamtest.h" + +#include + +#include + +PamTest::PamTest(QObject *parent) + : QObject (parent) +{ +} + +PamTest::~PamTest() +{ +} + +void PamTest::startPam() +{ + pam_handle_t *pamHandle = nullptr; + pam_conv conv = {PAMConversation, static_cast(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; +} diff --git a/example/pamtest.h b/example/pamtest.h new file mode 100644 index 0000000..7dd91d0 --- /dev/null +++ b/example/pamtest.h @@ -0,0 +1,23 @@ +#ifndef PAMTEST_H +#define PAMTEST_H + +#include + +#include + +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 diff --git a/example/service/policy.cpp b/example/service/policy.cpp new file mode 100644 index 0000000..24e9c15 --- /dev/null +++ b/example/service/policy.cpp @@ -0,0 +1,542 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "policy.h" + +#include +#include +#include +#include +#include +#include + +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 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; +} diff --git a/example/service/policy.h b/example/service/policy.h new file mode 100644 index 0000000..c12cb50 --- /dev/null +++ b/example/service/policy.h @@ -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 +#include + +enum class SDKType { QT, SD }; + +struct PolicyWhitelist +{ + QString name; + QStringList process; +}; + +typedef QMap QMapWhitelists; +// hide, default:false +typedef QMap QMapPathHide; +// subpath, default:false +typedef QMap QMapSubPath; + +// typedef QMap QMapProcess; +struct PolicyMethod +{ + QString method; + bool needPermission; + QStringList processes; +}; + +typedef QMap QMapMethod; + +struct PolicyProperty +{ + QString property; + bool needPermission; + QStringList processes; +}; + +typedef QMap QMapProperty; + +struct PolicyInterface +{ + QString interface; + bool needPermission; + QStringList processes; + QMapMethod methods; + QMapProperty properties; +}; + +typedef QMap QMapInterface; + +struct PolicyPath +{ + QString path; + bool needPermission; + QStringList processes; + QMapInterface interfaces; +}; + +typedef QMap 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 diff --git a/example/service/qtdbushook.cpp b/example/service/qtdbushook.cpp new file mode 100644 index 0000000..98f73f1 --- /dev/null +++ b/example/service/qtdbushook.cpp @@ -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 +#include +#include +#include +#include +#include + +#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(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 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 arguments; + arguments << ""; + QDBusMessage reply = msg.createReply(arguments); + ServiceQtDBus *srv = qobject_cast(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 &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(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(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 arguments; + arguments << ""; + QDBusMessage reply = msg.createReply(arguments); + ServiceQtDBus *srv = qobject_cast(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 &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(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(serviceObj); + if (srv) { + srv->qDbusConnection().send(reply); + return -1; + } + } + } + return 0; + + // test TODO + // if (msg.member() == "Register") { + // Policy ppp; + // ppp.Test(); + // QList 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; +} diff --git a/example/service/qtdbushook.h b/example/service/qtdbushook.h new file mode 100644 index 0000000..3df0433 --- /dev/null +++ b/example/service/qtdbushook.h @@ -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 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 diff --git a/example/service/servicebase.cpp b/example/service/servicebase.cpp new file mode 100644 index 0000000..9cb3fe2 --- /dev/null +++ b/example/service/servicebase.cpp @@ -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 +#include +#include +#include +#include + +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; +} diff --git a/example/service/servicebase.h b/example/service/servicebase.h new file mode 100644 index 0000000..da7d693 --- /dev/null +++ b/example/service/servicebase.h @@ -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 +#include + +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 diff --git a/example/service/serviceqtdbus.cpp b/example/service/serviceqtdbus.cpp new file mode 100644 index 0000000..9b4e7bc --- /dev/null +++ b/example/service/serviceqtdbus.cpp @@ -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 +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/example/service/serviceqtdbus.h b/example/service/serviceqtdbus.h new file mode 100644 index 0000000..feb840c --- /dev/null +++ b/example/service/serviceqtdbus.h @@ -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 diff --git a/lupdate.sh b/lupdate.sh new file mode 100755 index 0000000..a049ea0 --- /dev/null +++ b/lupdate.sh @@ -0,0 +1,6 @@ +cd dp4-dbus-service +sh ./lupdate.sh +cd .. +cd dp4-login-plugin +sh ./lupdate.sh +cd .. \ No newline at end of file