From 5901cb001d929249c5c0848ec1eadd8918b2c12f Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Thu, 25 Jan 2024 16:38:10 +0100 Subject: [PATCH] Xnamespace: add selection isolation Selection names (as seen by the client) are internally prefixed with the namespace ID, so each client can only access those within it's namespace. If a client within namespace "foo" want's to operate on "PRIMARY", it actually will be doing so on "PRIMARY", w/o ever noticing it. Events will sent back to the client still pointing to "PRIMARY". Signed-off-by: Enrico Weigelt, metux IT consult --- Xext/namespace/hook-selection.c | 67 +++++++++++++++++++++++++++++++++ Xext/namespace/hooks.h | 1 + Xext/namespace/meson.build | 1 + Xext/namespace/namespace.c | 4 +- 4 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 Xext/namespace/hook-selection.c diff --git a/Xext/namespace/hook-selection.c b/Xext/namespace/hook-selection.c new file mode 100644 index 000000000..f0edf27ad --- /dev/null +++ b/Xext/namespace/hook-selection.c @@ -0,0 +1,67 @@ +#define HOOK_NAME "selection" + +#include + +#include + +#include "dix/selection_priv.h" + +#include "namespace.h" +#include "hooks.h" + +static inline const char *stripNS(const char* name) { + if ((!name) || (name[0] != '<')) + return name; // can this ever happen ? + const char *got = strchr(name, '>'); + if (!got) + return name; + return ++got; +} + +/* + * This hook is rewriting the client visible selection names to internally used, + * per namespace ones. Whenever a client is asking for a selection, it's name + * is replaced by a namespaced one, e.g. asking for "PRIMARY" while being in + * namespace "foo" will become "PRIMARY" + * + * A malicious client could still send specially crafted messages to others, + * asking them to send their selection data to him. This needs to be solved + * separately, by a send hook. + */ +void hookSelectionFilter(CallbackListPtr *pcbl, void *unused, void *calldata) +{ + XNS_HOOK_HEAD(SelectionFilterParamRec); + + /* no rewrite if client is in root namespace */ + if (subj->ns->superPower) + return; + + const char *origSelectionName = NameForAtom(param->selection); + + char selname[PATH_MAX] = { 0 }; + snprintf(selname, sizeof(selname)-1, "<%s>%s", subj->ns->name, origSelectionName); + Atom realSelection = MakeAtom(selname, strlen(selname), TRUE); + + switch (param->op) { + case SELECTION_FILTER_GETOWNER: + case SELECTION_FILTER_SETOWNER: + case SELECTION_FILTER_CONVERT: + case SELECTION_FILTER_LISTEN: + // TODO: check whether window really belongs to the client + param->selection = realSelection; + break; + + case SELECTION_FILTER_NOTIFY: + { + // need to translate back, since we're having the ns-prefixed name here + const char *stripped = stripNS(origSelectionName); + param->selection = MakeAtom(stripped, strlen(stripped), TRUE); + break; + } + + // nothing to do here: already having the client visible name + case SELECTION_FILTER_EV_REQUEST: + case SELECTION_FILTER_EV_CLEAR: + break; + } +} diff --git a/Xext/namespace/hooks.h b/Xext/namespace/hooks.h index 3043ec93f..d06408c52 100644 --- a/Xext/namespace/hooks.h +++ b/Xext/namespace/hooks.h @@ -25,5 +25,6 @@ struct XnamespaceClientPriv *subj = XnsClientPriv(client); void hookClientState(CallbackListPtr *pcbl, void *unused, void *calldata); +void hookSelectionFilter(CallbackListPtr *pcbl, void *unused, void *calldata); #endif /* __XSERVER_NAMESPACE_HOOKS_H */ diff --git a/Xext/namespace/meson.build b/Xext/namespace/meson.build index d6a693bb8..165882231 100644 --- a/Xext/namespace/meson.build +++ b/Xext/namespace/meson.build @@ -3,6 +3,7 @@ libxserver_namespace = static_library( [ 'config.c', 'hook-clientstate.c', + 'hook-selection.c', 'namespace.c', ], include_directories: inc, diff --git a/Xext/namespace/namespace.c b/Xext/namespace/namespace.c index 845c2e585..b2aa59913 100644 --- a/Xext/namespace/namespace.c +++ b/Xext/namespace/namespace.c @@ -4,6 +4,7 @@ #include #include "dix/dix_priv.h" +#include "dix/selection_priv.h" #include "include/os.h" #include "miext/extinit_priv.h" @@ -27,7 +28,8 @@ NamespaceExtensionInit(void) if (!(dixRegisterPrivateKey(&namespaceClientPrivKeyRec, PRIVATE_CLIENT, sizeof(struct XnamespaceClientPriv)) && - AddCallback(&ClientStateCallback, hookClientState, NULL))) + AddCallback(&ClientStateCallback, hookClientState, NULL) && + AddCallback(&SelectionFilterCallback, hookSelectionFilter, NULL))) FatalError("NamespaceExtensionInit: allocation failure\n"); /* Do the serverClient */