From 1f1763c37ae72752a43befd3644947a388e4ce6d Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sun, 3 Jan 2021 11:39:33 +0100 Subject: [PATCH] LaunchServer+LibDesktop: Add unveil-like mechanism for LaunchServer Clients of LaunchServer can now provide a list of allowed handlers, optionally with a specific set of URLs. The list can be sealed to prevent future additions to it. If LaunchServer receives a request to open something not on the allowed handlers list, it will disconnect the client immediately. The main idea here is to allow otherwise restricted programs to launch specific things, e.g "Help" to open their manual, or "Browser" to load the SerenityOS home page. :^) --- Libraries/LibDesktop/Launcher.cpp | 45 +++++++++++++-- Libraries/LibDesktop/Launcher.h | 3 + Services/LaunchServer/ClientConnection.cpp | 65 ++++++++++++++++++++++ Services/LaunchServer/ClientConnection.h | 12 ++++ Services/LaunchServer/LaunchServer.ipc | 4 ++ 5 files changed, 123 insertions(+), 6 deletions(-) diff --git a/Libraries/LibDesktop/Launcher.cpp b/Libraries/LibDesktop/Launcher.cpp index e2abbf0445..740756c9bf 100644 --- a/Libraries/LibDesktop/Launcher.cpp +++ b/Libraries/LibDesktop/Launcher.cpp @@ -72,10 +72,45 @@ private: virtual void handle(const Messages::LaunchClient::Dummy&) override { } }; +static LaunchServerConnection& connection() +{ + static auto connection = LaunchServerConnection::construct(); + return connection; +} + +bool Launcher::add_allowed_handler_with_any_url(const String& handler) +{ + auto response = connection().send_sync(handler); + if (!response) { + dbgln("Launcher::add_allowed_handler_with_any_url: Failed"); + return false; + } + return true; +} + +bool Launcher::add_allowed_handler_with_only_specific_urls(const String& handler, const Vector& urls) +{ + auto response = connection().send_sync(handler, urls); + if (!response) { + dbgln("Launcher::add_allowed_handler_with_only_specific_urls: Failed"); + return false; + } + return true; +} + +bool Launcher::seal_allowed_handler_list() +{ + auto response = connection().send_sync(); + if (!response) { + dbgln("Launcher::seal_allowed_handler_list: Failed"); + return false; + } + return true; +} + bool Launcher::open(const URL& url, const String& handler_name) { - auto connection = LaunchServerConnection::construct(); - return connection->send_sync(url, handler_name)->response(); + return connection().send_sync(url, handler_name)->response(); } bool Launcher::open(const URL& url, const Details& details) @@ -86,14 +121,12 @@ bool Launcher::open(const URL& url, const Details& details) Vector Launcher::get_handlers_for_url(const URL& url) { - auto connection = LaunchServerConnection::construct(); - return connection->send_sync(url.to_string())->handlers(); + return connection().send_sync(url.to_string())->handlers(); } auto Launcher::get_handlers_with_details_for_url(const URL& url) -> NonnullRefPtrVector
{ - auto connection = LaunchServerConnection::construct(); - auto details = connection->send_sync(url.to_string())->handlers_details(); + auto details = connection().send_sync(url.to_string())->handlers_details(); NonnullRefPtrVector
handlers_with_details; for (auto& value : details) { handlers_with_details.append(Details::from_details_str(value)); diff --git a/Libraries/LibDesktop/Launcher.h b/Libraries/LibDesktop/Launcher.h index 73a23a98f7..d8412f3ec7 100644 --- a/Libraries/LibDesktop/Launcher.h +++ b/Libraries/LibDesktop/Launcher.h @@ -51,6 +51,9 @@ public: static NonnullRefPtr
from_details_str(const String&); }; + [[nodiscard]] static bool add_allowed_handler_with_any_url(const String& handler); + [[nodiscard]] static bool add_allowed_handler_with_only_specific_urls(const String& handler, const Vector&); + [[nodiscard]] static bool seal_allowed_handler_list(); static bool open(const URL&, const String& handler_name = {}); static bool open(const URL&, const Details& details); static Vector get_handlers_for_url(const URL&); diff --git a/Services/LaunchServer/ClientConnection.cpp b/Services/LaunchServer/ClientConnection.cpp index 69d1523d2a..5c2fbe86be 100644 --- a/Services/LaunchServer/ClientConnection.cpp +++ b/Services/LaunchServer/ClientConnection.cpp @@ -55,6 +55,22 @@ OwnPtr ClientConnection::handle(const Mes OwnPtr ClientConnection::handle(const Messages::LaunchServer::OpenURL& request) { + if (!m_allowed_handlers.is_empty()) { + bool allowed = false; + for (auto& allowed_handler : m_allowed_handlers) { + if (allowed_handler.handler_name == request.handler_name() + && (allowed_handler.any_url || allowed_handler.urls.contains_slow(request.url()))) { + allowed = true; + break; + } + } + if (!allowed) { + // You are not on the list, go home! + did_misbehave(String::formatted("Client requested a combination of handler/URL that was not on the list: '{}' with '{}'", request.handler_name(), request.url()).characters()); + return nullptr; + } + } + URL url(request.url()); auto result = Launcher::the().open_url(url, request.handler_name()); return make(result); @@ -74,4 +90,53 @@ OwnPtr ClientConne return make(result); } +OwnPtr ClientConnection::handle(const Messages::LaunchServer::AddAllowedHandlerWithAnyURL& request) +{ + if (m_allowed_handler_list_is_sealed) { + did_misbehave("Got request to add more allowed handlers after list was sealed"); + return nullptr; + } + + if (request.handler_name().is_empty()) { + did_misbehave("Got request to allow empty handler name"); + return nullptr; + } + + m_allowed_handlers.empend(request.handler_name(), true, Vector()); + + return make(); +} + +OwnPtr ClientConnection::handle(const Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLs& request) +{ + if (m_allowed_handler_list_is_sealed) { + did_misbehave("Got request to add more allowed handlers after list was sealed"); + return nullptr; + } + + if (request.handler_name().is_empty()) { + did_misbehave("Got request to allow empty handler name"); + return nullptr; + } + + if (request.urls().is_empty()) { + did_misbehave("Got request to allow empty URL list"); + return nullptr; + } + + m_allowed_handlers.empend(request.handler_name(), false, request.urls()); + + return make(); +} + +OwnPtr ClientConnection::handle(const Messages::LaunchServer::SealAllowedHandlersList&) +{ + if (m_allowed_handler_list_is_sealed) { + did_misbehave("Got more than one request to seal the allowed handlers list"); + return nullptr; + } + + return make(); +} + } diff --git a/Services/LaunchServer/ClientConnection.h b/Services/LaunchServer/ClientConnection.h index ab3d31bdad..9f7c7d2bb5 100644 --- a/Services/LaunchServer/ClientConnection.h +++ b/Services/LaunchServer/ClientConnection.h @@ -47,5 +47,17 @@ private: virtual OwnPtr handle(const Messages::LaunchServer::OpenURL&) override; virtual OwnPtr handle(const Messages::LaunchServer::GetHandlersForURL&) override; virtual OwnPtr handle(const Messages::LaunchServer::GetHandlersWithDetailsForURL&) override; + virtual OwnPtr handle(const Messages::LaunchServer::AddAllowedHandlerWithAnyURL&) override; + virtual OwnPtr handle(const Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLs&) override; + virtual OwnPtr handle(const Messages::LaunchServer::SealAllowedHandlersList&) override; + + struct AllowedHandler { + String handler_name; + bool any_url { false }; + Vector urls; + }; + + Vector m_allowed_handlers; + bool m_allowed_handler_list_is_sealed { false }; }; } diff --git a/Services/LaunchServer/LaunchServer.ipc b/Services/LaunchServer/LaunchServer.ipc index f6871ef0a8..154198f1d2 100644 --- a/Services/LaunchServer/LaunchServer.ipc +++ b/Services/LaunchServer/LaunchServer.ipc @@ -4,4 +4,8 @@ endpoint LaunchServer = 101 OpenURL(URL url, String handler_name) => (bool response) GetHandlersForURL(URL url) => (Vector handlers) GetHandlersWithDetailsForURL(URL url) => (Vector handlers_details) + + AddAllowedHandlerWithAnyURL(String handler_name) => () + AddAllowedHandlerWithOnlySpecificURLs(String handler_name, Vector urls) => () + SealAllowedHandlersList() => () }