mirror of
https://github.com/hyprwm/Hyprland
synced 2024-12-18 22:49:48 +01:00
parent
0c7a7e2d56
commit
da86aac0f5
10 changed files with 374 additions and 1 deletions
|
@ -331,6 +331,7 @@ protocolnew("staging/drm-lease" "drm-lease-v1" false)
|
|||
protocolnew("staging/linux-drm-syncobj" "linux-drm-syncobj-v1" false)
|
||||
protocolnew("staging/xdg-dialog" "xdg-dialog-v1" false)
|
||||
protocolnew("staging/single-pixel-buffer" "single-pixel-buffer-v1" false)
|
||||
protocolnew("staging/security-context" "security-context-v1" false)
|
||||
|
||||
protocolwayland()
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ protocols = [
|
|||
wayland_protocol_dir / 'staging/linux-drm-syncobj/linux-drm-syncobj-v1.xml',
|
||||
wayland_protocol_dir / 'staging/xdg-dialog/xdg-dialog-v1.xml',
|
||||
wayland_protocol_dir / 'staging/single-pixel-buffer/single-pixel-buffer-v1.xml',
|
||||
wayland_protocol_dir / 'staging/security-context/security-context-v1.xml',
|
||||
]
|
||||
|
||||
wl_protocols = []
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "protocols/LayerShell.hpp"
|
||||
#include "protocols/XDGShell.hpp"
|
||||
#include "protocols/XDGOutput.hpp"
|
||||
#include "protocols/SecurityContext.hpp"
|
||||
#include "protocols/core/Compositor.hpp"
|
||||
#include "protocols/core/Subcompositor.hpp"
|
||||
#include "desktop/LayerSurface.hpp"
|
||||
|
@ -210,11 +211,22 @@ void CCompositor::setRandomSplash() {
|
|||
|
||||
static std::vector<SP<Aquamarine::IOutput>> pendingOutputs;
|
||||
|
||||
//
|
||||
|
||||
static bool filterGlobals(const wl_client* client, const wl_global* global, void* data) {
|
||||
if (!PROTO::securityContext->isClientSandboxed(client))
|
||||
return true;
|
||||
|
||||
return !g_pProtocolManager || !g_pProtocolManager->isGlobalPrivileged(global);
|
||||
}
|
||||
|
||||
//
|
||||
void CCompositor::initServer(std::string socketName, int socketFd) {
|
||||
|
||||
m_sWLDisplay = wl_display_create();
|
||||
|
||||
wl_display_set_global_filter(m_sWLDisplay, ::filterGlobals, nullptr);
|
||||
|
||||
m_sWLEventLoop = wl_display_get_event_loop(m_sWLDisplay);
|
||||
|
||||
// register crit signal handler
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "../protocols/GlobalShortcuts.hpp"
|
||||
#include "../protocols/XDGDialog.hpp"
|
||||
#include "../protocols/SinglePixel.hpp"
|
||||
#include "../protocols/SecurityContext.hpp"
|
||||
|
||||
#include "../protocols/core/Seat.hpp"
|
||||
#include "../protocols/core/DataDevice.hpp"
|
||||
|
@ -155,6 +156,7 @@ CProtocolManager::CProtocolManager() {
|
|||
PROTO::globalShortcuts = std::make_unique<CGlobalShortcutsProtocol>(&hyprland_global_shortcuts_manager_v1_interface, 1, "GlobalShortcuts");
|
||||
PROTO::xdgDialog = std::make_unique<CXDGDialogProtocol>(&xdg_dialog_v1_interface, 1, "XDGDialog");
|
||||
PROTO::singlePixel = std::make_unique<CSinglePixelProtocol>(&wp_single_pixel_buffer_manager_v1_interface, 1, "SinglePixel");
|
||||
PROTO::securityContext = std::make_unique<CSecurityContextProtocol>(&wp_security_context_manager_v1_interface, 1, "SecurityContext");
|
||||
|
||||
for (auto const& b : g_pCompositor->m_pAqBackend->getImplementations()) {
|
||||
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
|
||||
|
@ -225,9 +227,62 @@ CProtocolManager::~CProtocolManager() {
|
|||
PROTO::toplevelExport.reset();
|
||||
PROTO::globalShortcuts.reset();
|
||||
PROTO::xdgDialog.reset();
|
||||
PROTO::singlePixel.reset();
|
||||
PROTO::securityContext.reset();
|
||||
|
||||
PROTO::lease.reset();
|
||||
PROTO::sync.reset();
|
||||
PROTO::mesaDRM.reset();
|
||||
PROTO::linuxDma.reset();
|
||||
}
|
||||
|
||||
bool CProtocolManager::isGlobalPrivileged(const wl_global* global) {
|
||||
if (!global)
|
||||
return false;
|
||||
|
||||
for (auto& [k, v] : PROTO::outputs) {
|
||||
if (global == v->getGlobal())
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is a static whitelist of allowed protocols,
|
||||
// outputs are dynamic so we checked them above
|
||||
// clang-format off
|
||||
static const std::vector<wl_global*> ALLOWED_WHITELIST = {
|
||||
PROTO::seat->getGlobal(),
|
||||
PROTO::data->getGlobal(),
|
||||
PROTO::compositor->getGlobal(),
|
||||
PROTO::subcompositor->getGlobal(),
|
||||
PROTO::shm->getGlobal(),
|
||||
PROTO::viewport->getGlobal(),
|
||||
PROTO::tearing->getGlobal(),
|
||||
PROTO::fractional->getGlobal(),
|
||||
PROTO::cursorShape->getGlobal(),
|
||||
PROTO::idleInhibit->getGlobal(),
|
||||
PROTO::relativePointer->getGlobal(),
|
||||
PROTO::xdgDecoration->getGlobal(),
|
||||
PROTO::alphaModifier->getGlobal(),
|
||||
PROTO::pointerGestures->getGlobal(),
|
||||
PROTO::shortcutsInhibit->getGlobal(),
|
||||
PROTO::textInputV1->getGlobal(),
|
||||
PROTO::textInputV3->getGlobal(),
|
||||
PROTO::constraints->getGlobal(),
|
||||
PROTO::activation->getGlobal(),
|
||||
PROTO::idle->getGlobal(),
|
||||
PROTO::ime->getGlobal(),
|
||||
PROTO::virtualKeyboard->getGlobal(),
|
||||
PROTO::virtualPointer->getGlobal(),
|
||||
PROTO::serverDecorationKDE->getGlobal(),
|
||||
PROTO::tablet->getGlobal(),
|
||||
PROTO::presentation->getGlobal(),
|
||||
PROTO::xdgShell->getGlobal(),
|
||||
PROTO::xdgDialog->getGlobal(),
|
||||
PROTO::singlePixel->getGlobal(),
|
||||
PROTO::sync ? PROTO::sync->getGlobal() : nullptr,
|
||||
PROTO::mesaDRM ? PROTO::mesaDRM->getGlobal() : nullptr,
|
||||
PROTO::linuxDma ? PROTO::linuxDma->getGlobal() : nullptr,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
return std::find(ALLOWED_WHITELIST.begin(), ALLOWED_WHITELIST.end(), global) == ALLOWED_WHITELIST.end();
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ class CProtocolManager {
|
|||
CProtocolManager();
|
||||
~CProtocolManager();
|
||||
|
||||
bool isGlobalPrivileged(const wl_global* global);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, CHyprSignalListener> m_mModeChangeListeners;
|
||||
|
||||
|
|
214
src/protocols/SecurityContext.cpp
Normal file
214
src/protocols/SecurityContext.cpp
Normal file
|
@ -0,0 +1,214 @@
|
|||
#include "SecurityContext.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include <sys/socket.h>
|
||||
|
||||
static int onListenFdEvent(int fd, uint32_t mask, void* data) {
|
||||
auto sc = (CSecurityContext*)data;
|
||||
sc->onListen(mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int onCloseFdEvent(int fd, uint32_t mask, void* data) {
|
||||
auto sc = (CSecurityContext*)data;
|
||||
sc->onClose(mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SP<CSecurityContextSandboxedClient> CSecurityContextSandboxedClient::create(int clientFD) {
|
||||
auto p = SP<CSecurityContextSandboxedClient>(new CSecurityContextSandboxedClient(clientFD));
|
||||
if (!p->client)
|
||||
return nullptr;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void onSecurityContextClientDestroy(wl_listener* l, void* d) {
|
||||
CSecurityContextSandboxedClient* client = wl_container_of(l, client, destroyListener);
|
||||
client->onDestroy();
|
||||
}
|
||||
|
||||
CSecurityContextSandboxedClient::CSecurityContextSandboxedClient(int clientFD) {
|
||||
client = wl_client_create(g_pCompositor->m_sWLDisplay, clientFD);
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
destroyListener.notify = ::onSecurityContextClientDestroy;
|
||||
wl_client_add_destroy_late_listener(client, &destroyListener);
|
||||
}
|
||||
|
||||
CSecurityContextSandboxedClient::~CSecurityContextSandboxedClient() {
|
||||
wl_list_remove(&destroyListener.link);
|
||||
}
|
||||
|
||||
void CSecurityContextSandboxedClient::onDestroy() {
|
||||
std::erase_if(PROTO::securityContext->m_vSandboxedClients, [this](const auto& e) { return e.get() == this; });
|
||||
}
|
||||
|
||||
CSecurityContext::CSecurityContext(SP<CWpSecurityContextV1> resource_, int listenFD_, int closeFD_) : listenFD(listenFD_), closeFD(closeFD_), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setDestroy([this](CWpSecurityContextV1* r) {
|
||||
LOGM(LOG, "security_context at 0x{:x}: resource destroyed, keeping context until fd hangup", (uintptr_t)this);
|
||||
resource = nullptr;
|
||||
});
|
||||
resource->setOnDestroy([this](CWpSecurityContextV1* r) {
|
||||
LOGM(LOG, "security_context at 0x{:x}: resource destroyed, keeping context until fd hangup", (uintptr_t)this);
|
||||
resource = nullptr;
|
||||
});
|
||||
|
||||
LOGM(LOG, "New security_context at 0x{:x}", (uintptr_t)this);
|
||||
|
||||
resource->setSetSandboxEngine([this](CWpSecurityContextV1* r, const char* engine) {
|
||||
if (!sandboxEngine.empty()) {
|
||||
r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Sandbox engine already set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committed) {
|
||||
r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Context already committed");
|
||||
return;
|
||||
}
|
||||
|
||||
sandboxEngine = engine ? engine : "(null)";
|
||||
LOGM(LOG, "security_context at 0x{:x} sets engine to {}", (uintptr_t)this, sandboxEngine);
|
||||
});
|
||||
|
||||
resource->setSetAppId([this](CWpSecurityContextV1* r, const char* appid) {
|
||||
if (!appID.empty()) {
|
||||
r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Sandbox appid already set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committed) {
|
||||
r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Context already committed");
|
||||
return;
|
||||
}
|
||||
|
||||
appID = appid ? appid : "(null)";
|
||||
LOGM(LOG, "security_context at 0x{:x} sets appid to {}", (uintptr_t)this, appID);
|
||||
});
|
||||
|
||||
resource->setSetInstanceId([this](CWpSecurityContextV1* r, const char* instance) {
|
||||
if (!instanceID.empty()) {
|
||||
r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_SET, "Sandbox instance already set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (committed) {
|
||||
r->error(WP_SECURITY_CONTEXT_V1_ERROR_ALREADY_USED, "Context already committed");
|
||||
return;
|
||||
}
|
||||
|
||||
instanceID = instance ? instance : "(null)";
|
||||
LOGM(LOG, "security_context at 0x{:x} sets instance to {}", (uintptr_t)this, instanceID);
|
||||
});
|
||||
|
||||
resource->setCommit([this](CWpSecurityContextV1* r) {
|
||||
committed = true;
|
||||
|
||||
LOGM(LOG, "security_context at 0x{:x} commits", (uintptr_t)this);
|
||||
|
||||
listenSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, listenFD, WL_EVENT_READABLE, ::onListenFdEvent, this);
|
||||
closeSource = wl_event_loop_add_fd(g_pCompositor->m_sWLEventLoop, closeFD, 0, ::onCloseFdEvent, this);
|
||||
|
||||
if (!listenSource || !closeSource) {
|
||||
r->noMemory();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CSecurityContext::~CSecurityContext() {
|
||||
if (listenSource)
|
||||
wl_event_source_remove(listenSource);
|
||||
if (closeSource)
|
||||
wl_event_source_remove(closeSource);
|
||||
}
|
||||
|
||||
bool CSecurityContext::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
void CSecurityContext::onListen(uint32_t mask) {
|
||||
if (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR)) {
|
||||
LOGM(ERR, "security_context at 0x{:x} got an error in listen", (uintptr_t)this);
|
||||
PROTO::securityContext->destroyContext(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(mask & WL_EVENT_READABLE))
|
||||
return;
|
||||
|
||||
int clientFD = accept(listenFD, nullptr, nullptr);
|
||||
if (clientFD < 0) {
|
||||
LOGM(ERR, "security_context at 0x{:x} couldn't accept", (uintptr_t)this);
|
||||
return;
|
||||
}
|
||||
|
||||
auto newClient = CSecurityContextSandboxedClient::create(clientFD);
|
||||
if (!newClient) {
|
||||
LOGM(ERR, "security_context at 0x{:x} couldn't create a client", (uintptr_t)this);
|
||||
close(clientFD);
|
||||
return;
|
||||
}
|
||||
|
||||
PROTO::securityContext->m_vSandboxedClients.emplace_back(newClient);
|
||||
|
||||
LOGM(LOG, "security_context at 0x{:x} got a new wl_client 0x{:x}", (uintptr_t)this, (uintptr_t)newClient->client);
|
||||
}
|
||||
|
||||
void CSecurityContext::onClose(uint32_t mask) {
|
||||
if (!(mask & (WL_EVENT_ERROR | WL_EVENT_HANGUP)))
|
||||
return;
|
||||
|
||||
PROTO::securityContext->destroyContext(this);
|
||||
}
|
||||
|
||||
CSecurityContextManagerResource::CSecurityContextManagerResource(SP<CWpSecurityContextManagerV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setDestroy([this](CWpSecurityContextManagerV1* r) { PROTO::securityContext->destroyResource(this); });
|
||||
resource->setOnDestroy([this](CWpSecurityContextManagerV1* r) { PROTO::securityContext->destroyResource(this); });
|
||||
|
||||
resource->setCreateListener([](CWpSecurityContextManagerV1* r, uint32_t id, int32_t lfd, int32_t cfd) {
|
||||
const auto RESOURCE =
|
||||
PROTO::securityContext->m_vContexts.emplace_back(makeShared<CSecurityContext>(makeShared<CWpSecurityContextV1>(r->client(), r->version(), id), lfd, cfd));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
r->noMemory();
|
||||
PROTO::securityContext->m_vContexts.pop_back();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool CSecurityContextManagerResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CSecurityContextProtocol::CSecurityContextProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CSecurityContextProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CSecurityContextManagerResource>(makeShared<CWpSecurityContextManagerV1>(client, ver, id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vManagers.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CSecurityContextProtocol::destroyResource(CSecurityContextManagerResource* res) {
|
||||
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == res; });
|
||||
}
|
||||
|
||||
void CSecurityContextProtocol::destroyContext(CSecurityContext* context) {
|
||||
std::erase_if(m_vContexts, [&](const auto& other) { return other.get() == context; });
|
||||
}
|
||||
|
||||
bool CSecurityContextProtocol::isClientSandboxed(const wl_client* client) {
|
||||
return std::find_if(m_vSandboxedClients.begin(), m_vSandboxedClients.end(), [client](const auto& e) { return e->client == client; }) != m_vSandboxedClients.end();
|
||||
}
|
83
src/protocols/SecurityContext.hpp
Normal file
83
src/protocols/SecurityContext.hpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "security-context-v1.hpp"
|
||||
|
||||
class CSecurityContext {
|
||||
public:
|
||||
CSecurityContext(SP<CWpSecurityContextV1> resource_, int listenFD_, int closeFD_);
|
||||
~CSecurityContext();
|
||||
|
||||
bool good();
|
||||
|
||||
std::string sandboxEngine, appID, instanceID;
|
||||
int listenFD = -1, closeFD = -1;
|
||||
|
||||
void onListen(uint32_t mask);
|
||||
void onClose(uint32_t mask);
|
||||
|
||||
private:
|
||||
SP<CWpSecurityContextV1> resource;
|
||||
|
||||
wl_event_source * listenSource = nullptr, *closeSource = nullptr;
|
||||
|
||||
bool committed = false;
|
||||
};
|
||||
|
||||
class CSecurityContextManagerResource {
|
||||
public:
|
||||
CSecurityContextManagerResource(SP<CWpSecurityContextManagerV1> resource_);
|
||||
|
||||
bool good();
|
||||
|
||||
private:
|
||||
SP<CWpSecurityContextManagerV1> resource;
|
||||
};
|
||||
|
||||
class CSecurityContextSandboxedClient {
|
||||
public:
|
||||
static SP<CSecurityContextSandboxedClient> create(int clientFD);
|
||||
~CSecurityContextSandboxedClient();
|
||||
|
||||
void onDestroy();
|
||||
|
||||
wl_listener destroyListener;
|
||||
|
||||
private:
|
||||
CSecurityContextSandboxedClient(int clientFD);
|
||||
|
||||
wl_client* client = nullptr;
|
||||
|
||||
friend class CSecurityContextProtocol;
|
||||
friend class CSecurityContext;
|
||||
};
|
||||
|
||||
class CSecurityContextProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CSecurityContextProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
bool isClientSandboxed(const wl_client* client);
|
||||
|
||||
private:
|
||||
void destroyResource(CSecurityContextManagerResource* resource);
|
||||
|
||||
void destroyContext(CSecurityContext* context);
|
||||
|
||||
//
|
||||
std::vector<SP<CSecurityContextManagerResource>> m_vManagers;
|
||||
std::vector<SP<CSecurityContext>> m_vContexts;
|
||||
std::vector<SP<CSecurityContextSandboxedClient>> m_vSandboxedClients;
|
||||
|
||||
friend class CSecurityContextManagerResource;
|
||||
friend class CSecurityContext;
|
||||
friend class CSecurityContextSandboxedClient;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CSecurityContextProtocol> securityContext;
|
||||
};
|
|
@ -60,7 +60,7 @@ class CTextInputV1 {
|
|||
friend class CTextInputV1Protocol;
|
||||
};
|
||||
|
||||
class CTextInputV1Protocol : IWaylandProtocol {
|
||||
class CTextInputV1Protocol : public IWaylandProtocol {
|
||||
public:
|
||||
CTextInputV1Protocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
|
|
|
@ -40,3 +40,7 @@ IWaylandProtocol::~IWaylandProtocol() {
|
|||
void IWaylandProtocol::removeGlobal() {
|
||||
wl_global_remove(m_pGlobal);
|
||||
}
|
||||
|
||||
wl_global* IWaylandProtocol::getGlobal() {
|
||||
return m_pGlobal;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ class IWaylandProtocol {
|
|||
|
||||
virtual void onDisplayDestroy();
|
||||
virtual void removeGlobal();
|
||||
virtual wl_global* getGlobal();
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) = 0;
|
||||
|
||||
|
|
Loading…
Reference in a new issue