From 14ccc45d548c9dfa73594f155e920c83e2882081 Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Mon, 3 Feb 2025 11:55:28 +0000 Subject: win32: retry when command needs elevated privileges Some installer programs have an entry in their manifest to indicate that they need elevated privileges. The shell in busybox-w32 was unable to run such programs. When a program fails to run with ERROR_ELEVATION_REQUIRED, try again using ShellExecuteEx() with the 'runas' verb to give it elevated privileges. Adds 272-288 bytes. (GitHub issue #481) --- include/mingw.h | 1 + loginutils/suw32.c | 18 ++++++------------ win32/mingw.c | 22 ++++++++++++++++++++++ win32/process.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/include/mingw.h b/include/mingw.h index c41c0f91e..3ee1cc46f 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -669,3 +669,4 @@ enum { }; int elevation_state(void); void set_interp(int i) FAST_FUNC; +int mingw_shell_execute(SHELLEXECUTEINFO *info); diff --git a/loginutils/suw32.c b/loginutils/suw32.c index b3976dcfd..77c038582 100644 --- a/loginutils/suw32.c +++ b/loginutils/suw32.c @@ -48,8 +48,8 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) char *opt_command = NULL; char *opt_shell = NULL; SHELLEXECUTEINFO info; - char *bb_path, *cwd, *realcwd, *q, *args; - DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *); + const char *bb_path; + char *cwd, *realcwd, *q, *args; opt = getopt32(argv, "^c:s:tNW" "\0" "s--N:N--s", &opt_command, &opt_shell); argv += optind; @@ -70,19 +70,17 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) } #endif - /* ShellExecuteEx() needs backslash as separator in UNC paths. */ if (opt_shell) { bb_path = file_is_win32_exe(opt_shell); if (!bb_path) bb_error_msg_and_die("%s: Not found", opt_shell); args = NULL; } else { - bb_path = xstrdup(bb_busybox_exec_path); + bb_path = bb_busybox_exec_path; args = xstrdup("--busybox ash"); if (!test_mode) args = xappendword(args, "-t \"BusyBox ash (Admin)\""); } - slash_to_bs(bb_path); memset(&info, 0, sizeof(SHELLEXECUTEINFO)); info.cbSize = sizeof(SHELLEXECUTEINFO); @@ -137,12 +135,7 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) /* info.lpDirectory = NULL; */ info.nShow = SW_SHOWNORMAL; - if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) { - ret = -1; - goto end; - } - - if (!ShellExecuteExA(&info)) { + if (!mingw_shell_execute(&info)) { ret = 1; goto end; } @@ -159,7 +152,8 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) } end: if (ENABLE_FEATURE_CLEAN_UP) { - free(bb_path); + if (bb_path != bb_busybox_exec_path) + free((void *)bb_path); free(cwd); free(realcwd); free(args); diff --git a/win32/mingw.c b/win32/mingw.c index 7a5198ccf..cb1f84f30 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -2538,3 +2538,25 @@ char * FAST_FUNC exe_relative_path(const char *tail) free(exepath); return relpath; } + +int mingw_shell_execute(SHELLEXECUTEINFO *info) +{ + DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *); + char *lpath; + int ret; + + if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) { + errno = ENOSYS; + return FALSE; + } + + // ShellExecuteEx() needs backslash as separator in UNC paths. + lpath = xstrdup(info->lpFile); + slash_to_bs(lpath); + info->lpFile = lpath; + + ret = ShellExecuteExA(info); + + free(lpath); + return ret; +} diff --git a/win32/process.c b/win32/process.c index fd89707ca..33f45ee42 100644 --- a/win32/process.c +++ b/win32/process.c @@ -501,6 +501,38 @@ static NORETURN void wait_for_child(HANDLE child, const char *cmd) exit((int)code); } +static intptr_t +shell_execute(const char *path, char *const *argv) +{ + SHELLEXECUTEINFO info; + char *args; + + memset(&info, 0, sizeof(SHELLEXECUTEINFO)); + info.cbSize = sizeof(SHELLEXECUTEINFO); + info.fMask = SEE_MASK_NOCLOSEPROCESS; + /* info.hwnd = NULL; */ + info.lpVerb = "runas"; + info.lpFile = path; + + args = NULL; + if (*argv++) { + while (*argv) { + char *q = quote_arg(*argv++); + args = xappendword(args, q); + free(q); + } + } + + info.lpParameters = args; + /* info.lpDirectory = NULL; */ + info.nShow = SW_SHOWNORMAL; + + mingw_shell_execute(&info); + + free(args); + return info.hProcess ? (intptr_t)info.hProcess : -1; +} + int mingw_execvp(const char *cmd, char *const *argv) { @@ -514,6 +546,16 @@ int mingw_execve(const char *cmd, char *const *argv, char *const *envp) { intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); + + if (ret == -1 && GetLastError() == ERROR_ELEVATION_REQUIRED) { + // Command exists but failed because it wants elevated privileges. + // Try again using ShellExecuteEx(). + SetLastError(0); + ret = shell_execute(cmd, argv); + if (GetLastError()) + exit(1); + } + if (ret != -1) wait_for_child((HANDLE)ret, cmd); return ret; -- cgit v1.2.3-55-g6feb