Skip to content

Commit

Permalink
[Wayland] Handle clipboard pasting more securely
Browse files Browse the repository at this point in the history
Only receive clipboard offers when pasting instead of storing the data
indefinitely.

This is also more performant by default as it is not doing unnecessary
work.
  • Loading branch information
lbonn committed Mar 6, 2024
1 parent 1c41373 commit d8ba203
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 72 deletions.
3 changes: 2 additions & 1 deletion include/display-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#ifndef ROFI_DISPLAY_INTERNAL_H
#define ROFI_DISPLAY_INTERNAL_H

#include "display.h"
#include "helper.h"
#include "nkutils-bindings.h"
#include <glib.h>
Expand All @@ -48,7 +49,7 @@ typedef struct _display_proxy {

void (*set_input_focus)(guint window);
void (*revert_input_focus)(void);
char *(*get_clipboard_data)(int type);
void (*get_clipboard_data)(int type, ClipboardCb callback, void *user_data);
void (*set_fullscreen_mode)(void);

guint (*scale)(void);
Expand Down
3 changes: 2 additions & 1 deletion include/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ enum clipboard_type {
CLIPBOARD_PRIMARY,
};

char *display_get_clipboard_data(enum clipboard_type);
typedef void (* ClipboardCb)(char *clipboard_data, void *user_data);
void display_get_clipboard_data(enum clipboard_type, ClipboardCb callback, void* user_data);

void display_set_fullscreen_mode(void);

Expand Down
7 changes: 5 additions & 2 deletions include/wayland-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ typedef struct {

typedef struct _wayland_seat wayland_seat;

typedef struct {
void *offer;
} clipboard_data;

typedef struct {
GMainLoop *main_loop;
GWaterWaylandSource *main_loop_source;
Expand Down Expand Up @@ -77,8 +81,7 @@ typedef struct {
int32_t scale;
NkBindingsSeat *bindings_seat;

char *clipboard_default_data;
char *clipboard_primary_data;
clipboard_data clipboards[2];

uint32_t layer_width;
uint32_t layer_height;
Expand Down
6 changes: 3 additions & 3 deletions source/display.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include "keyb.h"
#include <glib.h>

#include "display-internal.h"
#include "display.h"
#include "display-internal.h"

#include "view.h"

Expand Down Expand Up @@ -41,8 +41,8 @@ void display_startup_notification(RofiHelperExecuteContext *context,

guint display_scale(void) { return proxy->scale(); }

char *display_get_clipboard_data(enum clipboard_type type) {
return proxy->get_clipboard_data(type);
void display_get_clipboard_data(enum clipboard_type type, ClipboardCb callback, void* user_data) {
proxy->get_clipboard_data(type, callback, user_data);
}

void display_set_fullscreen_mode(void) { proxy->set_fullscreen_mode(); }
22 changes: 14 additions & 8 deletions source/view.c
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,18 @@ static void rofi_view_input_changed(void) {
}
}

#ifdef ENABLE_WAYLAND
static void rofi_view_clipboard_callback(char *clipboard_data, G_GNUC_UNUSED void *user_data) {
RofiViewState *state = rofi_view_get_active();
if (clipboard_data != NULL) {
if (state != NULL) {
rofi_view_handle_text(state, clipboard_data);
}
g_free(clipboard_data);
}
}
#endif

static void rofi_view_trigger_global_action(KeyBindingAction action) {
RofiViewState *state = rofi_view_get_active();
switch (action) {
Expand All @@ -936,10 +948,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
#endif
#ifdef ENABLE_WAYLAND
if (config.backend == DISPLAY_WAYLAND) {
char *d = display_get_clipboard_data(CLIPBOARD_PRIMARY);
if (d != NULL) {
rofi_view_handle_text(current_active_menu, d);
}
display_get_clipboard_data(CLIPBOARD_PRIMARY, rofi_view_clipboard_callback, NULL);
}
#endif
break;
Expand All @@ -954,10 +963,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
#endif
#ifdef ENABLE_WAYLAND
if (config.backend == DISPLAY_WAYLAND) {
char *d = display_get_clipboard_data(CLIPBOARD_DEFAULT);
if (d != NULL) {
rofi_view_handle_text(current_active_menu, d);
}
display_get_clipboard_data(CLIPBOARD_DEFAULT, rofi_view_clipboard_callback, NULL);
}
#endif
break;
Expand Down
101 changes: 44 additions & 57 deletions source/wayland/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -885,13 +885,12 @@ static void wayland_keyboard_release(wayland_seat *self) {

#define CLIPBOARD_READ_INCREMENT 1024

typedef void (*clipboard_read_callback)(char *data);

struct clipboard_read_info {
char *buffer;
size_t size;
int fd;
clipboard_read_callback callback;
ClipboardCb callback;
void *user_data;
};

static gboolean clipboard_read_glib_callback(GIOChannel *channel,
Expand All @@ -900,8 +899,9 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel,
struct clipboard_read_info *info = opaque;
gsize read;

switch (g_io_channel_read_chars(channel, info->buffer + info->size,
CLIPBOARD_READ_INCREMENT, &read, NULL)) {
GIOStatus status = g_io_channel_read_chars(channel, info->buffer + info->size,
CLIPBOARD_READ_INCREMENT, &read, NULL);
switch (status) {
case G_IO_STATUS_AGAIN:
return TRUE;

Expand All @@ -921,7 +921,12 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel,

default:
info->buffer[info->size] = '\0';
info->callback(info->buffer);
if (status == G_IO_STATUS_EOF) {
info->callback(info->buffer, info->user_data);
} else { // G_IO_STATUS_ERROR
g_warning("Could not read data from clipboard");
g_free(info->buffer);
}
g_io_channel_shutdown(channel, FALSE, NULL);
g_io_channel_unref(channel);
close(info->fd);
Expand All @@ -930,7 +935,7 @@ static gboolean clipboard_read_glib_callback(GIOChannel *channel,
}
}

static gboolean clipboard_read_data(int fd, clipboard_read_callback callback) {
static gboolean clipboard_read_data(int fd, ClipboardCb callback, void *user_data) {
GIOChannel *channel = g_io_channel_unix_new(fd);

struct clipboard_read_info *info = g_malloc(sizeof *info);
Expand All @@ -943,6 +948,7 @@ static gboolean clipboard_read_data(int fd, clipboard_read_callback callback) {
info->fd = fd;
info->size = 0;
info->callback = callback;
info->user_data = user_data;
info->buffer = g_malloc(CLIPBOARD_READ_INCREMENT);

if (info->buffer == NULL) {
Expand Down Expand Up @@ -980,32 +986,24 @@ static void data_device_handle_data_offer(void *data,
wl_data_offer_add_listener(offer, &data_offer_listener, NULL);
}

static void clipboard_default_callback(char *data) {
if (wayland->clipboard_default_data != NULL) {
g_free(wayland->clipboard_default_data);
static void clipboard_handle_selection(enum clipboard_type cb_type, void *offer) {
clipboard_data *clipboard = &wayland->clipboards[cb_type];

if (clipboard->offer != NULL) {
if (cb_type == CLIPBOARD_DEFAULT) {
wl_data_offer_destroy(clipboard->offer);
} else {
zwp_primary_selection_offer_v1_destroy(clipboard->offer);
}
}
wayland->clipboard_default_data = data;
clipboard->offer = offer;

}

static void data_device_handle_selection(void *data,
struct wl_data_device *data_device,
struct wl_data_offer *offer) {
if (offer == NULL) {
// clipboard is empty
return;
}

int fds[2];
if (pipe(fds) < 0) {
return;
}
wl_data_offer_receive(offer, "text/plain", fds[1]);
close(fds[1]);

wl_display_roundtrip(wayland->display);

clipboard_read_data(fds[0], clipboard_default_callback);
wl_data_offer_destroy(offer);
clipboard_handle_selection(CLIPBOARD_DEFAULT, offer);
}

static const struct wl_data_device_listener data_device_listener = {
Expand All @@ -1030,32 +1028,10 @@ static void primary_selection_device_handle_data_offer(
offer, &primary_selection_offer_listener, NULL);
}

static void clipboard_primary_callback(char *data) {
if (wayland->clipboard_primary_data != NULL) {
g_free(wayland->clipboard_primary_data);
}
wayland->clipboard_primary_data = data;
}

static void primary_selection_device_handle_selection(
void *data, struct zwp_primary_selection_device_v1 *data_device,
struct zwp_primary_selection_offer_v1 *offer) {
if (offer == NULL) {
// clipboard is empty
return;
}

int fds[2];
if (pipe(fds) < 0) {
return;
}
zwp_primary_selection_offer_v1_receive(offer, "text/plain", fds[1]);
close(fds[1]);

wl_display_roundtrip(wayland->display);

clipboard_read_data(fds[0], clipboard_primary_callback);
zwp_primary_selection_offer_v1_destroy(offer);
clipboard_handle_selection(CLIPBOARD_PRIMARY, offer);
}

static const struct zwp_primary_selection_device_v1_listener
Expand Down Expand Up @@ -1748,15 +1724,26 @@ static const struct _view_proxy *wayland_display_view_proxy(void) {

static guint wayland_display_scale(void) { return wayland->scale; }

static char *wayland_get_clipboard_data(int type) {
switch (type) {
case CLIPBOARD_DEFAULT:
return wayland->clipboard_default_data;
case CLIPBOARD_PRIMARY:
return wayland->clipboard_primary_data;
static void wayland_get_clipboard_data(int cb_type, ClipboardCb callback, void *user_data) {
clipboard_data *clipboard = &wayland->clipboards[cb_type];

if (clipboard->offer == NULL) {
return;
}

return NULL;
int fds[2];
if (pipe(fds) < 0) {
return;
}

if (cb_type == CLIPBOARD_DEFAULT) {
wl_data_offer_receive(clipboard->offer, "text/plain", fds[1]);
} else {
zwp_primary_selection_offer_v1_receive(clipboard->offer, "text/plain", fds[1]);
}
close(fds[1]);

clipboard_read_data(fds[0], callback, user_data);
}

static void wayland_set_fullscreen_mode(void) {
Expand Down

0 comments on commit d8ba203

Please sign in to comment.