KWin
Loading...
Searching...
No Matches
security_context_test.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "kwin_wayland_test.h"
10
12#include "wayland/display.h"
13#include "wayland_server.h"
14
15#include <QTemporaryFile>
16
17#include "KWayland/Client/connection_thread.h"
18#include "KWayland/Client/registry.h"
19
20#include <sys/socket.h>
21#include <sys/un.h>
22#include <unistd.h>
23
24namespace KWin
25{
26
27static const QString s_socketName = QStringLiteral("wayland_test_security_context-0");
28
29class SecurityContextTest : public QObject
30{
31 Q_OBJECT
32
33private Q_SLOTS:
34 void initTestCase();
35 void init();
36 void cleanup();
37 void testSecurityContext();
38 void testClosedCloseFdOnStartup();
39};
40
41void SecurityContextTest::initTestCase()
42{
43 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
44 QVERIFY(waylandServer()->init(s_socketName));
45 kwinApp()->start();
46 QVERIFY(applicationStartedSpy.wait());
47}
48
49void SecurityContextTest::init()
50{
52}
53
54void SecurityContextTest::cleanup()
55{
57}
58
59void SecurityContextTest::testSecurityContext()
60{
61 // This tests a mock flatpak server creating a Security Context
62 // connecting a client to the newly created server
63 // and making sure everything is torn down after the closeFd is closed
64 auto securityContextManager = Test::waylandSecurityContextManagerV1();
65 QVERIFY(securityContextManager);
66
67 int listenFd = socket(AF_UNIX, SOCK_STREAM, 0);
68 QVERIFY(listenFd != 0);
69
70 QTemporaryDir tempDir;
71
72 sockaddr_un sockaddr;
73 sockaddr.sun_family = AF_UNIX;
74 snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", tempDir.filePath("socket").toUtf8().constData());
75 qDebug() << "listening socket:" << sockaddr.sun_path;
76 QVERIFY(bind(listenFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == 0);
77 QVERIFY(listen(listenFd, 0) == 0);
78
79 int syncFds[2];
80 QVERIFY(pipe(syncFds) >= 0);
81 int closeFdForClientToKeep = syncFds[0];
82 int closeFdToGiveToKwin = syncFds[1];
83
84 auto securityContext = new QtWayland::wp_security_context_v1(securityContextManager->create_listener(listenFd, closeFdToGiveToKwin));
85 close(closeFdToGiveToKwin);
86 close(listenFd);
87 securityContext->set_instance_id("kde.unitest.instance_id");
88 securityContext->set_app_id("kde.unittest.app_id");
89 securityContext->set_sandbox_engine("test_sandbox_engine");
90 securityContext->commit();
91 securityContext->destroy();
92 delete securityContext;
93
94 qputenv("WAYLAND_DISPLAY", tempDir.filePath("socket").toUtf8());
95 QSignalSpy clientConnectedspy(waylandServer()->display(), &Display::clientConnected);
96
97 // connect a client using the newly created listening socket
98 KWayland::Client::ConnectionThread restrictedClientConnection;
99 QSignalSpy connectedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::connected);
100 QThread restictedClientThread;
101 auto restictedClientThreadQuitter = qScopeGuard([&restictedClientThread]() {
102 restictedClientThread.quit();
103 restictedClientThread.wait();
104 });
105 restrictedClientConnection.moveToThread(&restictedClientThread);
106 restictedClientThread.start();
107 restrictedClientConnection.initConnection();
108 QVERIFY(connectedSpy.wait());
109
110 // verify that our new restricted client is seen by kwin with the right security context
111 QVERIFY(clientConnectedspy.count());
112 QCOMPARE(clientConnectedspy.first().first().value<KWin::ClientConnection *>()->securityContextAppId(), "kde.unittest.app_id");
113
114 // verify that the globals for the restricted client does not contain the security context
115 KWayland::Client::Registry registry;
116 registry.create(&restrictedClientConnection);
117 QSignalSpy interfaceAnnounced(&registry, &KWayland::Client::Registry::interfaceAnnounced);
118 QSignalSpy allAnnouncedSpy(&registry, &KWayland::Client::Registry::interfacesAnnounced);
119 registry.setup();
120 QVERIFY(allAnnouncedSpy.wait());
121 for (auto interfaceSignal : interfaceAnnounced) {
122 QVERIFY(interfaceSignal.first().toString() != "wp_security_context_manager_v1");
123 }
124
125 // close the mock flatpak closeFDs
126 close(closeFdForClientToKeep);
127
128 // security context properties should have not changed after close-fd is closed
129 QVERIFY(Test::waylandSync());
130 QCOMPARE(clientConnectedspy.first().first().value<KWin::ClientConnection *>()->securityContextAppId(), "kde.unittest.app_id");
131
132 // new clients can't connect anymore
133 KWayland::Client::ConnectionThread restrictedClientConnection2;
134 QSignalSpy connectedSpy2(&restrictedClientConnection2, &KWayland::Client::ConnectionThread::connected);
135 QSignalSpy failedSpy2(&restrictedClientConnection2, &KWayland::Client::ConnectionThread::failed);
136 QThread restictedClientThread2;
137 auto restictedClientThreadQuitter2 = qScopeGuard([&restictedClientThread2]() {
138 restictedClientThread2.quit();
139 restictedClientThread2.wait();
140 });
141 restrictedClientConnection2.moveToThread(&restictedClientThread2);
142 restictedClientThread2.start();
143 restrictedClientConnection2.initConnection();
144 QVERIFY(failedSpy2.wait());
145 QVERIFY(connectedSpy2.isEmpty());
146}
147
148void SecurityContextTest::testClosedCloseFdOnStartup()
149{
150 // This tests what would happen if the closeFd is already closed when kwin processes the security context
151 auto securityContextManager = Test::waylandSecurityContextManagerV1();
152 QVERIFY(securityContextManager);
153
154 int listenFd = socket(AF_UNIX, SOCK_STREAM, 0);
155 QVERIFY(listenFd != 0);
156
157 QTemporaryDir tempDir;
158
159 sockaddr_un sockaddr;
160 sockaddr.sun_family = AF_UNIX;
161 snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", tempDir.filePath("socket").toUtf8().constData());
162 qDebug() << "listening socket:" << sockaddr.sun_path;
163 QVERIFY(bind(listenFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == 0);
164 QVERIFY(listen(listenFd, 0) == 0);
165
166 int syncFds[2];
167 QVERIFY(pipe(syncFds) >= 0);
168 int closeFdForClientToKeep = syncFds[0];
169 int closeFdToGiveToKwin = syncFds[1];
170
171 close(closeFdForClientToKeep); // closes the connection
172
173 auto securityContext = new QtWayland::wp_security_context_v1(securityContextManager->create_listener(listenFd, closeFdToGiveToKwin));
174 close(closeFdToGiveToKwin);
175 close(listenFd);
176 securityContext->set_instance_id("kde.unitest.instance_id");
177 securityContext->set_app_id("kde.unittest.app_id");
178 securityContext->set_sandbox_engine("test_sandbox_engine");
179 securityContext->commit();
180 securityContext->destroy();
181 delete securityContext;
182
183 QVERIFY(Test::waylandSync());
184
185 qputenv("WAYLAND_DISPLAY", tempDir.filePath("socket").toUtf8());
186 QSignalSpy clientConnectedspy(waylandServer()->display(), &Display::clientConnected);
187
188 // new clients can't connect anymore
189 KWayland::Client::ConnectionThread restrictedClientConnection;
190 QSignalSpy connectedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::connected);
191 QSignalSpy failedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::failed);
192 QThread restictedClientThread;
193 auto restictedClientThreadQuitter = qScopeGuard([&restictedClientThread]() {
194 restictedClientThread.quit();
195 restictedClientThread.wait();
196 });
197 restrictedClientConnection.moveToThread(&restictedClientThread);
198 restictedClientThread.start();
199 restrictedClientConnection.initConnection();
200 QVERIFY(failedSpy.wait());
201 QVERIFY(connectedSpy.isEmpty());
202 QVERIFY(clientConnectedspy.isEmpty());
203}
204}
205
207#include "security_context_test.moc"
Convenient Class which represents a wl_client.
QString securityContextAppId() const
#define WAYLANDTEST_MAIN(TestObject)
void destroyWaylandConnection()
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
SecurityContextManagerV1 * waylandSecurityContextManagerV1()
KWayland::Client::Registry * registry
bool waylandSync()
WaylandServer * waylandServer()