From 34aed69f58d2c8742ae68dcda00de232abe631e8 Mon Sep 17 00:00:00 2001 From: Emilio Escobar Date: Mon, 26 Oct 2026 18:57:22 +0000 Subject: [PATCH] Drop real UID/GID too so GTK's bwrap image loaders can run (#1310) drop_privs() only changed the effective IDs, leaving the process at ruid=0/euid=65534. Children inherit that mixed state and bubblewrap treats it as a setuid invocation and refuses to start, so glycin's sandboxed image loaders exit with status 1 and GTK aborts in ensure_surface_for_gicon(). Use setresuid()/setresgid() where available to set the real and effective IDs to the unprivileged user while keeping the saved set-user-ID at 0, so regain_privs() (redirect commands, atexit restore of ip_forward) keeps working. Platforms without setresuid() keep the old seteuid()/setegid() behaviour. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- cmake/Modules/EttercapLibCheck.cmake | 1 + include/config.h.cmake | 1 + include/ec.h | 2 +- src/ec_utils.c | 26 ++++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/cmake/Modules/EttercapLibCheck.cmake b/cmake/Modules/EttercapLibCheck.cmake index 5a64b543b..bbb28299c 100644 --- a/cmake/Modules/EttercapLibCheck.cmake +++ b/cmake/Modules/EttercapLibCheck.cmake @@ -233,6 +233,7 @@ check_function_exists(memmem HAVE_MEMMEM) check_function_exists(memrchr HAVE_MEMRCHR) check_function_exists(basename HAVE_BASENAME) check_function_exists(strndup HAVE_STRNDUP) +check_function_exists(setresuid HAVE_SETRESUID) find_library(HAVE_PCAP pcap) if(HAVE_PCAP) diff --git a/include/config.h.cmake b/include/config.h.cmake index 69d4eded8..265404a84 100644 --- a/include/config.h.cmake +++ b/include/config.h.cmake @@ -43,6 +43,7 @@ #cmakedefine HAVE_MEMMEM #cmakedefine HAVE_MEMRCHR #cmakedefine HAVE_BASENAME +#cmakedefine HAVE_SETRESUID #cmakedefine HAVE_NCURSES #cmakedefine HAVE_GTK diff --git a/include/ec.h b/include/ec.h index d69de613b..553698250 100644 --- a/include/ec.h +++ b/include/ec.h @@ -28,6 +28,7 @@ #define _REENTRANT /* for strtok_r() */ #endif #include +#include /* for setresuid() and setresgid() */ #if defined (__USE_GNU) #undef __USE_GNU #endif @@ -35,7 +36,6 @@ #undef _GNU_SOURCE #endif #include -#include #include /* diff --git a/src/ec_utils.c b/src/ec_utils.c index ab8bbb4b6..2d25d1dc8 100644 --- a/src/ec_utils.c +++ b/src/ec_utils.c @@ -184,8 +184,17 @@ void regain_privs(void) #ifdef OS_WINDOWS return; #endif +#ifdef HAVE_SETRESUID + /* the saved set-user-ID is still 0, see drop_privs() */ + if (setresuid(0, 0, 0) < 0) + ERROR_MSG("setresuid()"); + + if (setresgid(0, 0, 0) < 0) + ERROR_MSG("setresgid()"); +#else if(seteuid(0) < 0) ERROR_MSG("seteuid()"); +#endif USER_MSG("Regained root privileges: %d %d", getuid(), geteuid()); } @@ -227,6 +236,22 @@ void drop_privs(void) reset_logfile_owners(geteuid(), getegid(), uid, gid); +#ifdef HAVE_SETRESUID + /* + * set both the real and the effective IDs, keeping only the saved + * set-user-ID as root so regain_privs() still works. + * a process with real UID 0 and a non-root effective UID looks like a + * setuid binary to its children, and e.g. bubblewrap (used by the + * glycin image loaders behind GTK) refuses to run in that state. + */ + DEBUG_MSG("drop_privs: setresuid(%d) setresgid(%d)", uid, gid); + + if ( setresgid(gid, gid, 0) < 0 ) + ERROR_MSG("setresgid()"); + + if ( setresuid(uid, uid, 0) < 0 ) + ERROR_MSG("setresuid()"); +#else DEBUG_MSG("drop_privs: seteuid(%d) setegid(%d)", uid, gid); /* drop to a good uid/gid ;) */ @@ -235,6 +260,7 @@ void drop_privs(void) if ( seteuid(uid) < 0 ) ERROR_MSG("seteuid()"); +#endif DEBUG_MSG("privs: UID: %d %d GID: %d %d", (int)getuid(), (int)geteuid(), (int)getgid(), (int)getegid() ); USER_MSG("Privileges dropped to EUID %d EGID %d...\n\n", (int)geteuid(), (int)getegid() );