You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
8.1 KiB
248 lines
8.1 KiB
From 4c2928a54482913cf236bff0e66650a8f47e17ea Mon Sep 17 00:00:00 2001
|
|
From: Colin Walters <walters@verbum.org>
|
|
Date: Wed, 22 Aug 2012 18:26:11 +0000
|
|
Subject: CVE-2012-3524: Hardening for being run in a setuid environment
|
|
|
|
Some programs attempt to use libglib (or even libgio) when setuid.
|
|
For a long time, GTK+ simply aborted if launched in this
|
|
configuration, but we never had a real policy for GLib.
|
|
|
|
I'm not sure whether we should advertise such support. However, given
|
|
that there are real-world programs that do this currently, we can make
|
|
them safer with not too much effort.
|
|
|
|
Better to fix a problem caused by an interaction between two
|
|
components in *both* places if possible.
|
|
|
|
This patch adds a private function g_check_setuid() which is used to
|
|
first ensure we don't run an external dbus-launch binary if
|
|
DBUS_SESSION_BUS_ADDRESS isn't set.
|
|
|
|
Second, we also ensure the local VFS is used in this case. The
|
|
gdaemonvfs extension point will end up talking to the session bus
|
|
which is typically undesirable in a setuid context.
|
|
|
|
Implementing g_check_setuid() is interesting - whether or not we're
|
|
running in a privilege-escalated path is operating system specific.
|
|
Note that GTK+'s code to check euid versus uid worked historically on
|
|
Unix, more modern systems have filesystem capabilities and SELinux
|
|
domain transitions, neither of which are captured by the uid
|
|
comparison.
|
|
|
|
On Linux/glibc, the way this works is that the kernel sets an
|
|
AT_SECURE flag in the ELF auxiliary vector, and glibc looks for it on
|
|
startup. If found, then glibc sets a public-but-undocumented
|
|
__libc_enable_secure variable which we can use. Unfortunately, while
|
|
it *previously* worked to check this variable, a combination of newer
|
|
binutils and RPM break it:
|
|
http://www.openwall.com/lists/owl-dev/2012/08/14/1
|
|
|
|
So for now on Linux/glibc, we fall back to the historical Unix version
|
|
until we get glibc fixed.
|
|
|
|
On some BSD variants, there is a issetugid() function. On other Unix
|
|
variants, we fall back to what GTK+ has been doing.
|
|
|
|
Reported-By: Sebastian Krahmer <krahmer@suse.de>
|
|
Signed-off-by: Colin Walters <walters@verbum.org>
|
|
---
|
|
diff --git a/configure.ac b/configure.ac
|
|
index 584df1d..67ea1a9 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -583,9 +583,20 @@ AC_TRY_COMPILE([#include <dirent.h>], [DIR *dir;],
|
|
# Checks for library functions.
|
|
AC_FUNC_VPRINTF
|
|
AC_FUNC_ALLOCA
|
|
-AC_CHECK_FUNCS(mmap posix_memalign memalign valloc fsync pipe2)
|
|
+AC_CHECK_FUNCS(mmap posix_memalign memalign valloc fsync pipe2 issetugid)
|
|
AC_CHECK_FUNCS(atexit on_exit timegm gmtime_r)
|
|
|
|
+AC_CACHE_CHECK([for __libc_enable_secure], glib_cv_have_libc_enable_secure,
|
|
+ [AC_TRY_LINK([#include <unistd.h>
|
|
+ extern int __libc_enable_secure;],
|
|
+ [return __libc_enable_secure;],
|
|
+ glib_cv_have_libc_enable_secure=yes,
|
|
+ glib_cv_have_libc_enable_secure=no)])
|
|
+AS_IF([test x$glib_cv_have_libc_enable_secure = xyes], [
|
|
+ AC_DEFINE(HAVE_LIBC_ENABLE_SECURE, 1,
|
|
+ [Define if you have the __libc_enable_secure variable (GNU libc, eglibc)])
|
|
+])
|
|
+
|
|
AC_CHECK_SIZEOF(char)
|
|
AC_CHECK_SIZEOF(short)
|
|
AC_CHECK_SIZEOF(long)
|
|
@@ -984,7 +995,7 @@ AC_MSG_RESULT(unsigned $glib_size_type)
|
|
|
|
# Check for some functions
|
|
AC_CHECK_FUNCS(lstat strerror strsignal memmove vsnprintf stpcpy strcasecmp strncasecmp poll getcwd vasprintf setenv unsetenv getc_unlocked readlink symlink fdwalk memmem)
|
|
-AC_CHECK_FUNCS(chown lchmod lchown fchmod fchown link utimes getgrgid getpwuid)
|
|
+AC_CHECK_FUNCS(chown lchmod lchown fchmod fchown link utimes getgrgid getpwuid getresuid)
|
|
AC_CHECK_FUNCS(getmntent_r setmntent endmntent hasmntopt getfsstat getvfsstat)
|
|
# Check for high-resolution sleep functions
|
|
AC_CHECK_FUNCS(splice)
|
|
diff --git a/gio/gdbusaddress.c b/gio/gdbusaddress.c
|
|
index 4aa13b9..96b6343 100644
|
|
--- a/gio/gdbusaddress.c
|
|
+++ b/gio/gdbusaddress.c
|
|
@@ -37,6 +37,7 @@
|
|
#include "giostream.h"
|
|
#include "gasyncresult.h"
|
|
#include "gsimpleasyncresult.h"
|
|
+#include "glib-private.h"
|
|
#include "gdbusprivate.h"
|
|
#include "giomodule-priv.h"
|
|
#include "gdbusdaemon.h"
|
|
@@ -1023,6 +1024,14 @@ get_session_address_dbus_launch (GError **error)
|
|
restore_dbus_verbose = FALSE;
|
|
old_dbus_verbose = NULL;
|
|
|
|
+ /* Don't run binaries as root if we're setuid. */
|
|
+ if (GLIB_PRIVATE_CALL (g_check_setuid) ())
|
|
+ {
|
|
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
|
+ _("Cannot spawn a message bus when setuid"));
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
machine_id = _g_dbus_get_machine_id (error);
|
|
if (machine_id == NULL)
|
|
{
|
|
diff --git a/gio/gvfs.c b/gio/gvfs.c
|
|
index dda8afb..9afbcec 100644
|
|
--- a/gio/gvfs.c
|
|
+++ b/gio/gvfs.c
|
|
@@ -23,6 +23,7 @@
|
|
#include "config.h"
|
|
#include <string.h>
|
|
#include "gvfs.h"
|
|
+#include "glib-private.h"
|
|
#include "glocalvfs.h"
|
|
#include "gresourcefile.h"
|
|
#include "giomodule-priv.h"
|
|
@@ -191,6 +192,8 @@ g_vfs_parse_name (GVfs *vfs,
|
|
GVfs *
|
|
g_vfs_get_default (void)
|
|
{
|
|
+ if (GLIB_PRIVATE_CALL (g_check_setuid) ())
|
|
+ return g_vfs_get_local ();
|
|
return _g_io_module_get_default (G_VFS_EXTENSION_POINT_NAME,
|
|
"GIO_USE_VFS",
|
|
(GIOModuleVerifyFunc)g_vfs_is_active);
|
|
diff --git a/glib/genviron.c b/glib/genviron.c
|
|
index 59a8bbe..9525cf0 100644
|
|
--- a/glib/genviron.c
|
|
+++ b/glib/genviron.c
|
|
@@ -40,6 +40,7 @@
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
+#include "glib-private.h"
|
|
#include "gmem.h"
|
|
#include "gmessages.h"
|
|
#include "gstrfuncs.h"
|
|
diff --git a/glib/glib-private.c b/glib/glib-private.c
|
|
index 3946e77..3506782 100644
|
|
--- a/glib/glib-private.c
|
|
+++ b/glib/glib-private.c
|
|
@@ -38,7 +38,9 @@ glib__private__ (void)
|
|
g_wakeup_signal,
|
|
g_wakeup_acknowledge,
|
|
|
|
- g_get_worker_context
|
|
+ g_get_worker_context,
|
|
+
|
|
+ g_check_setuid
|
|
};
|
|
|
|
return &table;
|
|
diff --git a/glib/glib-private.h b/glib/glib-private.h
|
|
index fde0be8..87da6f3 100644
|
|
--- a/glib/glib-private.h
|
|
+++ b/glib/glib-private.h
|
|
@@ -25,6 +25,8 @@
|
|
|
|
G_GNUC_INTERNAL
|
|
GMainContext * g_get_worker_context (void);
|
|
+G_GNUC_INTERNAL
|
|
+gboolean g_check_setuid (void);
|
|
|
|
#define GLIB_PRIVATE_CALL(symbol) (glib__private__()->symbol)
|
|
|
|
@@ -40,6 +42,8 @@ typedef struct {
|
|
/* See gmain.c */
|
|
GMainContext * (* g_get_worker_context) (void);
|
|
/* Add other private functions here, initialize them in glib-private.c */
|
|
+
|
|
+ gboolean (* g_check_setuid) (void);
|
|
} GLibPrivateVTable;
|
|
|
|
GLibPrivateVTable *glib__private__ (void);
|
|
diff --git a/glib/gutils.c b/glib/gutils.c
|
|
index 38b5e44..f8a38d1 100644
|
|
--- a/glib/gutils.c
|
|
+++ b/glib/gutils.c
|
|
@@ -2409,3 +2409,60 @@ g_get_tmp_dir (void)
|
|
}
|
|
|
|
#endif
|
|
+
|
|
+/* Private API:
|
|
+ *
|
|
+ * Returns %TRUE if the current process was executed as setuid (or an
|
|
+ * equivalent __libc_enable_secure is available). See:
|
|
+ * http://osdir.com/ml/linux.lfs.hardened/2007-04/msg00032.html
|
|
+ */
|
|
+gboolean
|
|
+g_check_setuid (void)
|
|
+{
|
|
+ /* TODO: get __libc_enable_secure exported from glibc.
|
|
+ * See http://www.openwall.com/lists/owl-dev/2012/08/14/1
|
|
+ */
|
|
+#if 0 && defined(HAVE_LIBC_ENABLE_SECURE)
|
|
+ {
|
|
+ /* See glibc/include/unistd.h */
|
|
+ extern int __libc_enable_secure;
|
|
+ return __libc_enable_secure;
|
|
+ }
|
|
+#elif defined(HAVE_ISSETUGID)
|
|
+ /* BSD: http://www.freebsd.org/cgi/man.cgi?query=issetugid&sektion=2 */
|
|
+ return issetugid ();
|
|
+#elif defined(G_OS_UNIX)
|
|
+ uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
|
|
+ gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
|
|
+
|
|
+ static gsize check_setuid_initialised;
|
|
+ static gboolean is_setuid;
|
|
+
|
|
+ if (g_once_init_enter (&check_setuid_initialised))
|
|
+ {
|
|
+#ifdef HAVE_GETRESUID
|
|
+ /* These aren't in the header files, so we prototype them here.
|
|
+ */
|
|
+ int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid);
|
|
+ int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid);
|
|
+
|
|
+ if (getresuid (&ruid, &euid, &suid) != 0 ||
|
|
+ getresgid (&rgid, &egid, &sgid) != 0)
|
|
+#endif /* HAVE_GETRESUID */
|
|
+ {
|
|
+ suid = ruid = getuid ();
|
|
+ sgid = rgid = getgid ();
|
|
+ euid = geteuid ();
|
|
+ egid = getegid ();
|
|
+ }
|
|
+
|
|
+ is_setuid = (ruid != euid || ruid != suid ||
|
|
+ rgid != egid || rgid != sgid);
|
|
+
|
|
+ g_once_init_leave (&check_setuid_initialised, 1);
|
|
+ }
|
|
+ return is_setuid;
|
|
+#else
|
|
+ return FALSE;
|
|
+#endif
|
|
+}
|
|
--
|
|
cgit v0.9.0.2
|