From f324776137342814de98c0c466f43169dbbe0f7e Mon Sep 17 00:00:00 2001 From: Aleix Pol Date: Fri, 12 Jul 2019 17:27:32 +0200 Subject: [PATCH] Allow blacklisting some wayland interfaces Summary: For some interfaces, we'll look at the X-KDE-Wayland-Interfaces property in the desktop file to see which interfaces are requested. Requires D22570. Reviewers: #plasma, #kwin, davidedmundson Reviewed By: #plasma, #kwin, davidedmundson Subscribers: fvogt, zzag, broulik, graesslin, davidedmundson, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D22571 --- wayland_server.cpp | 94 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/wayland_server.cpp b/wayland_server.cpp index c8082169d7..2feede0965 100644 --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -66,8 +66,13 @@ along with this program. If not, see . #include #include #include +#include + +// KF +#include // Qt +#include #include #include #include @@ -197,10 +202,97 @@ void WaylandServer::createSurface(T *surface) }); } +class KWinDisplay : public KWayland::Server::FilteredDisplay +{ +public: + KWinDisplay(QObject *parent) + : KWayland::Server::FilteredDisplay(parent) + {} + + static QByteArray sha256(const QString &fileName) + { + QFile f(fileName); + if (f.open(QFile::ReadOnly)) { + QCryptographicHash hash(QCryptographicHash::Sha256); + if (hash.addData(&f)) { + return hash.result(); + } + } + return QByteArray(); + } + + bool isTrustedOrigin(KWayland::Server::ClientConnection *client) const { + const auto fullPathSha = sha256(QStringLiteral("/proc/") + QString::number(client->processId()) + QLatin1String("/root") + client->executablePath()); + const auto localSha = sha256(QLatin1String("/proc/") + QString::number(client->processId()) + QLatin1String("/exe")); + const bool trusted = !localSha.isEmpty() && fullPathSha == localSha; + + if (!trusted) { + qCWarning(KWIN_CORE) << "Could not trust" << client->executablePath() << "sha" << localSha << fullPathSha; + } + + return trusted; + } + + QStringList fetchRequestedInterfaces(KWayland::Server::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()) { + return {}; + } + + return servicesFound.first()->property("X-KDE-Wayland-Interfaces").toStringList(); + } + + QSet interfacesBlackList = {"org_kde_kwin_remote_access_manager", "org_kde_plasma_window_management", "org_kde_kwin_fake_input", "org_kde_kwin_keystate"}; + + bool allowInterface(KWayland::Server::ClientConnection *client, const QByteArray &interfaceName) override { + if (client->processId() == getpid()) { + return true; + } + + if (!interfacesBlackList.contains(interfaceName)) { + return true; + } + + if (client->executablePath().isEmpty()) { + qCWarning(KWIN_CORE) << "Could not identify process with pid" << client->processId(); + return false; + } + + { + auto requestedInterfaces = client->property("requestedInterfaces"); + if (requestedInterfaces.isNull()) { + requestedInterfaces = fetchRequestedInterfaces(client); + client->setProperty("requestedInterfaces", requestedInterfaces); + } + qCDebug(KWIN_CORE) << "interfaces for" << client->executablePath() << requestedInterfaces << interfaceName << requestedInterfaces.toStringList().contains(QString::fromUtf8(interfaceName)); + if (!requestedInterfaces.toStringList().contains(QString::fromUtf8(interfaceName))) { + qCWarning(KWIN_CORE) << "Did not grant the interface" << interfaceName << "to" << client->executablePath() << ". Please request it under X-Wayland-Interfaces"; + return false; + } + } + + { + auto trustedOrigin = client->property("isPrivileged"); + if (trustedOrigin.isNull()) { + trustedOrigin = isTrustedOrigin(client); + client->setProperty("isPrivileged", trustedOrigin); + } + + if (!trustedOrigin.toBool()) { + return false; + } + } + qCDebug(KWIN_CORE) << "authorized" << client->executablePath() << interfaceName; + return true; + } +}; + bool WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags) { m_initFlags = flags; - m_display = new KWayland::Server::Display(this); + m_display = new KWinDisplay(this); if (!socketName.isNull() && !socketName.isEmpty()) { m_display->setSocketName(QString::fromUtf8(socketName)); } else {