From fb959dd3f29c2d0655e01147f7b0b9bb2dab4223 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sat, 3 Aug 2024 10:00:59 +0100 Subject: su: detect inability to raise privilege When privilege has been dropped by the 'drop' applet, the 'su' applet is unable to raise it again because ShellExecuteEx() thinks it unnecessary. Detect this situation, report an error and return exit code 2. Costs 72-112 bytes. (GitHub issue #437) --- include/mingw.h | 5 +++++ loginutils/suw32.c | 10 ++++++++++ win32/mingw.c | 44 ++++++++++++++++++++++---------------------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/include/mingw.h b/include/mingw.h index c4c2e199a..7a07de619 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -640,3 +640,8 @@ char *xappendword(const char *str, const char *word); int windows_env(void); void change_critical_error_dialogs(const char *newval) FAST_FUNC; char *exe_relative_path(const char *tail); +enum { + ELEVATED_PRIVILEGE = 1, + ADMIN_ENABLED = 2 +}; +int elevation_state(void); diff --git a/loginutils/suw32.c b/loginutils/suw32.c index edf42177b..a0afe5bb7 100644 --- a/loginutils/suw32.c +++ b/loginutils/suw32.c @@ -44,6 +44,16 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) char *bb_path, *cwd, *realcwd, *q, *args; DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *); +#if ENABLE_DROP || ENABLE_CDROP || ENABLE_PDROP + // If privilege has been dropped (ELEVATED_PRIVILEGE but not + // ADMIN_ENABLED) ShellExecuteEx() thinks we already have elevated + // privilege and doesn't raise privilege. In that case, give up. + if (elevation_state() == ELEVATED_PRIVILEGE) { + xfunc_error_retval = 2; + bb_error_msg_and_die("unable to restore privilege"); + } +#endif + opt = getopt32(argv, "c:NW", &opt_command); argv += optind; if (argv[0]) { diff --git a/win32/mingw.c b/win32/mingw.c index f2b025e43..97bc95d8e 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -1174,49 +1174,49 @@ char *get_user_name(void) return user_name; } -#if ENABLE_DROP || ENABLE_CDROP || ENABLE_PDROP /* * When 'drop' drops privileges TokenIsElevated is still TRUE. * Find out if we're really privileged by checking if the group * BUILTIN\Administrators is enabled. */ -static int -really_privileged(void) +int +elevation_state(void) { - BOOL admin_enabled; + int elevated = FALSE; + int enabled = TRUE; + HANDLE h; +#if ENABLE_DROP || ENABLE_CDROP || ENABLE_PDROP + BOOL admin_enabled = TRUE; unsigned char admin[16] = { 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x20, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00 }; - - if (CheckTokenMembership(NULL, (PSID)admin, &admin_enabled)) - return admin_enabled; - - return TRUE; -} -#else -# define really_privileged() (TRUE) #endif -int getuid(void) -{ - int ret = DEFAULT_UID; - HANDLE h; - if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h)) { TOKEN_ELEVATION elevation = { 0 }; DWORD size; if (GetTokenInformation(h, TokenElevation, &elevation, - sizeof(elevation), &size)) { - if (elevation.TokenIsElevated && really_privileged()) - ret = 0; - } + sizeof(elevation), &size)) + elevated = elevation.TokenIsElevated != 0; CloseHandle(h); } - return ret; + +#if ENABLE_DROP || ENABLE_CDROP || ENABLE_PDROP + if (CheckTokenMembership(NULL, (PSID)admin, &admin_enabled)) + enabled = admin_enabled != 0; +#endif + + return elevated | (enabled << 1); +} + +int getuid(void) +{ + return elevation_state() == (ELEVATED_PRIVILEGE | ADMIN_ENABLED) ? + 0 : DEFAULT_UID; } struct passwd *getpwnam(const char *name) -- cgit v1.2.3-55-g6feb