diff options
author | Ron Yorston <rmy@pobox.com> | 2025-02-03 11:55:28 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2025-02-03 11:55:28 +0000 |
commit | 14ccc45d548c9dfa73594f155e920c83e2882081 (patch) | |
tree | af1fcbc52ba1240474f321346781144d983bdc8d | |
parent | 820ade383860692d0b5abbba00a3e428f21b88e4 (diff) | |
download | busybox-w32-14ccc45d548c9dfa73594f155e920c83e2882081.tar.gz busybox-w32-14ccc45d548c9dfa73594f155e920c83e2882081.tar.bz2 busybox-w32-14ccc45d548c9dfa73594f155e920c83e2882081.zip |
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)
-rw-r--r-- | include/mingw.h | 1 | ||||
-rw-r--r-- | loginutils/suw32.c | 18 | ||||
-rw-r--r-- | win32/mingw.c | 22 | ||||
-rw-r--r-- | 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 { | |||
669 | }; | 669 | }; |
670 | int elevation_state(void); | 670 | int elevation_state(void); |
671 | void set_interp(int i) FAST_FUNC; | 671 | void set_interp(int i) FAST_FUNC; |
672 | 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) | |||
48 | char *opt_command = NULL; | 48 | char *opt_command = NULL; |
49 | char *opt_shell = NULL; | 49 | char *opt_shell = NULL; |
50 | SHELLEXECUTEINFO info; | 50 | SHELLEXECUTEINFO info; |
51 | char *bb_path, *cwd, *realcwd, *q, *args; | 51 | const char *bb_path; |
52 | DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *); | 52 | char *cwd, *realcwd, *q, *args; |
53 | 53 | ||
54 | opt = getopt32(argv, "^c:s:tNW" "\0" "s--N:N--s", &opt_command, &opt_shell); | 54 | opt = getopt32(argv, "^c:s:tNW" "\0" "s--N:N--s", &opt_command, &opt_shell); |
55 | argv += optind; | 55 | argv += optind; |
@@ -70,19 +70,17 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) | |||
70 | } | 70 | } |
71 | #endif | 71 | #endif |
72 | 72 | ||
73 | /* ShellExecuteEx() needs backslash as separator in UNC paths. */ | ||
74 | if (opt_shell) { | 73 | if (opt_shell) { |
75 | bb_path = file_is_win32_exe(opt_shell); | 74 | bb_path = file_is_win32_exe(opt_shell); |
76 | if (!bb_path) | 75 | if (!bb_path) |
77 | bb_error_msg_and_die("%s: Not found", opt_shell); | 76 | bb_error_msg_and_die("%s: Not found", opt_shell); |
78 | args = NULL; | 77 | args = NULL; |
79 | } else { | 78 | } else { |
80 | bb_path = xstrdup(bb_busybox_exec_path); | 79 | bb_path = bb_busybox_exec_path; |
81 | args = xstrdup("--busybox ash"); | 80 | args = xstrdup("--busybox ash"); |
82 | if (!test_mode) | 81 | if (!test_mode) |
83 | args = xappendword(args, "-t \"BusyBox ash (Admin)\""); | 82 | args = xappendword(args, "-t \"BusyBox ash (Admin)\""); |
84 | } | 83 | } |
85 | slash_to_bs(bb_path); | ||
86 | 84 | ||
87 | memset(&info, 0, sizeof(SHELLEXECUTEINFO)); | 85 | memset(&info, 0, sizeof(SHELLEXECUTEINFO)); |
88 | info.cbSize = sizeof(SHELLEXECUTEINFO); | 86 | info.cbSize = sizeof(SHELLEXECUTEINFO); |
@@ -137,12 +135,7 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) | |||
137 | /* info.lpDirectory = NULL; */ | 135 | /* info.lpDirectory = NULL; */ |
138 | info.nShow = SW_SHOWNORMAL; | 136 | info.nShow = SW_SHOWNORMAL; |
139 | 137 | ||
140 | if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) { | 138 | if (!mingw_shell_execute(&info)) { |
141 | ret = -1; | ||
142 | goto end; | ||
143 | } | ||
144 | |||
145 | if (!ShellExecuteExA(&info)) { | ||
146 | ret = 1; | 139 | ret = 1; |
147 | goto end; | 140 | goto end; |
148 | } | 141 | } |
@@ -159,7 +152,8 @@ int suw32_main(int argc UNUSED_PARAM, char **argv) | |||
159 | } | 152 | } |
160 | end: | 153 | end: |
161 | if (ENABLE_FEATURE_CLEAN_UP) { | 154 | if (ENABLE_FEATURE_CLEAN_UP) { |
162 | free(bb_path); | 155 | if (bb_path != bb_busybox_exec_path) |
156 | free((void *)bb_path); | ||
163 | free(cwd); | 157 | free(cwd); |
164 | free(realcwd); | 158 | free(realcwd); |
165 | free(args); | 159 | 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) | |||
2538 | free(exepath); | 2538 | free(exepath); |
2539 | return relpath; | 2539 | return relpath; |
2540 | } | 2540 | } |
2541 | |||
2542 | int mingw_shell_execute(SHELLEXECUTEINFO *info) | ||
2543 | { | ||
2544 | DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *); | ||
2545 | char *lpath; | ||
2546 | int ret; | ||
2547 | |||
2548 | if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA)) { | ||
2549 | errno = ENOSYS; | ||
2550 | return FALSE; | ||
2551 | } | ||
2552 | |||
2553 | // ShellExecuteEx() needs backslash as separator in UNC paths. | ||
2554 | lpath = xstrdup(info->lpFile); | ||
2555 | slash_to_bs(lpath); | ||
2556 | info->lpFile = lpath; | ||
2557 | |||
2558 | ret = ShellExecuteExA(info); | ||
2559 | |||
2560 | free(lpath); | ||
2561 | return ret; | ||
2562 | } | ||
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) | |||
501 | exit((int)code); | 501 | exit((int)code); |
502 | } | 502 | } |
503 | 503 | ||
504 | static intptr_t | ||
505 | shell_execute(const char *path, char *const *argv) | ||
506 | { | ||
507 | SHELLEXECUTEINFO info; | ||
508 | char *args; | ||
509 | |||
510 | memset(&info, 0, sizeof(SHELLEXECUTEINFO)); | ||
511 | info.cbSize = sizeof(SHELLEXECUTEINFO); | ||
512 | info.fMask = SEE_MASK_NOCLOSEPROCESS; | ||
513 | /* info.hwnd = NULL; */ | ||
514 | info.lpVerb = "runas"; | ||
515 | info.lpFile = path; | ||
516 | |||
517 | args = NULL; | ||
518 | if (*argv++) { | ||
519 | while (*argv) { | ||
520 | char *q = quote_arg(*argv++); | ||
521 | args = xappendword(args, q); | ||
522 | free(q); | ||
523 | } | ||
524 | } | ||
525 | |||
526 | info.lpParameters = args; | ||
527 | /* info.lpDirectory = NULL; */ | ||
528 | info.nShow = SW_SHOWNORMAL; | ||
529 | |||
530 | mingw_shell_execute(&info); | ||
531 | |||
532 | free(args); | ||
533 | return info.hProcess ? (intptr_t)info.hProcess : -1; | ||
534 | } | ||
535 | |||
504 | int | 536 | int |
505 | mingw_execvp(const char *cmd, char *const *argv) | 537 | mingw_execvp(const char *cmd, char *const *argv) |
506 | { | 538 | { |
@@ -514,6 +546,16 @@ int | |||
514 | mingw_execve(const char *cmd, char *const *argv, char *const *envp) | 546 | mingw_execve(const char *cmd, char *const *argv, char *const *envp) |
515 | { | 547 | { |
516 | intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); | 548 | intptr_t ret = mingw_spawn_interpreter(P_NOWAIT, cmd, argv, envp, 0); |
549 | |||
550 | if (ret == -1 && GetLastError() == ERROR_ELEVATION_REQUIRED) { | ||
551 | // Command exists but failed because it wants elevated privileges. | ||
552 | // Try again using ShellExecuteEx(). | ||
553 | SetLastError(0); | ||
554 | ret = shell_execute(cmd, argv); | ||
555 | if (GetLastError()) | ||
556 | exit(1); | ||
557 | } | ||
558 | |||
517 | if (ret != -1) | 559 | if (ret != -1) |
518 | wait_for_child((HANDLE)ret, cmd); | 560 | wait_for_child((HANDLE)ret, cmd); |
519 | return ret; | 561 | return ret; |