Support SecurityContextManagerV1
This allows KWin to securely identify the client for a given connection, without relying on the process name. This patch does not do anything meaningful with the application ID other than store it. This first version does not support kwin restarts, it can come afterwards. Testing done: With latest flatpak, running `WAYLAND_DEBUG=1 flatpak run org.telegram.desktop |& grep security` shows that flatpak itself bound the security context, and the client did not see it advertised.
This commit is contained in:
parent
411953ee37
commit
4f9531ad77
14 changed files with 532 additions and 0 deletions
|
@ -16,6 +16,8 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
|
|||
${WaylandProtocols_DATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml
|
||||
${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
|
||||
${WaylandProtocols_DATADIR}/staging/cursor-shape/cursor-shape-v1.xml
|
||||
${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml
|
||||
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml
|
||||
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-screen-edge-v1.xml
|
||||
|
@ -125,6 +127,7 @@ integrationTest(NAME testDbusInterface SRCS dbus_interface_test.cpp LIBS XCB::IC
|
|||
integrationTest(NAME testXwaylandServerCrash SRCS xwaylandserver_crash_test.cpp LIBS XCB::ICCCM)
|
||||
integrationTest(NAME testXwaylandServerRestart SRCS xwaylandserver_restart_test.cpp LIBS XCB::ICCCM)
|
||||
integrationTest(NAME testFakeInput SRCS fakeinput_test.cpp)
|
||||
integrationTest(NAME testSecurityContext SRCS security_context_test.cpp)
|
||||
|
||||
qt_add_dbus_interfaces(DBUS_SRCS ${CMAKE_BINARY_DIR}/src/org.kde.kwin.VirtualKeyboard.xml)
|
||||
integrationTest(NAME testVirtualKeyboardDBus SRCS test_virtualkeyboard_dbus.cpp ${DBUS_SRCS})
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "qwayland-kde-output-device-v2.h"
|
||||
#include "qwayland-kde-output-management-v2.h"
|
||||
#include "qwayland-kde-screen-edge-v1.h"
|
||||
#include "qwayland-security-context-v1.h"
|
||||
#include "qwayland-text-input-unstable-v3.h"
|
||||
#include "qwayland-wlr-layer-shell-unstable-v1.h"
|
||||
#include "qwayland-xdg-decoration-unstable-v1.h"
|
||||
|
@ -527,6 +528,12 @@ public:
|
|||
~FakeInput() override;
|
||||
};
|
||||
|
||||
class SecurityContextManagerV1 : public QtWayland::wp_security_context_manager_v1
|
||||
{
|
||||
public:
|
||||
~SecurityContextManagerV1() override;
|
||||
};
|
||||
|
||||
enum class AdditionalWaylandInterface {
|
||||
Seat = 1 << 0,
|
||||
PlasmaShell = 1 << 2,
|
||||
|
@ -547,6 +554,7 @@ enum class AdditionalWaylandInterface {
|
|||
ScreenEdgeV1 = 1 << 17,
|
||||
CursorShapeV1 = 1 << 18,
|
||||
FakeInput = 1 << 19,
|
||||
SecurityContextManagerV1 = 1 << 20,
|
||||
};
|
||||
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
|
||||
|
||||
|
@ -642,6 +650,7 @@ KWayland::Client::Output *waylandOutput(const QString &name);
|
|||
ScreencastingV1 *screencasting();
|
||||
QList<WaylandOutputDeviceV2 *> waylandOutputDevicesV2();
|
||||
FakeInput *waylandFakeInput();
|
||||
SecurityContextManagerV1 *waylandSecurityContextManagerV1();
|
||||
|
||||
bool waitForWaylandSurface(Window *window);
|
||||
|
||||
|
|
207
autotests/integration/security_context_test.cpp
Normal file
207
autotests/integration/security_context_test.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "kwin_wayland_test.h"
|
||||
|
||||
#include "wayland/clientconnection.h"
|
||||
#include "wayland/display.h"
|
||||
#include "wayland_server.h"
|
||||
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include "KWayland/Client/connection_thread.h"
|
||||
#include "KWayland/Client/registry.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_security_context-0");
|
||||
|
||||
class SecurityContextTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
void testSecurityContext();
|
||||
void testClosedCloseFdOnStartup();
|
||||
};
|
||||
|
||||
void SecurityContextTest::initTestCase()
|
||||
{
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(waylandServer()->init(s_socketName));
|
||||
kwinApp()->start();
|
||||
QVERIFY(applicationStartedSpy.wait());
|
||||
}
|
||||
|
||||
void SecurityContextTest::init()
|
||||
{
|
||||
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::SecurityContextManagerV1));
|
||||
}
|
||||
|
||||
void SecurityContextTest::cleanup()
|
||||
{
|
||||
Test::destroyWaylandConnection();
|
||||
}
|
||||
|
||||
void SecurityContextTest::testSecurityContext()
|
||||
{
|
||||
// This tests a mock flatpak server creating a Security Context
|
||||
// connecting a client to the newly created server
|
||||
// and making sure everything is torn down after the closeFd is closed
|
||||
auto securityContextManager = Test::waylandSecurityContextManagerV1();
|
||||
QVERIFY(securityContextManager);
|
||||
|
||||
int listenFd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
QVERIFY(listenFd != 0);
|
||||
|
||||
QTemporaryDir tempDir;
|
||||
|
||||
sockaddr_un sockaddr;
|
||||
sockaddr.sun_family = AF_UNIX;
|
||||
snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", tempDir.filePath("socket").toUtf8().constData());
|
||||
qDebug() << "listening socket:" << sockaddr.sun_path;
|
||||
QVERIFY(bind(listenFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == 0);
|
||||
QVERIFY(listen(listenFd, 0) == 0);
|
||||
|
||||
int syncFds[2];
|
||||
QVERIFY(pipe(syncFds) >= 0);
|
||||
int closeFdForClientToKeep = syncFds[0];
|
||||
int closeFdToGiveToKwin = syncFds[1];
|
||||
|
||||
auto securityContext = new QtWayland::wp_security_context_v1(securityContextManager->create_listener(listenFd, closeFdToGiveToKwin));
|
||||
close(closeFdToGiveToKwin);
|
||||
close(listenFd);
|
||||
securityContext->set_instance_id("kde.unitest.instance_id");
|
||||
securityContext->set_app_id("kde.unittest.app_id");
|
||||
securityContext->set_sandbox_engine("test_sandbox_engine");
|
||||
securityContext->commit();
|
||||
securityContext->destroy();
|
||||
delete securityContext;
|
||||
|
||||
qputenv("WAYLAND_DISPLAY", tempDir.filePath("socket").toUtf8());
|
||||
QSignalSpy clientConnectedspy(waylandServer()->display(), &Display::clientConnected);
|
||||
|
||||
// connect a client using the newly created listening socket
|
||||
KWayland::Client::ConnectionThread restrictedClientConnection;
|
||||
QSignalSpy connectedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::connected);
|
||||
QThread restictedClientThread;
|
||||
auto restictedClientThreadQuitter = qScopeGuard([&restictedClientThread]() {
|
||||
restictedClientThread.quit();
|
||||
restictedClientThread.wait();
|
||||
});
|
||||
restrictedClientConnection.moveToThread(&restictedClientThread);
|
||||
restictedClientThread.start();
|
||||
restrictedClientConnection.initConnection();
|
||||
QVERIFY(connectedSpy.wait());
|
||||
|
||||
// verify that our new restricted client is seen by kwin with the right security context
|
||||
QVERIFY(clientConnectedspy.count());
|
||||
QCOMPARE(clientConnectedspy.first().first().value<KWin::ClientConnection *>()->securityContextAppId(), "kde.unittest.app_id");
|
||||
|
||||
// verify that the globals for the restricted client does not contain the security context
|
||||
KWayland::Client::Registry registry;
|
||||
registry.create(&restrictedClientConnection);
|
||||
QSignalSpy interfaceAnnounced(®istry, &KWayland::Client::Registry::interfaceAnnounced);
|
||||
QSignalSpy allAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced);
|
||||
registry.setup();
|
||||
QVERIFY(allAnnouncedSpy.wait());
|
||||
for (auto interfaceSignal : interfaceAnnounced) {
|
||||
QVERIFY(interfaceSignal.first().toString() != "wp_security_context_manager_v1");
|
||||
}
|
||||
|
||||
// close the mock flatpak closeFDs
|
||||
close(closeFdForClientToKeep);
|
||||
|
||||
// security context properties should have not changed after close-fd is closed
|
||||
QVERIFY(Test::waylandSync());
|
||||
QCOMPARE(clientConnectedspy.first().first().value<KWin::ClientConnection *>()->securityContextAppId(), "kde.unittest.app_id");
|
||||
|
||||
// new clients can't connect anymore
|
||||
KWayland::Client::ConnectionThread restrictedClientConnection2;
|
||||
QSignalSpy connectedSpy2(&restrictedClientConnection2, &KWayland::Client::ConnectionThread::connected);
|
||||
QSignalSpy failedSpy2(&restrictedClientConnection2, &KWayland::Client::ConnectionThread::failed);
|
||||
QThread restictedClientThread2;
|
||||
auto restictedClientThreadQuitter2 = qScopeGuard([&restictedClientThread2]() {
|
||||
restictedClientThread2.quit();
|
||||
restictedClientThread2.wait();
|
||||
});
|
||||
restrictedClientConnection2.moveToThread(&restictedClientThread2);
|
||||
restictedClientThread2.start();
|
||||
restrictedClientConnection2.initConnection();
|
||||
QVERIFY(failedSpy2.wait());
|
||||
QVERIFY(connectedSpy2.isEmpty());
|
||||
}
|
||||
|
||||
void SecurityContextTest::testClosedCloseFdOnStartup()
|
||||
{
|
||||
// This tests what would happen if the closeFd is already closed when kwin processes the security context
|
||||
auto securityContextManager = Test::waylandSecurityContextManagerV1();
|
||||
QVERIFY(securityContextManager);
|
||||
|
||||
int listenFd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
QVERIFY(listenFd != 0);
|
||||
|
||||
QTemporaryDir tempDir;
|
||||
|
||||
sockaddr_un sockaddr;
|
||||
sockaddr.sun_family = AF_UNIX;
|
||||
snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", tempDir.filePath("socket").toUtf8().constData());
|
||||
qDebug() << "listening socket:" << sockaddr.sun_path;
|
||||
QVERIFY(bind(listenFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == 0);
|
||||
QVERIFY(listen(listenFd, 0) == 0);
|
||||
|
||||
int syncFds[2];
|
||||
QVERIFY(pipe(syncFds) >= 0);
|
||||
int closeFdForClientToKeep = syncFds[0];
|
||||
int closeFdToGiveToKwin = syncFds[1];
|
||||
|
||||
close(closeFdForClientToKeep); // closes the connection
|
||||
|
||||
auto securityContext = new QtWayland::wp_security_context_v1(securityContextManager->create_listener(listenFd, closeFdToGiveToKwin));
|
||||
close(closeFdToGiveToKwin);
|
||||
close(listenFd);
|
||||
securityContext->set_instance_id("kde.unitest.instance_id");
|
||||
securityContext->set_app_id("kde.unittest.app_id");
|
||||
securityContext->set_sandbox_engine("test_sandbox_engine");
|
||||
securityContext->commit();
|
||||
securityContext->destroy();
|
||||
delete securityContext;
|
||||
|
||||
QVERIFY(Test::waylandSync());
|
||||
|
||||
qputenv("WAYLAND_DISPLAY", tempDir.filePath("socket").toUtf8());
|
||||
QSignalSpy clientConnectedspy(waylandServer()->display(), &Display::clientConnected);
|
||||
|
||||
// new clients can't connect anymore
|
||||
KWayland::Client::ConnectionThread restrictedClientConnection;
|
||||
QSignalSpy connectedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::connected);
|
||||
QSignalSpy failedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::failed);
|
||||
QThread restictedClientThread;
|
||||
auto restictedClientThreadQuitter = qScopeGuard([&restictedClientThread]() {
|
||||
restictedClientThread.quit();
|
||||
restictedClientThread.wait();
|
||||
});
|
||||
restrictedClientConnection.moveToThread(&restictedClientThread);
|
||||
restictedClientThread.start();
|
||||
restrictedClientConnection.initConnection();
|
||||
QVERIFY(failedSpy.wait());
|
||||
QVERIFY(connectedSpy.isEmpty());
|
||||
QVERIFY(clientConnectedspy.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(KWin::SecurityContextTest)
|
||||
#include "security_context_test.moc"
|
|
@ -264,6 +264,11 @@ FakeInput::~FakeInput()
|
|||
destroy();
|
||||
}
|
||||
|
||||
SecurityContextManagerV1::~SecurityContextManagerV1()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
KWayland::Client::ConnectionThread *connection = nullptr;
|
||||
|
@ -296,6 +301,7 @@ static struct
|
|||
ScreenEdgeManagerV1 *screenEdgeManagerV1 = nullptr;
|
||||
CursorShapeManagerV1 *cursorShapeManagerV1 = nullptr;
|
||||
FakeInput *fakeInput = nullptr;
|
||||
SecurityContextManagerV1 *securityContextManagerV1 = nullptr;
|
||||
} s_waylandConnection;
|
||||
|
||||
MockInputMethod *inputMethod()
|
||||
|
@ -493,6 +499,12 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
|
|||
s_waylandConnection.fakeInput->init(*registry, name, version);
|
||||
}
|
||||
}
|
||||
if (flags & AdditionalWaylandInterface::SecurityContextManagerV1) {
|
||||
if (interface == wp_security_context_manager_v1_interface.name) {
|
||||
s_waylandConnection.securityContextManagerV1 = new SecurityContextManagerV1();
|
||||
s_waylandConnection.securityContextManagerV1->init(*registry, name, version);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
|
||||
|
@ -616,6 +628,8 @@ void destroyWaylandConnection()
|
|||
s_waylandConnection.cursorShapeManagerV1 = nullptr;
|
||||
delete s_waylandConnection.fakeInput;
|
||||
s_waylandConnection.fakeInput = nullptr;
|
||||
delete s_waylandConnection.securityContextManagerV1;
|
||||
s_waylandConnection.securityContextManagerV1 = nullptr;
|
||||
|
||||
delete s_waylandConnection.queue; // Must be destroyed last
|
||||
s_waylandConnection.queue = nullptr;
|
||||
|
@ -727,6 +741,11 @@ FakeInput *waylandFakeInput()
|
|||
return s_waylandConnection.fakeInput;
|
||||
}
|
||||
|
||||
SecurityContextManagerV1 *waylandSecurityContextManagerV1()
|
||||
{
|
||||
return s_waylandConnection.securityContextManagerV1;
|
||||
}
|
||||
|
||||
bool waitForWaylandSurface(Window *window)
|
||||
{
|
||||
if (window->surface()) {
|
||||
|
|
|
@ -74,11 +74,29 @@ FileDescriptor FileDescriptor::duplicate() const
|
|||
}
|
||||
}
|
||||
|
||||
bool FileDescriptor::isClosed() const
|
||||
{
|
||||
return isClosed(m_fd);
|
||||
}
|
||||
|
||||
bool FileDescriptor::isReadable() const
|
||||
{
|
||||
return isReadable(m_fd);
|
||||
}
|
||||
|
||||
bool FileDescriptor::isClosed(int fd)
|
||||
{
|
||||
pollfd pfd = {
|
||||
.fd = fd,
|
||||
.events = POLLIN,
|
||||
.revents = 0,
|
||||
};
|
||||
if (poll(&pfd, 1, 0) < 0) {
|
||||
return true;
|
||||
}
|
||||
return pfd.revents & (POLLHUP | POLLERR);
|
||||
}
|
||||
|
||||
bool FileDescriptor::isReadable(int fd)
|
||||
{
|
||||
pollfd pfd = {
|
||||
|
|
|
@ -29,8 +29,10 @@ public:
|
|||
FileDescriptor duplicate() const;
|
||||
|
||||
bool isReadable() const;
|
||||
bool isClosed() const;
|
||||
|
||||
static bool isReadable(int fd);
|
||||
static bool isClosed(int fd);
|
||||
|
||||
private:
|
||||
int m_fd = -1;
|
||||
|
|
|
@ -221,6 +221,11 @@ ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
|||
BASENAME frog-color-management-v1
|
||||
)
|
||||
|
||||
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml
|
||||
BASENAME security-context-v1
|
||||
)
|
||||
|
||||
target_sources(kwin PRIVATE
|
||||
abstract_data_source.cpp
|
||||
abstract_drop_handler.cpp
|
||||
|
@ -275,6 +280,7 @@ target_sources(kwin PRIVATE
|
|||
screencast_v1.cpp
|
||||
screenedge_v1.cpp
|
||||
seat.cpp
|
||||
securitycontext_v1.cpp
|
||||
server_decoration.cpp
|
||||
server_decoration_palette.cpp
|
||||
shadow.cpp
|
||||
|
@ -347,6 +353,7 @@ install(FILES
|
|||
screencast_v1.h
|
||||
screenedge_v1.h
|
||||
seat.h
|
||||
securitycontext_v1.h
|
||||
server_decoration.h
|
||||
server_decoration_palette.h
|
||||
shadow.h
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
uid_t user = 0;
|
||||
gid_t group = 0;
|
||||
QString executablePath;
|
||||
QString securityContextAppId;
|
||||
qreal scaleOverride = 1.0;
|
||||
|
||||
private:
|
||||
|
@ -165,6 +166,15 @@ qreal ClientConnection::scaleOverride() const
|
|||
return d->scaleOverride;
|
||||
}
|
||||
|
||||
void ClientConnection::setSecurityContextAppId(const QString &appId)
|
||||
{
|
||||
d->securityContextAppId = appId;
|
||||
}
|
||||
|
||||
QString ClientConnection::securityContextAppId() const
|
||||
{
|
||||
return d->securityContextAppId;
|
||||
}
|
||||
}
|
||||
|
||||
#include "moc_clientconnection.cpp"
|
||||
|
|
|
@ -124,6 +124,9 @@ public:
|
|||
void setScaleOverride(qreal scaleOverride);
|
||||
qreal scaleOverride() const;
|
||||
|
||||
void setSecurityContextAppId(const QString &appId);
|
||||
QString securityContextAppId() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal is emitted when the client is about to be destroyed.
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
#include "shmclientbuffer_p.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
@ -242,6 +246,50 @@ GraphicsBuffer *Display::bufferForResource(wl_resource *resource)
|
|||
}
|
||||
}
|
||||
|
||||
SecurityContext::SecurityContext(Display *display, FileDescriptor &&listenFd, FileDescriptor &&closeFd, const QString &appId)
|
||||
: QObject(display)
|
||||
, m_display(display)
|
||||
, m_listenFd(std::move(listenFd))
|
||||
, m_closeFd(std::move(closeFd))
|
||||
, m_appId(appId)
|
||||
{
|
||||
qCDebug(KWIN_CORE) << "Adding listen fd for" << appId;
|
||||
|
||||
auto closeSocketWatcher = new QSocketNotifier(m_closeFd.get(), QSocketNotifier::Read, this);
|
||||
connect(closeSocketWatcher, &QSocketNotifier::activated, this, &SecurityContext::onCloseFdActivated);
|
||||
|
||||
if (m_closeFd.isClosed()) {
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
auto listenFdListener = new QSocketNotifier(m_listenFd.get(), QSocketNotifier::Read, this);
|
||||
connect(listenFdListener, &QSocketNotifier::activated, this, &SecurityContext::onListenFdActivated);
|
||||
}
|
||||
|
||||
SecurityContext::~SecurityContext()
|
||||
{
|
||||
qCDebug(KWIN_CORE) << "Removing listen fd for " << m_appId;
|
||||
}
|
||||
|
||||
void SecurityContext::onListenFdActivated(QSocketDescriptor socketDescriptor)
|
||||
{
|
||||
int clientFd = accept4(socketDescriptor, nullptr, nullptr, SOCK_CLOEXEC);
|
||||
if (clientFd < 0) {
|
||||
qCWarning(KWIN_CORE) << "Failed to accept client from security listen FD";
|
||||
return;
|
||||
}
|
||||
auto client = m_display->createClient(clientFd);
|
||||
client->setSecurityContextAppId(m_appId);
|
||||
}
|
||||
|
||||
void SecurityContext::onCloseFdActivated()
|
||||
{
|
||||
if (m_closeFd.isClosed()) {
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_display.cpp"
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <wayland-server-core.h>
|
||||
|
||||
#include "utils/filedescriptor.h"
|
||||
#include <QList>
|
||||
#include <QSocketNotifier>
|
||||
#include <QString>
|
||||
|
@ -43,4 +44,26 @@ public:
|
|||
QStringList socketNames;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The SecurityContext is a helper for the SecurityContextProtocol
|
||||
* It stays alive whilst closeFd remains open, listening for new connections on listenFd
|
||||
* Any new clients created via listenFd are tagged with the appId
|
||||
* It is parented to the display
|
||||
*/
|
||||
class SecurityContext : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SecurityContext(Display *display, FileDescriptor &&listenFd, FileDescriptor &&closeFd, const QString &appId);
|
||||
~SecurityContext() override;
|
||||
|
||||
private:
|
||||
void onCloseFdActivated();
|
||||
void onListenFdActivated(QSocketDescriptor descriptor);
|
||||
Display *m_display;
|
||||
FileDescriptor m_listenFd;
|
||||
FileDescriptor m_closeFd;
|
||||
QString m_appId;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
|
|
152
src/wayland/securitycontext_v1.cpp
Normal file
152
src/wayland/securitycontext_v1.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include "securitycontext_v1.h"
|
||||
#include "display.h"
|
||||
#include "utils/filedescriptor.h"
|
||||
#include "wayland/display_p.h"
|
||||
|
||||
#include <qwayland-server-security-context-v1.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class QSocketNotifier;
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
static const quint32 s_version = 1;
|
||||
|
||||
class SecurityContextManagerV1InterfacePrivate : public QtWaylandServer::wp_security_context_manager_v1
|
||||
{
|
||||
public:
|
||||
SecurityContextManagerV1InterfacePrivate(Display *display);
|
||||
|
||||
protected:
|
||||
void wp_security_context_manager_v1_create_listener(Resource *resource, uint32_t id, int32_t listen_fd, int32_t close_fd);
|
||||
|
||||
private:
|
||||
Display *m_display;
|
||||
};
|
||||
|
||||
class SecurityContextV1Interface : public QtWaylandServer::wp_security_context_v1
|
||||
{
|
||||
public:
|
||||
SecurityContextV1Interface(Display *display, int listenFd, int closeFd, wl_resource *resource);
|
||||
|
||||
protected:
|
||||
void wp_security_context_v1_set_sandbox_engine(Resource *resource, const QString &name) override;
|
||||
void wp_security_context_v1_set_app_id(Resource *resource, const QString &app_id) override;
|
||||
void wp_security_context_v1_set_instance_id(Resource *resource, const QString &instance_id) override;
|
||||
void wp_security_context_v1_commit(Resource *resource) override;
|
||||
void wp_security_context_v1_destroy_resource(Resource *resource) override;
|
||||
void wp_security_context_v1_destroy(Resource *resource) override;
|
||||
|
||||
private:
|
||||
Display *m_display;
|
||||
FileDescriptor m_listenFd;
|
||||
FileDescriptor m_closeFd;
|
||||
bool m_committed = false;
|
||||
QString m_sandboxEngine;
|
||||
QString m_appId;
|
||||
QString m_instanceId;
|
||||
};
|
||||
|
||||
SecurityContextManagerV1Interface::SecurityContextManagerV1Interface(Display *display, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new SecurityContextManagerV1InterfacePrivate(display))
|
||||
{
|
||||
}
|
||||
|
||||
SecurityContextManagerV1Interface::~SecurityContextManagerV1Interface()
|
||||
{
|
||||
}
|
||||
|
||||
SecurityContextManagerV1InterfacePrivate::SecurityContextManagerV1InterfacePrivate(Display *display)
|
||||
: QtWaylandServer::wp_security_context_manager_v1(*display, s_version)
|
||||
, m_display(display)
|
||||
{
|
||||
}
|
||||
|
||||
void SecurityContextManagerV1InterfacePrivate::wp_security_context_manager_v1_create_listener(Resource *resource, uint32_t id, int32_t listen_fd, int32_t close_fd)
|
||||
{
|
||||
auto *securityContextResource = wl_resource_create(resource->client(), &wp_security_context_v1_interface, resource->version(), id);
|
||||
if (!securityContextResource) {
|
||||
wl_resource_post_no_memory(resource->handle);
|
||||
return;
|
||||
}
|
||||
new SecurityContextV1Interface(m_display, listen_fd, close_fd, securityContextResource);
|
||||
}
|
||||
|
||||
SecurityContextV1Interface::SecurityContextV1Interface(Display *display, int listenFd, int closeFd, wl_resource *resource)
|
||||
: QtWaylandServer::wp_security_context_v1(resource)
|
||||
, m_display(display)
|
||||
, m_listenFd(listenFd)
|
||||
, m_closeFd(closeFd)
|
||||
{
|
||||
}
|
||||
|
||||
void SecurityContextV1Interface::wp_security_context_v1_set_sandbox_engine(Resource *resource, const QString &name)
|
||||
{
|
||||
if (m_committed) {
|
||||
wl_resource_post_error(resource->handle, error_already_used, "Already committed");
|
||||
return;
|
||||
}
|
||||
m_sandboxEngine = name;
|
||||
}
|
||||
|
||||
void SecurityContextV1Interface::wp_security_context_v1_set_app_id(Resource *resource, const QString &app_id)
|
||||
{
|
||||
if (m_committed) {
|
||||
wl_resource_post_error(resource->handle, error_already_used, "Already committed");
|
||||
return;
|
||||
}
|
||||
if (app_id.isEmpty()) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_metadata, "App ID cannot be empty");
|
||||
return;
|
||||
}
|
||||
m_appId = app_id;
|
||||
}
|
||||
|
||||
void SecurityContextV1Interface::wp_security_context_v1_set_instance_id(Resource *resource, const QString &instance_id)
|
||||
{
|
||||
if (m_committed) {
|
||||
wl_resource_post_error(resource->handle, error_already_used, "Already committed");
|
||||
return;
|
||||
}
|
||||
m_instanceId = instance_id;
|
||||
}
|
||||
|
||||
void SecurityContextV1Interface::wp_security_context_v1_commit(Resource *resource)
|
||||
{
|
||||
if (m_committed) {
|
||||
wl_resource_post_error(resource->handle, error_already_used, "Already committed");
|
||||
return;
|
||||
}
|
||||
if (m_appId.isEmpty()) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_metadata, "App ID cannot be empty");
|
||||
return;
|
||||
}
|
||||
if (m_sandboxEngine.isEmpty()) {
|
||||
wl_resource_post_error(resource->handle, error_invalid_metadata, "Sandbox engine cannot be empty");
|
||||
return;
|
||||
}
|
||||
|
||||
m_committed = true;
|
||||
|
||||
// lifespan is managed by the closeFD's state
|
||||
new SecurityContext(m_display, std::move(m_listenFd), std::move(m_closeFd), m_appId);
|
||||
}
|
||||
|
||||
void SecurityContextV1Interface::wp_security_context_v1_destroy_resource(Resource *resource)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
void SecurityContextV1Interface::wp_security_context_v1_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
}
|
||||
|
||||
}
|
25
src/wayland/securitycontext_v1.h
Normal file
25
src/wayland/securitycontext_v1.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
#include <QObject>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class SecurityContextManagerV1InterfacePrivate;
|
||||
class SecurityContextManagerV1Interface;
|
||||
class Display;
|
||||
|
||||
class SecurityContextManagerV1Interface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SecurityContextManagerV1Interface(Display *display, QObject *parent = nullptr);
|
||||
~SecurityContextManagerV1Interface() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<SecurityContextManagerV1InterfacePrivate> d;
|
||||
};
|
||||
}
|
|
@ -54,6 +54,7 @@
|
|||
#include "wayland/relativepointer_v1.h"
|
||||
#include "wayland/screenedge_v1.h"
|
||||
#include "wayland/seat.h"
|
||||
#include "wayland/securitycontext_v1.h"
|
||||
#include "wayland/server_decoration.h"
|
||||
#include "wayland/server_decoration_palette.h"
|
||||
#include "wayland/shadow.h"
|
||||
|
@ -153,6 +154,10 @@ public:
|
|||
|
||||
bool allowInterface(ClientConnection *client, const QByteArray &interfaceName) override
|
||||
{
|
||||
if (!client->securityContextAppId().isEmpty() && interfaceName == QByteArrayLiteral("wp_security_context_manager_v1")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (client->processId() == getpid()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -406,6 +411,7 @@ bool WaylandServer::init(InitializationFlags flags)
|
|||
});
|
||||
|
||||
new ViewporterInterface(m_display, m_display);
|
||||
new SecurityContextManagerV1Interface(m_display, m_display);
|
||||
new FractionalScaleManagerV1Interface(m_display, m_display);
|
||||
m_display->createShm();
|
||||
m_seat = new SeatInterface(m_display, m_display);
|
||||
|
|
Loading…
Reference in a new issue