Skip to content

Commit 1d60119

Browse files
committed
tray: Create tray icons for libappindicator securely
If we write directly to filenames in /tmp, we're subject to time-of-check/time-of-use symlink attacks on most systems (although recent Linux kernels mitigate these by default). We can avoid these attacks by securely creating a directory owned by our own uid, and doing all our file I/O in that directory. Other uids cannot create symbolic links in that directory, so we are protected from symlink attacks. This does not protect us from an attacker that is running with the same uid, but if such an attacker exists, then we have already lost. Resolves: libsdl-org#11887 Signed-off-by: Simon McVittie <[email protected]>
1 parent e6bb50a commit 1d60119

File tree

1 file changed

+31
-11
lines changed

1 file changed

+31
-11
lines changed

src/tray/unix/SDL_tray.c

+31-11
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "../SDL_tray_utils.h"
2525

2626
#include <dlfcn.h>
27+
#include <errno.h>
2728

2829
/* getpid() */
2930
#include <unistd.h>
@@ -55,6 +56,7 @@ typedef enum
5556
} GConnectFlags;
5657
gulong (*g_signal_connect_data)(gpointer instance, const gchar *detailed_signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags);
5758
void (*g_object_unref)(gpointer object);
59+
gchar *(*g_mkdtemp)(gchar *template);
5860

5961
#define g_signal_connect(instance, detailed_signal, c_handler, data) \
6062
g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)
@@ -248,6 +250,9 @@ static bool init_gtk(void)
248250
gtk_check_menu_item_get_active = dlsym(libgtk, "gtk_check_menu_item_get_active");
249251
gtk_widget_get_sensitive = dlsym(libgtk, "gtk_widget_get_sensitive");
250252

253+
/* Technically these are GLib or GObject functions, but we can find
254+
* them via GDK */
255+
g_mkdtemp = dlsym(libgdk, "g_mkdtemp");
251256
g_signal_connect_data = dlsym(libgdk, "g_signal_connect_data");
252257
g_object_unref = dlsym(libgdk, "g_object_unref");
253258

@@ -270,6 +275,7 @@ static bool init_gtk(void)
270275
!gtk_menu_shell_append ||
271276
!gtk_menu_shell_insert ||
272277
!gtk_widget_destroy ||
278+
!g_mkdtemp ||
273279
!g_signal_connect_data ||
274280
!g_object_unref ||
275281
!app_indicator_new ||
@@ -319,9 +325,12 @@ struct SDL_TrayEntry {
319325
SDL_TrayMenu *submenu;
320326
};
321327

328+
#define ICON_DIR_TEMPLATE "/tmp/SDL-tray-XXXXXX"
329+
322330
struct SDL_Tray {
323331
AppIndicator *indicator;
324332
SDL_TrayMenu *menu;
333+
char icon_dir[sizeof(ICON_DIR_TEMPLATE)];
325334
char icon_path[256];
326335
};
327336

@@ -343,19 +352,19 @@ static void call_callback(GtkMenuItem *item, gpointer ptr)
343352
}
344353
}
345354

346-
/* Since AppIndicator deals only in filenames, which are inherently subject to
347-
timing attacks, don't bother generating a secure filename. */
348-
static bool get_tmp_filename(char *buffer, size_t size)
355+
static bool new_tmp_filename(SDL_Tray *tray)
349356
{
350357
static int count = 0;
351358

352-
if (size < 64) {
353-
return SDL_SetError("Can't create temporary file for icon: size %u < 64", (unsigned int)size);
354-
}
359+
int would_have_written = SDL_snprintf(tray->icon_path, sizeof(tray->icon_path), "%s/%d.bmp", tray->icon_dir, count++);
355360

356-
int would_have_written = SDL_snprintf(buffer, size, "/tmp/sdl_appindicator_icon_%d_%d.bmp", getpid(), count++);
361+
if (would_have_written > 0 && ((unsigned) would_have_written) < sizeof(tray->icon_path) - 1) {
362+
return true;
363+
}
357364

358-
return would_have_written > 0 && would_have_written < size - 1;
365+
tray->icon_path[0] = '\0';
366+
SDL_SetError("Failed to format new temporary filename");
367+
return false;
359368
}
360369

361370
static const char *get_appindicator_id(void)
@@ -402,8 +411,16 @@ SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip)
402411
}
403412

404413
SDL_memset((void *) tray, 0, sizeof(*tray));
414+
SDL_strlcpy(tray->icon_dir, ICON_DIR_TEMPLATE, sizeof(tray->icon_dir));
415+
if (!g_mkdtemp(tray->icon_dir)) {
416+
SDL_SetError("Cannot create directory for tray icon: %s", strerror(errno));
417+
return NULL;
418+
}
419+
420+
if (!new_tmp_filename(tray)) {
421+
return NULL;
422+
}
405423

406-
get_tmp_filename(tray->icon_path, sizeof(tray->icon_path));
407424
SDL_SaveBMP(icon, tray->icon_path);
408425

409426
tray->indicator = app_indicator_new(get_appindicator_id(), tray->icon_path,
@@ -424,8 +441,7 @@ void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon)
424441

425442
/* AppIndicator caches the icon files; always change filename to avoid caching */
426443

427-
if (icon) {
428-
get_tmp_filename(tray->icon_path, sizeof(tray->icon_path));
444+
if (icon && new_tmp_filename(tray)) {
429445
SDL_SaveBMP(icon, tray->icon_path);
430446
app_indicator_set_icon(tray->indicator, tray->icon_path);
431447
} else {
@@ -677,6 +693,10 @@ void SDL_DestroyTray(SDL_Tray *tray)
677693
SDL_RemovePath(tray->icon_path);
678694
}
679695

696+
if (*tray->icon_dir) {
697+
SDL_RemovePath(tray->icon_dir);
698+
}
699+
680700
if (tray->indicator) {
681701
g_object_unref(tray->indicator);
682702
}

0 commit comments

Comments
 (0)