diff --git a/data/org_kde_kwin.categories b/data/org_kde_kwin.categories --- a/data/org_kde_kwin.categories +++ b/data/org_kde_kwin.categories @@ -1,4 +1,5 @@ kwin_core KWin Core DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_CORE] +kwin_utils KWin utils DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_UTILS] kwin_virtualkeyboard KWin Virtual Keyboard Integration DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_VIRTUALKEYBOARD] kwineffects KWin Effects DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWINEFFECTS] libkwineffects KWin Effects Library DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [LIBKWINEFFECTS] diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -23,6 +23,7 @@ KF5::Notifications # screenshot effect KF5::Plasma # screenedge effect KF5::WindowSystem + KF5::Service # utils / screenshot effect ) if (HAVE_ACCESSIBILITY) @@ -110,6 +111,7 @@ windowgeometry/windowgeometry.cpp wobblywindows/wobblywindows.cpp zoom/zoom.cpp + ../service_utils.cpp ) if (HAVE_ACCESSIBILITY) diff --git a/effects/screenshot/CMakeLists.txt b/effects/screenshot/CMakeLists.txt --- a/effects/screenshot/CMakeLists.txt +++ b/effects/screenshot/CMakeLists.txt @@ -3,5 +3,6 @@ # Source files set(kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources} + ../service_utils.cpp screenshot/screenshot.cpp ) diff --git a/effects/screenshot/screenshot.h b/effects/screenshot/screenshot.h --- a/effects/screenshot/screenshot.h +++ b/effects/screenshot/screenshot.h @@ -154,6 +154,7 @@ void hideInfoMessage(); bool isTakingScreenshot() const; void computeCoordinatesAfterScaling(); + bool checkCall() const; EffectWindow *m_scheduledScreenshot; ScreenShotType m_type; diff --git a/effects/screenshot/screenshot.cpp b/effects/screenshot/screenshot.cpp --- a/effects/screenshot/screenshot.cpp +++ b/effects/screenshot/screenshot.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include #include @@ -37,6 +39,7 @@ #include #include +#include "../service_utils.h" class ComparableQPoint : public QPoint { @@ -63,14 +66,17 @@ const static QString s_errorAlreadyTaking = QStringLiteral("org.kde.kwin.Screenshot.Error.AlreadyTaking"); const static QString s_errorAlreadyTakingMsg = QStringLiteral("A screenshot is already been taken"); +const static QString s_errorNotAuthorized = QStringLiteral("org.kde.kwin.Screenshot.Error.NoAuthorized"); +const static QString s_errorNotAuthorizedMsg = QStringLiteral("The process is not authorized to take a screenshot"); const static QString s_errorFd = QStringLiteral("org.kde.kwin.Screenshot.Error.FileDescriptor"); const static QString s_errorFdMsg = QStringLiteral("No valid file descriptor"); const static QString s_errorCancelled = QStringLiteral("org.kde.kwin.Screenshot.Error.Cancelled"); const static QString s_errorCancelledMsg = QStringLiteral("Screenshot got cancelled"); const static QString s_errorInvalidArea = QStringLiteral("org.kde.kwin.Screenshot.Error.InvalidArea"); const static QString s_errorInvalidAreaMsg = QStringLiteral("Invalid area requested"); const static QString s_errorInvalidScreen = QStringLiteral("org.kde.kwin.Screenshot.Error.InvalidScreen"); const static QString s_errorInvalidScreenMsg = QStringLiteral("Invalid screen requested"); +const static QString s_dbusInterfaceName = QStringLiteral("org.kde.kwin.Screenshot"); bool ScreenShotEffect::supported() { @@ -496,13 +502,35 @@ } } -QString ScreenShotEffect::interactive(int mask) +bool ScreenShotEffect::checkCall() const { if (!calledFromDBus()) { - return QString(); + return false; + } + + const QDBusReply reply = connection().interface()->servicePid(message().service()); + if (reply.isValid()) { + const uint pid = reply.value(); + const auto interfaces = KWin::fetchRestrictedDBusInterfacesFromPid(pid); + if (!interfaces.contains(s_dbusInterfaceName)) { + sendErrorReply(s_errorNotAuthorized, s_errorNotAuthorizedMsg); + qCWarning(KWINEFFECTS) << "Process " << pid << " tried to take a screenshot without being granted to DBus interface" << s_dbusInterfaceName; + return false; + } + } else { + return false; } + if (isTakingScreenshot()) { sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); + return false; + } + return true; +} + +QString ScreenShotEffect::interactive(int mask) +{ + if (!checkCall()) { return QString(); } m_type = (ScreenShotType) mask; @@ -528,11 +556,7 @@ void ScreenShotEffect::interactive(QDBusUnixFileDescriptor fd, int mask) { - if (!calledFromDBus()) { - return; - } - if (isTakingScreenshot()) { - sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); + if (!checkCall()) { return; } m_fd = dup(fd.fileDescriptor()); @@ -581,11 +605,7 @@ QString ScreenShotEffect::screenshotFullscreen(bool captureCursor) { - if (!calledFromDBus()) { - return QString(); - } - if (isTakingScreenshot()) { - sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); + if (!checkCall()) { return QString(); } m_replyMessage = message(); @@ -598,11 +618,7 @@ void ScreenShotEffect::screenshotFullscreen(QDBusUnixFileDescriptor fd, bool captureCursor, bool shouldReturnNativeSize) { - if (!calledFromDBus()) { - return; - } - if (isTakingScreenshot()) { - sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); + if (!checkCall()) { return; } m_fd = dup(fd.fileDescriptor()); @@ -613,29 +629,13 @@ m_captureCursor = captureCursor; m_nativeSize = shouldReturnNativeSize; - showInfoMessage(InfoMessageMode::Screen); - effects->startInteractivePositionSelection( - [this] (const QPoint &p) { - hideInfoMessage(); - if (p == QPoint(-1, -1)) { - // error condition - close(m_fd); - m_fd = -1; - } else { - m_scheduledGeometry = effects->virtualScreenGeometry(); - effects->addRepaint(m_scheduledGeometry); - } - } - ); + m_scheduledGeometry = effects->virtualScreenGeometry(); + effects->addRepaint(m_scheduledGeometry); } QString ScreenShotEffect::screenshotScreen(int screen, bool captureCursor) { - if (!calledFromDBus()) { - return QString(); - } - if (isTakingScreenshot()) { - sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); + if (!checkCall()) { return QString(); } m_scheduledGeometry = effects->clientArea(FullScreenArea, screen, 0); @@ -652,11 +652,7 @@ void ScreenShotEffect::screenshotScreen(QDBusUnixFileDescriptor fd, bool captureCursor) { - if (!calledFromDBus()) { - return; - } - if (isTakingScreenshot()) { - sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); + if (!checkCall()) { return; } m_fd = dup(fd.fileDescriptor()); @@ -689,11 +685,7 @@ QString ScreenShotEffect::screenshotArea(int x, int y, int width, int height, bool captureCursor) { - if (!calledFromDBus()) { - return QString(); - } - if (isTakingScreenshot()) { - sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg); + if (!checkCall()) { return QString(); } m_scheduledGeometry = QRect(x, y, width, height); diff --git a/service_utils.h b/service_utils.h new file mode 100644 --- /dev/null +++ b/service_utils.h @@ -0,0 +1,77 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2020, Méven Car + +This program 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 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#ifndef SERVICE_UTILS_H +#define SERVICE_UTILS_H + +// cmake stuff +#include +// kwin +#include +// Qt +#include +#include +//KF +#include + +namespace KWin +{ + +const static QString s_waylandInterfaceName = QStringLiteral("X-KDE-Wayland-Interfaces"); +const static QString s_dbusRestrictedInterfaceName = QStringLiteral("X-KDE-DBUS-Restricted-Interfaces"); + +static QStringList fetchProcessServiceField(const QString &executablePath, const QString &fieldName) +{ + // needed to be able to use the logging category in a header static function + static QLoggingCategory KWIN_UTILS ("KWIN_UTILS"); + const auto servicesFound = KApplicationTrader::query([&executablePath] (const KService::Ptr &service) { + + if (service->exec().isEmpty() || service->exec() != executablePath) + return false; + + return true; + }); + + if (servicesFound.isEmpty()) { + qCDebug(KWIN_UTILS) << "Could not find the desktop file for" << executablePath; + return {}; + } + + const auto fieldValues = servicesFound.first()->property(fieldName).toStringList(); + if (KWIN_UTILS().isDebugEnabled()) { + qCDebug(KWIN_UTILS) << "Interfaces found for" << executablePath << fieldName << ":" << fieldValues; + } + return fieldValues; +} + +static QStringList fetchRequestedInterfaces(const QString &executablePath) +{ + return fetchProcessServiceField(executablePath, s_waylandInterfaceName); +} + +static QStringList fetchRestrictedDBusInterfacesFromPid(const uint pid) +{ + const auto executablePath = QFileInfo(QStringLiteral("/proc/%1/exe").arg(pid)).symLinkTarget(); + return fetchProcessServiceField(executablePath, s_dbusRestrictedInterfaceName); +} + +}// namespace + +#endif // SERVICE_UTILS_H diff --git a/service_utils.cpp b/service_utils.cpp new file mode 100644 --- /dev/null +++ b/service_utils.cpp @@ -0,0 +1,32 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2020, Méven Car + +This program 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 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +/* + + This file is for (very) small utility relating to services/process. + +*/ +#include "service_utils.h" + +namespace KWin +{ + +}// namespace + diff --git a/wayland_server.cpp b/wayland_server.cpp --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -26,6 +26,7 @@ #include "waylandxdgshellintegration.h" #include "workspace.h" #include "xdgshellclient.h" +#include "service_utils.h" // Client #include @@ -68,9 +69,6 @@ #include #include -// KF -#include - // Qt #include #include @@ -243,17 +241,7 @@ } QStringList fetchRequestedInterfaces(KWaylandServer::ClientConnection *client) const { - const auto serviceQuery = QStringLiteral("exist Exec and exist [X-KDE-Wayland-Interfaces] and '%1' =~ Exec").arg(client->executablePath()); - const auto servicesFound = KServiceTypeTrader::self()->query(QStringLiteral("Application"), serviceQuery); - - if (servicesFound.isEmpty()) { - qCDebug(KWIN_CORE) << "Could not find the desktop file for" << client->executablePath(); - return {}; - } - - const auto interfaces = servicesFound.first()->property("X-KDE-Wayland-Interfaces").toStringList(); - qCDebug(KWIN_CORE) << "Interfaces for" << client->executablePath() << interfaces; - return interfaces; + return KWin::fetchRequestedInterfaces(client->executablePath()); } const QSet interfacesBlackList = {"org_kde_kwin_remote_access_manager", "org_kde_plasma_window_management", "org_kde_kwin_fake_input", "org_kde_kwin_keystate"};