diff options
Diffstat (limited to 'gio')
-rw-r--r-- | gio/gdbusaddress.c | 38 | ||||
-rw-r--r-- | gio/gdesktopappinfo.c | 79 | ||||
-rw-r--r-- | gio/giomodule.c | 24 | ||||
-rw-r--r-- | gio/gresource.c | 9 | ||||
-rw-r--r-- | gio/gsettingsschema.c | 6 | ||||
-rw-r--r-- | gio/gsocket.c | 4 | ||||
-rw-r--r-- | gio/tests/desktop-app-info.c | 6 | ||||
-rw-r--r-- | gio/tests/desktop-files/usr/applications/invalid-desktop.desktop | 5 | ||||
-rw-r--r-- | gio/tests/file.c | 4 |
9 files changed, 138 insertions, 37 deletions
diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 3dd3cc84b..0044cd3c6 100644 --- a/gio/gdbusaddress.c +++ b/gio/gdbusaddress.c @@ -30,6 +30,7 @@ #include "gdbusaddress.h" #include "gdbuserror.h" #include "gioenumtypes.h" +#include "glib-private.h" #include "gnetworkaddress.h" #include "gsocketclient.h" #include "giostream.h" @@ -1279,6 +1280,7 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, GCancellable *cancellable, GError **error) { + gboolean has_elevated_privileges = GLIB_PRIVATE_CALL (g_check_setuid) (); gchar *ret, *s = NULL; const gchar *starter_bus; GError *local_error; @@ -1317,10 +1319,16 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, _g_dbus_debug_print_unlock (); } + /* Don’t load the addresses from the environment if running as setuid, as they + * come from an unprivileged caller. */ switch (bus_type) { case G_BUS_TYPE_SYSTEM: - ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS")); + if (has_elevated_privileges) + ret = NULL; + else + ret = g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS")); + if (ret == NULL) { ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket"); @@ -1328,7 +1336,33 @@ g_dbus_address_get_for_bus_sync (GBusType bus_type, break; case G_BUS_TYPE_SESSION: - ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS")); + if (has_elevated_privileges) + { +#ifdef G_OS_UNIX + if (geteuid () == getuid ()) + { + /* Ideally we shouldn't do this, because setgid and + * filesystem capabilities are also elevated privileges + * with which we should not be trusting environment variables + * from the caller. Unfortunately, there are programs with + * elevated privileges that rely on the session bus being + * available. We already prevent the really dangerous + * transports like autolaunch: and unixexec: when our + * privileges are elevated, so this can only make us connect + * to the wrong AF_UNIX or TCP socket. */ + ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS")); + } + else +#endif + { + ret = NULL; + } + } + else + { + ret = g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS")); + } + if (ret == NULL) { ret = get_session_address_platform_specific (&local_error); diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index b779b309b..2b139a01c 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -305,6 +305,55 @@ desktop_file_dir_app_name_is_masked (DesktopFileDir *dir, return FALSE; } +/* Not much to go on from https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html + * so validate it as a non-empty alphanumeric ASCII string with `-` and `_` allowed. + * + * Validation is important as the desktop IDs are used to construct filenames, + * and may be set by an unprivileged caller if running in a setuid program. */ +static gboolean +validate_xdg_desktop (const gchar *desktop) +{ + gsize i; + + for (i = 0; desktop[i] != '\0'; i++) + if (desktop[i] != '-' && desktop[i] != '_' && + !g_ascii_isalnum (desktop[i])) + return FALSE; + + if (i == 0) + return FALSE; + + return TRUE; +} + +static char ** +get_valid_current_desktops (const char *value) +{ + char **tmp; + gsize i; + GPtrArray *valid_desktops; + + if (value == NULL) + value = g_getenv ("XDG_CURRENT_DESKTOP"); + if (value == NULL) + value = ""; + + tmp = g_strsplit (value, G_SEARCHPATH_SEPARATOR_S, 0); + valid_desktops = g_ptr_array_new_full (g_strv_length (tmp) + 1, g_free); + for (i = 0; tmp[i]; i++) + { + if (validate_xdg_desktop (tmp[i])) + g_ptr_array_add (valid_desktops, tmp[i]); + else + g_free (tmp[i]); + } + g_ptr_array_add (valid_desktops, NULL); + g_free (tmp); + tmp = (char **) g_ptr_array_steal (valid_desktops, NULL); + g_ptr_array_unref (valid_desktops); + return tmp; +} + static const gchar * const * get_lowercase_current_desktops (void) { @@ -312,23 +361,15 @@ get_lowercase_current_desktops (void) if (g_once_init_enter (&result)) { - const gchar *envvar; - gchar **tmp; - - envvar = g_getenv ("XDG_CURRENT_DESKTOP"); + char **tmp = get_valid_current_desktops (NULL); + gsize i, j; - if (envvar) + for (i = 0; tmp[i]; i++) { - gint i, j; - - tmp = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0); - - for (i = 0; tmp[i]; i++) - for (j = 0; tmp[i][j]; j++) - tmp[i][j] = g_ascii_tolower (tmp[i][j]); + /* Convert to lowercase. */ + for (j = 0; tmp[i][j]; j++) + tmp[i][j] = g_ascii_tolower (tmp[i][j]); } - else - tmp = g_new0 (gchar *, 0 + 1); g_once_init_leave (&result, tmp); } @@ -343,15 +384,7 @@ get_current_desktops (const gchar *value) if (g_once_init_enter (&result)) { - gchar **tmp; - - if (!value) - value = g_getenv ("XDG_CURRENT_DESKTOP"); - - if (!value) - value = ""; - - tmp = g_strsplit (value, ":", 0); + char **tmp = get_valid_current_desktops (value); g_once_init_leave (&result, tmp); } diff --git a/gio/giomodule.c b/gio/giomodule.c index dc4d6d3b3..aaf46364c 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -30,6 +30,7 @@ #include "giomodule.h" #include "giomodule-priv.h" +#include "glib-private.h" #include "glocalfilemonitor.h" #include "gnativevolumemonitor.h" #include "gproxyresolver.h" @@ -806,6 +807,9 @@ _g_io_module_get_default_type (const gchar *extension_point, return G_TYPE_INVALID; } + /* It’s OK to query the environment here, even when running as setuid, because + * it only allows a choice between existing already-loaded modules. No new + * code is loaded based on the environment variable value. */ use_this = envvar ? g_getenv (envvar) : NULL; if (g_strcmp0 (use_this, "help") == 0) { @@ -955,6 +959,9 @@ _g_io_module_get_default (const gchar *extension_point, return NULL; } + /* It’s OK to query the environment here, even when running as setuid, because + * it only allows a choice between existing already-loaded modules. No new + * code is loaded based on the environment variable value. */ use_this = envvar ? g_getenv (envvar) : NULL; if (g_strcmp0 (use_this, "help") == 0) { @@ -1150,8 +1157,16 @@ static gchar * get_gio_module_dir (void) { gchar *module_dir; - - module_dir = g_strdup (g_getenv ("GIO_MODULE_DIR")); + gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) (); + + /* If running as setuid, loading modules from an arbitrary directory + * controlled by the unprivileged user who is running the program could allow + * for execution of arbitrary code (in constructors in modules). + * Don’t allow it. + * + * If a setuid program somehow needs to load additional GIO modules, it should + * explicitly call g_io_modules_scan_all_in_directory(). */ + module_dir = !is_setuid ? g_strdup (g_getenv ("GIO_MODULE_DIR")) : NULL; if (module_dir == NULL) { #ifdef G_OS_WIN32 @@ -1183,13 +1198,14 @@ _g_io_modules_ensure_loaded (void) if (!loaded_dirs) { + gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) (); gchar *module_dir; loaded_dirs = TRUE; scope = g_io_module_scope_new (G_IO_MODULE_SCOPE_BLOCK_DUPLICATES); - /* First load any overrides, extras */ - module_path = g_getenv ("GIO_EXTRA_MODULES"); + /* First load any overrides, extras (but not if running as setuid!) */ + module_path = !is_setuid ? g_getenv ("GIO_EXTRA_MODULES") : NULL; if (module_path) { gchar **paths; diff --git a/gio/gresource.c b/gio/gresource.c index b7222b8eb..13632d971 100644 --- a/gio/gresource.c +++ b/gio/gresource.c @@ -32,6 +32,8 @@ #include <gio/gzlibdecompressor.h> #include <gio/gconverterinputstream.h> +#include "glib-private.h" + struct _GResource { int ref_count; @@ -159,7 +161,7 @@ G_DEFINE_BOXED_TYPE (GResource, g_resource, g_resource_ref, g_resource_unref) * replace resources in the program or library, without recompiling, for debugging or quick hacking and testing * purposes. Since GLib 2.50, it is possible to use the `G_RESOURCE_OVERLAYS` environment variable to selectively overlay * resources with replacements from the filesystem. It is a %G_SEARCHPATH_SEPARATOR-separated list of substitutions to perform - * during resource lookups. + * during resource lookups. It is ignored when running in a setuid process. * * A substitution has the form * @@ -330,10 +332,13 @@ g_resource_find_overlay (const gchar *path, if (g_once_init_enter (&overlay_dirs)) { + gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) (); const gchar * const *result; const gchar *envvar; - envvar = g_getenv ("G_RESOURCE_OVERLAYS"); + /* Don’t load overlays if setuid, as they could allow reading privileged + * files. */ + envvar = !is_setuid ? g_getenv ("G_RESOURCE_OVERLAYS") : NULL; if (envvar != NULL) { gchar **parts; diff --git a/gio/gsettingsschema.c b/gio/gsettingsschema.c index 0b94f76f6..8e203db01 100644 --- a/gio/gsettingsschema.c +++ b/gio/gsettingsschema.c @@ -18,6 +18,7 @@ #include "config.h" +#include "glib-private.h" #include "gsettingsschema-internal.h" #include "gsettings.h" @@ -343,6 +344,7 @@ initialise_schema_sources (void) */ if G_UNLIKELY (g_once_init_enter (&initialised)) { + gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) (); const gchar * const *dirs; const gchar *path; gchar **extra_schema_dirs; @@ -357,7 +359,9 @@ initialise_schema_sources (void) try_prepend_data_dir (g_get_user_data_dir ()); - if ((path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL) + /* Disallow loading extra schemas if running as setuid, as that could + * allow reading privileged files. */ + if (!is_setuid && (path = g_getenv ("GSETTINGS_SCHEMA_DIR")) != NULL) { extra_schema_dirs = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 0); for (i = 0; extra_schema_dirs[i]; i++); diff --git a/gio/gsocket.c b/gio/gsocket.c index 0f8f9259f..e911de781 100644 --- a/gio/gsocket.c +++ b/gio/gsocket.c @@ -5475,10 +5475,10 @@ g_socket_receive_message_with_timeout (GSocket *socket, if (errsv == WSAEINTR) continue; + win32_unset_event_mask (socket, FD_READ); + if (errsv == WSAEWOULDBLOCK) { - win32_unset_event_mask (socket, FD_READ); - if (timeout_us != 0) { if (!block_on_timeout (socket, G_IO_IN, timeout_us, diff --git a/gio/tests/desktop-app-info.c b/gio/tests/desktop-app-info.c index f4e509a59..bc50ff491 100644 --- a/gio/tests/desktop-app-info.c +++ b/gio/tests/desktop-app-info.c @@ -571,7 +571,8 @@ assert_implementations (const gchar *interface, "gnome-terminal.desktop nautilus-autorun-software.desktop gcr-viewer.desktop " \ "nautilus-connect-server.desktop kde4-dolphin.desktop gnome-music.desktop " \ "kde4-konqbrowser.desktop gucharmap.desktop kde4-okular.desktop nautilus.desktop " \ - "gedit.desktop evince.desktop file-roller.desktop dconf-editor.desktop glade.desktop" + "gedit.desktop evince.desktop file-roller.desktop dconf-editor.desktop glade.desktop " \ + "invalid-desktop.desktop" #define HOME_APPS "epiphany-weather-for-toronto-island-9c6a4e022b17686306243dada811d550d25eb1fb.desktop" #define ALL_HOME_APPS HOME_APPS " eog.desktop" @@ -726,6 +727,9 @@ test_show_in (void) assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic"); assert_shown ("gcr-prompter.desktop", TRUE, "GNOME-Classic:KDE"); assert_shown ("gcr-prompter.desktop", TRUE, "KDE:GNOME-Classic"); + assert_shown ("invalid-desktop.desktop", TRUE, "GNOME"); + assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop"); + assert_shown ("invalid-desktop.desktop", FALSE, "../invalid/desktop:../invalid/desktop"); } /* Test g_desktop_app_info_launch_uris_as_manager() and diff --git a/gio/tests/desktop-files/usr/applications/invalid-desktop.desktop b/gio/tests/desktop-files/usr/applications/invalid-desktop.desktop new file mode 100644 index 000000000..dffaa2469 --- /dev/null +++ b/gio/tests/desktop-files/usr/applications/invalid-desktop.desktop @@ -0,0 +1,5 @@ +[Desktop Entry] +Type=Application +Name=appinfo-test +OnlyShowIn=../invalid/desktop;GNOME +NotShowIn=ROX; diff --git a/gio/tests/file.c b/gio/tests/file.c index c3877af4b..d8769656c 100644 --- a/gio/tests/file.c +++ b/gio/tests/file.c @@ -1041,7 +1041,7 @@ test_measure (void) if (size > 0) g_assert_cmpuint (num_bytes, ==, size); g_assert_cmpuint (num_dirs, ==, 6); - g_assert_cmpuint (num_files, ==, 31); + g_assert_cmpuint (num_files, ==, 32); g_object_unref (file); g_free (path); @@ -1131,7 +1131,7 @@ test_measure_async (void) g_free (path); data->expected_dirs = 6; - data->expected_files = 31; + data->expected_files = 32; g_file_measure_disk_usage_async (file, G_FILE_MEASURE_APPARENT_SIZE, |