Skip to content

Commit

Permalink
feat: implement security context v1 protocol
Browse files Browse the repository at this point in the history
This allows clients to create separate sockets for restricted access clients.
The most prominent client supposed to make use of that is Flatpak.
  • Loading branch information
romangg committed Jan 21, 2024
1 parent 639960c commit 7d0be51
Show file tree
Hide file tree
Showing 17 changed files with 1,059 additions and 0 deletions.
14 changes: 14 additions & 0 deletions autotests/client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,20 @@ target_link_libraries(testKdeIdle
add_test(NAME wrapland-testKdeIdle COMMAND testKdeIdle)
ecm_mark_as_test(testKdeIdle)

# ##################################################################################################
# Test security-context
# ##################################################################################################
set(security_context_test_SRCS security_context.cpp)
add_executable(security-context-test ${security_context_test_SRCS})
target_link_libraries(security-context-test
Qt6::Test
Qt6::Gui
Wrapland::Client
Wrapland::Server
)
add_test(NAME wrapland-test-security-context COMMAND security-context-test)
ecm_mark_as_test(security-context-test)

# ##################################################################################################
# Test Keyboard Shortcuts Inhibitor
# ##################################################################################################
Expand Down
210 changes: 210 additions & 0 deletions autotests/client/security_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
SPDX-FileCopyrightText: 2024 Roman Gilg <subdiff@gmail.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only
*/
#include <QtTest>

#include "../../src/client/connection_thread.h"
#include "../../src/client/event_queue.h"
#include "../../src/client/registry.h"
#include "../../src/client/security_context_v1.h"

#include "../../server/client.h"
#include "../../server/display.h"
#include "../../server/security_context_v1.h"

#include "../../tests/globals.h"

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

namespace Clt = Wrapland::Client;
namespace Srv = Wrapland::Server;

class security_context_test : public QObject
{
Q_OBJECT
private Q_SLOTS:
void init();
void cleanup();

void test_flatpak();

private:
struct {
std::unique_ptr<Srv::Display> display;
Srv::globals globals;
} server;

Clt::ConnectionThread* m_connection{nullptr};
QThread* m_thread{nullptr};
Clt::EventQueue* m_queue{nullptr};
Clt::security_context_manager_v1* m_context{nullptr};
};

constexpr auto socket_name{"wrapland-test-security-context-0"};

void security_context_test::init()
{
server.display = std::make_unique<Srv::Display>();
server.display->set_socket_name(socket_name);
server.display->start();
QVERIFY(server.display->running());

server.display->createShm();
server.globals.security_context_manager_v1
= std::make_unique<Srv::security_context_manager_v1>(server.display.get());

// setup connection
m_connection = new Clt::ConnectionThread;
QSignalSpy connected_spy(m_connection, &Clt::ConnectionThread::establishedChanged);
QVERIFY(connected_spy.isValid());
m_connection->setSocketName(socket_name);

m_thread = new QThread(this);
m_connection->moveToThread(m_thread);
m_thread->start();

m_connection->establishConnection();
QVERIFY(connected_spy.count() || connected_spy.wait());
QCOMPARE(connected_spy.count(), 1);

m_queue = new Clt::EventQueue(this);
m_queue->setup(m_connection);

Clt::Registry registry;
QSignalSpy interfacesAnnouncedSpy(&registry, &Clt::Registry::interfacesAnnounced);
QVERIFY(interfacesAnnouncedSpy.isValid());
registry.setEventQueue(m_queue);
registry.create(m_connection);
QVERIFY(registry.isValid());
registry.setup();
QVERIFY(interfacesAnnouncedSpy.wait());

m_context = registry.createSecurityContextManagerV1(
registry.interface(Clt::Registry::Interface::SecurityContextManagerV1).name,
registry.interface(Clt::Registry::Interface::SecurityContextManagerV1).version,
this);
QVERIFY(m_context->isValid());
}

void security_context_test::cleanup()
{
#define CLEANUP(variable) \
if (variable) { \
delete variable; \
variable = nullptr; \
}
CLEANUP(m_context)
CLEANUP(m_queue)
if (m_connection) {
m_connection->deleteLater();
m_connection = nullptr;
}
if (m_thread) {
m_thread->quit();
m_thread->wait();
delete m_thread;
m_thread = nullptr;
}
#undef CLEANUP

server = {};
}

void security_context_test::test_flatpak()
{
// 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 close-fd is closed.
int listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
QVERIFY(listen_fd != 0);

QTemporaryDir temp_dir;

sockaddr_un sockaddr;
sockaddr.sun_family = AF_UNIX;
snprintf(sockaddr.sun_path,
sizeof(sockaddr.sun_path),
"%s",
temp_dir.filePath("socket").toUtf8().constData());
qDebug() << "listening socket:" << sockaddr.sun_path;
QVERIFY(bind(listen_fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == 0);
QVERIFY(listen(listen_fd, 0) == 0);

int sync_fds[2];
QVERIFY(pipe(sync_fds) >= 0);
int close_fd_to_keep = sync_fds[0];
int close_fd_to_give = sync_fds[1];

auto security_context = std::unique_ptr<Clt::security_context_v1>(
m_context->create_listener(listen_fd, close_fd_to_give));
close(close_fd_to_give);
close(listen_fd);

security_context->set_instance_id("kde.unitest.instance_id");
security_context->set_app_id("kde.unittest.app_id");
security_context->set_sandbox_engine("test_sandbox_engine");
security_context->commit();
security_context = {};

qputenv("WAYLAND_DISPLAY", temp_dir.filePath("socket").toUtf8());
QSignalSpy client_connected_spy(server.display.get(), &Srv::Display::clientConnected);

// Secuirty context client connects
QVERIFY(client_connected_spy.wait());
QCOMPARE(client_connected_spy.size(), 1);

struct restricted_client {
restricted_client()
: connected_spy{&connection, &Clt::ConnectionThread::establishedChanged}
{
connection.moveToThread(&thread);
thread.start();
connection.establishConnection();
}

~restricted_client()
{
thread.quit();
thread.wait();
}

Clt::ConnectionThread connection;
QThread thread;
QSignalSpy connected_spy;
};

// connect a client using the newly created listening socket
restricted_client restricted_client1;
QVERIFY(restricted_client1.connected_spy.wait());

// Verify that our new restricted client is seen with the right security context
QCOMPARE(client_connected_spy.size(), 2);
QCOMPARE(client_connected_spy.back().front().value<Srv::Client*>()->security_context_app_id(),
"kde.unittest.app_id");

// Close the mock flatpak close-fd
close(close_fd_to_keep);

// security context properties should have not changed after close-fd is closed
QTest::qWait(500);
QCOMPARE(client_connected_spy.back().front().value<Srv::Client*>()->security_context_app_id(),
"kde.unittest.app_id");

// New clients can't connect anymore.
restricted_client restricted_client2;

QSignalSpy failed_spy(&restricted_client2.connection, &Clt::ConnectionThread::failed);
client_connected_spy.clear();

QVERIFY(restricted_client2.connected_spy.wait());
QVERIFY(client_connected_spy.isEmpty());

QEXPECT_FAIL("", "No fail on connect.", Continue);
QVERIFY(!failed_spy.isEmpty());
}

QTEST_GUILESS_MAIN(security_context_test)
#include "security_context.moc"
9 changes: 9 additions & 0 deletions server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ set(SERVER_LIB_SRCS
region.cpp
relative_pointer_v1.cpp
seat.cpp
security_context_v1.cpp
server_decoration_palette.cpp
shadow.cpp
slide.cpp
Expand Down Expand Up @@ -130,6 +131,11 @@ ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
BASENAME drm-lease-v1
)

ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${WaylandProtocols_DATADIR}/staging/security-context/security-context-v1.xml
BASENAME security-context-staging-v1
)

ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
PROTOCOL ${Wrapland_SOURCE_DIR}/src/client/protocols/blur.xml
BASENAME blur
Expand Down Expand Up @@ -300,6 +306,8 @@ set(SERVER_GENERATED_SRCS
${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-relativepointer-unstable-v1-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-relativepointer-unstable-v1-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-security-context-staging-v1-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-security-context-staging-v1-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration_palette-client-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-server_decoration_palette-server-protocol.h
${CMAKE_CURRENT_BINARY_DIR}/wayland-shadow-client-protocol.h
Expand Down Expand Up @@ -413,6 +421,7 @@ set(SERVER_LIB_HEADERS
region.h
relative_pointer_v1.h
seat.h
security_context_v1.h
server_decoration_palette.h
shadow.h
slide.h
Expand Down
10 changes: 10 additions & 0 deletions server/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,14 @@ std::string Client::executablePath() const
return d_ptr->executablePath();
}

std::string Client::security_context_app_id() const
{
return d_ptr->security_context_app_id();
}

void Client::set_security_context_app_id(std::string const& id)
{
d_ptr->set_security_context_app_id(id);
}

}
2 changes: 2 additions & 0 deletions server/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class WRAPLANDSERVER_EXPORT Client : public QObject
uid_t userId() const;
gid_t groupId() const;
std::string executablePath() const;
std::string security_context_app_id() const;
void set_security_context_app_id(std::string const& id);

Q_SIGNALS:
void disconnected(Client*);
Expand Down
4 changes: 4 additions & 0 deletions server/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class PresentationManager;
class primary_selection_device_manager;
class RelativePointerManagerV1;
class Seat;
class security_context_manager_v1;
class ServerSideDecorationPaletteManager;
class ShadowManager;
class SlideManager;
Expand Down Expand Up @@ -183,6 +184,9 @@ class WRAPLANDSERVER_EXPORT Display : public QObject
Server::kde_idle* kde_idle{nullptr};
Server::drm_lease_device_v1* drm_lease_device_v1{nullptr};
Server::IdleInhibitManagerV1* idle_inhibit_manager_v1{nullptr};

// Security
Server::security_context_manager_v1* security_context_manager_v1{nullptr};
} globals;

Q_SIGNALS:
Expand Down
Loading

0 comments on commit 7d0be51

Please sign in to comment.