From 09648a0b71473d5fb5d4009d4d4e0cf6fbaf5b65 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Tue, 7 Jan 2025 02:57:32 +0100 Subject: [PATCH] Initial version very hardcoded, but great for demonstrations ig. --- .gitignore | 10 +++++ Makefile | 14 +++++++ README.md | 35 ++++++++++++++++ unlocker.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 unlocker.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a99c330 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# compilation artifacts +*.o +wl_unlocker + +# autogenerated by wayland-scanner +session-lock.h +session-lock.c + +.* +!.gitignore diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..173b16c --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +# FIXME: use pkgconf --libs wayland-client +CFLAGS:=-lwayland +LDFLAGS:=-lwayland-client +wl_unlocker: unlocker.o session-lock.o + gcc $(LDFLAGS) unlocker.o session-lock.o -o wl_unlocker + +unlocker.o: unlocker.c session-lock.h + +session-lock.h: + # FIXME: don't hardcode path! + wayland-scanner client-header /usr/share/wayland-protocols/staging/ext-session-lock/ext-session-lock-v1.xml session-lock.h + +session-lock.c: + wayland-scanner private-code /usr/share/wayland-protocols/staging/ext-session-lock/ext-session-lock-v1.xml session-lock.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..80a9aea --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +A simple wayland unlocker +==== + +Two goals: learn how to use wayland protocols on the low level, and have a tool +to recover a locked session when the original locker is killed (when a shell is +available and *the compositor's policy* allows that). + +I might use this as a demonstration project, in that case more thorough docs +might emerge… + +Security +---- + +This might under certain circumstances undermine the security model of the +compositor, because whoever is able to run this might unlock the session. Use +and install with that knowledge. + +However, if someone has `rw` access to the compositor's socket, they can do the +same nevertheless, so in a regular system this is probably a non-issue. + +That all said, use at your own risk, I am not responsible for your fails :-) + +Building +-------- + +TODO + +Licence +------- + +GPL v2 only + +Improvements, patches, comments, feedback, praise, … +-------- +→ [wl\_unlocker@pokemon.ledoian.cz](mailto:wl_unlocker@pokemon.ledoian.cz) diff --git a/unlocker.c b/unlocker.c new file mode 100644 index 0000000..de8f8f2 --- /dev/null +++ b/unlocker.c @@ -0,0 +1,120 @@ +// Just random stdlib (and POSIX) so I don't have to think about these +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Wayland includes +#include "wayland-client.h" +#include "session-lock.h" + +#define LOCK_MANAGER_NAME "ext_session_lock_manager_v1" + +// global state +struct wl_display * wl = NULL; +struct wl_registry * reg = NULL; +struct ext_session_lock_manager_v1 * lockmgr = NULL; +struct ext_session_lock_v1 * lock = NULL; + +enum lock_state {LOCKED, FINISHED, UNSET} lock_state = UNSET; + +static void registry_global_handler(void * data, struct wl_registry * wl_registry, uint32_t name, const char * interface, uint32_t version) { + printf("UwU, iface %s (name %d) appeared!\n", interface, name); + if (strcmp(interface, LOCK_MANAGER_NAME) == 0) { + // fire the bind outright + lockmgr = wl_registry_bind(wl_registry, name, &ext_session_lock_manager_v1_interface, 1/*version, we don't know anything newer*/); + } +} + +static void registry_global_remove_handler(void * data, struct wl_registry * wl_registry, uint32_t name) { + printf("OwO, iface %d disappeared, should destroy!\n", name); +} + +static void callback_done_handler(void * data, struct wl_callback * wl_callback, uint32_t callback_data) { + printf("Heh, callback done!\n"); +} + +static void lock_locked_handler(void * data, struct ext_session_lock_v1 * lock) { + enum lock_state * state = (enum lock_state *)data; + *state = LOCKED; +} + +static void lock_finished_handler(void * data, struct ext_session_lock_v1 * lock) { + enum lock_state * state = (enum lock_state *)data; + *state = FINISHED; +} + +int main(void) { + // get wl_display, the core object + wl = wl_display_connect(NULL); + + // prepare listeners for wl_registry.global + struct wl_registry_listener reg_listener = { + .global = ®istry_global_handler, + .global_remove = ®istry_global_remove_handler, + }; + reg = wl_display_get_registry(wl); + wl_registry_add_listener(reg, ®_listener, NULL); + + // add the barrier to the end + struct wl_callback * cbk = wl_display_sync(wl); + struct wl_callback_listener cbk_l = { + .done = &callback_done_handler, + }; + wl_callback_add_listener(cbk, &cbk_l, NULL); + + // process the queue + wl_display_dispatch(wl); + + if (lockmgr == NULL) { + fprintf(stderr, "No support for un/locking, no fun.\n"); + return 1; + } + lock = ext_session_lock_manager_v1_lock(lockmgr); + struct ext_session_lock_v1_listener lock_l = { + .locked = &lock_locked_handler, + .finished = &lock_finished_handler, + }; + ext_session_lock_v1_add_listener(lock, &lock_l, &lock_state); + + // process the queue + wl_display_dispatch(wl); + + if (lock_state == UNSET) { + fprintf(stderr, "BUG!\n"); + return 1; + } else if (lock_state == FINISHED) { + printf("Not locked.\n"); + // nothing to do, just destroy stuff + ext_session_lock_v1_destroy(lock); + } else if (lock_state == LOCKED) { + printf("Unlocking…\n"); + ext_session_lock_v1_unlock_and_destroy(lock); + } else { + fprintf(stderr, "DAFUQ?\n"); + return 1; + } + + // just for completeness: destroy the objects we created. Also, set NULL for safety. + printf("Cleaning up…\n"); + // lock is destroyed already + lock = NULL; + ext_session_lock_manager_v1_destroy(lockmgr); + lockmgr = NULL; + wl_registry_destroy(reg); + reg = NULL; + // Display is special, that is diconnected and not destroyed. + // Also, make the roundtrip beforehand + // FIXME: is wl_display_roundtrip the thing we want? What does it return? + wl_display_roundtrip(wl); + wl_display_disconnect(wl); + wl = NULL; + + printf("Done lol\n"); + return 0; +}