diff --git a/CMakeLists.txt b/CMakeLists.txt index c383f35034..948445b439 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1110,6 +1110,8 @@ if(SDL_LIBC) check_symbol_exists(fdatasync "unistd.h" HAVE_FDATASYNC) check_symbol_exists(gethostname "unistd.h" HAVE_GETHOSTNAME) check_symbol_exists(getpagesize "unistd.h" HAVE_GETPAGESIZE) + check_symbol_exists(getresgid "unistd.h" HAVE_GETRESGID) + check_symbol_exists(getresuid "unistd.h" HAVE_GETRESUID) check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION) check_symbol_exists(sigtimedwait "signal.h" HAVE_SIGTIMEDWAIT) check_symbol_exists(setjmp "setjmp.h" HAVE_SETJMP) diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index a2a2331a54..6a98d41ede 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -195,6 +195,8 @@ #cmakedefine HAVE_ELF_AUX_INFO 1 #cmakedefine HAVE_POLL 1 #cmakedefine HAVE__EXIT 1 +#cmakedefine HAVE_GETRESUID 1 +#cmakedefine HAVE_GETRESGID 1 #endif /* HAVE_LIBC */ diff --git a/src/core/unix/SDL_gtk.c b/src/core/unix/SDL_gtk.c index f1ffce6acf..02b39d71db 100644 --- a/src/core/unix/SDL_gtk.c +++ b/src/core/unix/SDL_gtk.c @@ -21,9 +21,68 @@ #include "SDL_internal.h" #include "SDL_gtk.h" +#include +#include + +#ifndef HAVE_GETRESUID +// Non-POSIX, but Linux and some BSDs have it. +// To reduce the number of code paths, if getresuid() isn't available at +// compile-time, we behave as though it existed but failed at runtime. +static inline int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) +{ + errno = ENOSYS; + return -1; +} +#endif + +#ifndef HAVE_GETRESGID +// Same as getresuid() but for the primary group +static inline int getresgid(uid_t *ruid, uid_t *euid, uid_t *suid) +{ + errno = ENOSYS; + return -1; +} +#endif + bool SDL_CanUseGtk(void) { + // "Real", "effective" and "saved" IDs: see e.g. Linux credentials(7) + uid_t ruid = -1, euid = -1, suid = -1; + gid_t rgid = -1, egid = -1, sgid = -1; + if (!SDL_GetHintBoolean("SDL_ENABLE_GTK", true)) { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to hint"); + return false; + } + + // This is intended to match the check in gtkmain.c, rather than being + // an exhaustive check for having elevated privileges: as a result + // we don't use Linux getauxval() or prctl PR_GET_DUMPABLE, + // BSD issetugid(), or similar OS-specific detection + + if (getresuid(&ruid, &euid, &suid) != 0) { + ruid = suid = getuid(); + euid = geteuid(); + } + + if (getresgid(&rgid, &egid, &sgid) != 0) { + rgid = sgid = getgid(); + egid = getegid(); + } + + // Real ID != effective ID means we are setuid or setgid: + // GTK will refuse to initialize, and instead will call exit(). + if (ruid != euid || rgid != egid) { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to setuid/setgid"); + return false; + } + + // Real ID != saved ID means we are setuid or setgid, we previously + // dropped privileges, but we can regain them; this protects against + // accidents but does not protect against arbitrary code execution. + // Again, GTK will refuse to initialize if this is the case. + if (ruid != suid || rgid != sgid) { + SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Not using GTK due to saved uid/gid"); return false; }