diff options
Diffstat (limited to 'src/protocols/SecurityContext.cpp')
| -rw-r--r-- | src/protocols/SecurityContext.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/protocols/SecurityContext.cpp b/src/protocols/SecurityContext.cpp new file mode 100644 index 00000000..b93ef448 --- /dev/null +++ b/src/protocols/SecurityContext.cpp @@ -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(); +} |
