You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
7.7 KiB
207 lines
7.7 KiB
/* |
|
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"
|
|
|