File indexing completed on 2024-11-10 04:56:06
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2023 David Edmundson <davidedmundson@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "kwin_wayland_test.h" 0010 0011 #include "wayland/clientconnection.h" 0012 #include "wayland/display.h" 0013 #include "wayland_server.h" 0014 0015 #include <QTemporaryFile> 0016 0017 #include "KWayland/Client/connection_thread.h" 0018 #include "KWayland/Client/registry.h" 0019 0020 #include <sys/socket.h> 0021 #include <sys/un.h> 0022 #include <unistd.h> 0023 0024 namespace KWin 0025 { 0026 0027 static const QString s_socketName = QStringLiteral("wayland_test_security_context-0"); 0028 0029 class SecurityContextTest : public QObject 0030 { 0031 Q_OBJECT 0032 0033 private Q_SLOTS: 0034 void initTestCase(); 0035 void init(); 0036 void cleanup(); 0037 void testSecurityContext(); 0038 void testClosedCloseFdOnStartup(); 0039 }; 0040 0041 void SecurityContextTest::initTestCase() 0042 { 0043 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0044 QVERIFY(waylandServer()->init(s_socketName)); 0045 kwinApp()->start(); 0046 QVERIFY(applicationStartedSpy.wait()); 0047 } 0048 0049 void SecurityContextTest::init() 0050 { 0051 QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::SecurityContextManagerV1)); 0052 } 0053 0054 void SecurityContextTest::cleanup() 0055 { 0056 Test::destroyWaylandConnection(); 0057 } 0058 0059 void SecurityContextTest::testSecurityContext() 0060 { 0061 // This tests a mock flatpak server creating a Security Context 0062 // connecting a client to the newly created server 0063 // and making sure everything is torn down after the closeFd is closed 0064 auto securityContextManager = Test::waylandSecurityContextManagerV1(); 0065 QVERIFY(securityContextManager); 0066 0067 int listenFd = socket(AF_UNIX, SOCK_STREAM, 0); 0068 QVERIFY(listenFd != 0); 0069 0070 QTemporaryDir tempDir; 0071 0072 sockaddr_un sockaddr; 0073 sockaddr.sun_family = AF_UNIX; 0074 snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", tempDir.filePath("socket").toUtf8().constData()); 0075 qDebug() << "listening socket:" << sockaddr.sun_path; 0076 QVERIFY(bind(listenFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == 0); 0077 QVERIFY(listen(listenFd, 0) == 0); 0078 0079 int syncFds[2]; 0080 QVERIFY(pipe(syncFds) >= 0); 0081 int closeFdForClientToKeep = syncFds[0]; 0082 int closeFdToGiveToKwin = syncFds[1]; 0083 0084 auto securityContext = new QtWayland::wp_security_context_v1(securityContextManager->create_listener(listenFd, closeFdToGiveToKwin)); 0085 close(closeFdToGiveToKwin); 0086 close(listenFd); 0087 securityContext->set_instance_id("kde.unitest.instance_id"); 0088 securityContext->set_app_id("kde.unittest.app_id"); 0089 securityContext->set_sandbox_engine("test_sandbox_engine"); 0090 securityContext->commit(); 0091 securityContext->destroy(); 0092 delete securityContext; 0093 0094 qputenv("WAYLAND_DISPLAY", tempDir.filePath("socket").toUtf8()); 0095 QSignalSpy clientConnectedspy(waylandServer()->display(), &Display::clientConnected); 0096 0097 // connect a client using the newly created listening socket 0098 KWayland::Client::ConnectionThread restrictedClientConnection; 0099 QSignalSpy connectedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::connected); 0100 QThread restictedClientThread; 0101 auto restictedClientThreadQuitter = qScopeGuard([&restictedClientThread]() { 0102 restictedClientThread.quit(); 0103 restictedClientThread.wait(); 0104 }); 0105 restrictedClientConnection.moveToThread(&restictedClientThread); 0106 restictedClientThread.start(); 0107 restrictedClientConnection.initConnection(); 0108 QVERIFY(connectedSpy.wait()); 0109 0110 // verify that our new restricted client is seen by kwin with the right security context 0111 QVERIFY(clientConnectedspy.count()); 0112 QCOMPARE(clientConnectedspy.first().first().value<KWin::ClientConnection *>()->securityContextAppId(), "kde.unittest.app_id"); 0113 0114 // verify that the globals for the restricted client does not contain the security context 0115 KWayland::Client::Registry registry; 0116 registry.create(&restrictedClientConnection); 0117 QSignalSpy interfaceAnnounced(®istry, &KWayland::Client::Registry::interfaceAnnounced); 0118 QSignalSpy allAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced); 0119 registry.setup(); 0120 QVERIFY(allAnnouncedSpy.wait()); 0121 for (auto interfaceSignal : interfaceAnnounced) { 0122 QVERIFY(interfaceSignal.first().toString() != "wp_security_context_manager_v1"); 0123 } 0124 0125 // close the mock flatpak closeFDs 0126 close(closeFdForClientToKeep); 0127 0128 // security context properties should have not changed after close-fd is closed 0129 QVERIFY(Test::waylandSync()); 0130 QCOMPARE(clientConnectedspy.first().first().value<KWin::ClientConnection *>()->securityContextAppId(), "kde.unittest.app_id"); 0131 0132 // new clients can't connect anymore 0133 KWayland::Client::ConnectionThread restrictedClientConnection2; 0134 QSignalSpy connectedSpy2(&restrictedClientConnection2, &KWayland::Client::ConnectionThread::connected); 0135 QSignalSpy failedSpy2(&restrictedClientConnection2, &KWayland::Client::ConnectionThread::failed); 0136 QThread restictedClientThread2; 0137 auto restictedClientThreadQuitter2 = qScopeGuard([&restictedClientThread2]() { 0138 restictedClientThread2.quit(); 0139 restictedClientThread2.wait(); 0140 }); 0141 restrictedClientConnection2.moveToThread(&restictedClientThread2); 0142 restictedClientThread2.start(); 0143 restrictedClientConnection2.initConnection(); 0144 QVERIFY(failedSpy2.wait()); 0145 QVERIFY(connectedSpy2.isEmpty()); 0146 } 0147 0148 void SecurityContextTest::testClosedCloseFdOnStartup() 0149 { 0150 // This tests what would happen if the closeFd is already closed when kwin processes the security context 0151 auto securityContextManager = Test::waylandSecurityContextManagerV1(); 0152 QVERIFY(securityContextManager); 0153 0154 int listenFd = socket(AF_UNIX, SOCK_STREAM, 0); 0155 QVERIFY(listenFd != 0); 0156 0157 QTemporaryDir tempDir; 0158 0159 sockaddr_un sockaddr; 0160 sockaddr.sun_family = AF_UNIX; 0161 snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", tempDir.filePath("socket").toUtf8().constData()); 0162 qDebug() << "listening socket:" << sockaddr.sun_path; 0163 QVERIFY(bind(listenFd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) == 0); 0164 QVERIFY(listen(listenFd, 0) == 0); 0165 0166 int syncFds[2]; 0167 QVERIFY(pipe(syncFds) >= 0); 0168 int closeFdForClientToKeep = syncFds[0]; 0169 int closeFdToGiveToKwin = syncFds[1]; 0170 0171 close(closeFdForClientToKeep); // closes the connection 0172 0173 auto securityContext = new QtWayland::wp_security_context_v1(securityContextManager->create_listener(listenFd, closeFdToGiveToKwin)); 0174 close(closeFdToGiveToKwin); 0175 close(listenFd); 0176 securityContext->set_instance_id("kde.unitest.instance_id"); 0177 securityContext->set_app_id("kde.unittest.app_id"); 0178 securityContext->set_sandbox_engine("test_sandbox_engine"); 0179 securityContext->commit(); 0180 securityContext->destroy(); 0181 delete securityContext; 0182 0183 QVERIFY(Test::waylandSync()); 0184 0185 qputenv("WAYLAND_DISPLAY", tempDir.filePath("socket").toUtf8()); 0186 QSignalSpy clientConnectedspy(waylandServer()->display(), &Display::clientConnected); 0187 0188 // new clients can't connect anymore 0189 KWayland::Client::ConnectionThread restrictedClientConnection; 0190 QSignalSpy connectedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::connected); 0191 QSignalSpy failedSpy(&restrictedClientConnection, &KWayland::Client::ConnectionThread::failed); 0192 QThread restictedClientThread; 0193 auto restictedClientThreadQuitter = qScopeGuard([&restictedClientThread]() { 0194 restictedClientThread.quit(); 0195 restictedClientThread.wait(); 0196 }); 0197 restrictedClientConnection.moveToThread(&restictedClientThread); 0198 restictedClientThread.start(); 0199 restrictedClientConnection.initConnection(); 0200 QVERIFY(failedSpy.wait()); 0201 QVERIFY(connectedSpy.isEmpty()); 0202 QVERIFY(clientConnectedspy.isEmpty()); 0203 } 0204 } 0205 0206 WAYLANDTEST_MAIN(KWin::SecurityContextTest) 0207 #include "security_context_test.moc"