diff --git a/docs/reference/gio/overview.xml b/docs/reference/gio/overview.xml index 7817ab60f..d82f1ac94 100644 --- a/docs/reference/gio/overview.xml +++ b/docs/reference/gio/overview.xml @@ -436,6 +436,9 @@ Gvfs is also heavily distributed and relies on a session bus to be present. modules from this alternate directory instead of the directory built into GIO. This is useful when running tests, for example. + + This environment variable is ignored when running in a setuid program. + @@ -446,6 +449,9 @@ Gvfs is also heavily distributed and relies on a session bus to be present. paths separated by a colon, GIO will attempt to load additional modules from within the path. + + This environment variable is ignored when running in a setuid program. + diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c index 3dd3cc84b..4ec81efb2 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 is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) (); gchar *ret, *s = NULL; const gchar *starter_bus; GError *local_error; @@ -1317,10 +1319,12 @@ 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")); + ret = !is_setuid ? g_strdup (g_getenv ("DBUS_SYSTEM_BUS_ADDRESS")) : NULL; if (ret == NULL) { ret = g_strdup ("unix:path=/var/run/dbus/system_bus_socket"); @@ -1328,7 +1332,7 @@ 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")); + ret = !is_setuid ? g_strdup (g_getenv ("DBUS_SESSION_BUS_ADDRESS")) : NULL; if (ret == NULL) { ret = get_session_address_platform_specific (&local_error); diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index b779b309b..6124d61a8 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -305,6 +305,27 @@ 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 const gchar * const * get_lowercase_current_desktops (void) { @@ -320,12 +341,22 @@ get_lowercase_current_desktops (void) if (envvar) { gint i, j; + gsize tmp_len; tmp = g_strsplit (envvar, G_SEARCHPATH_SEPARATOR_S, 0); + tmp_len = g_strv_length (tmp); for (i = 0; tmp[i]; i++) - for (j = 0; tmp[i][j]; j++) - tmp[i][j] = g_ascii_tolower (tmp[i][j]); + { + /* If the desktop is invalid, drop it and shift the following + * ones (and trailing %NULL) up. */ + if (!validate_xdg_desktop (tmp[i])) + memmove (tmp + i, tmp + i + 1, tmp_len - i); + + /* 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); @@ -344,6 +375,7 @@ get_current_desktops (const gchar *value) if (g_once_init_enter (&result)) { gchar **tmp; + gsize tmp_len, i; if (!value) value = g_getenv ("XDG_CURRENT_DESKTOP"); @@ -352,6 +384,15 @@ get_current_desktops (const gchar *value) value = ""; tmp = g_strsplit (value, ":", 0); + tmp_len = g_strv_length (tmp); + + for (i = 0; tmp[i]; i++) + { + /* If the desktop is invalid, drop it and shift the following + * ones (and trailing %NULL) up. */ + if (!validate_xdg_desktop (tmp[i])) + memmove (tmp + i, tmp + i + 1, tmp_len - i); + } 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; + gboolean is_setuid = GLIB_PRIVATE_CALL (g_check_setuid) (); - module_dir = g_strdup (g_getenv ("GIO_MODULE_DIR")); + /* 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 #include +#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++);