From b0e7a401d51402c052563f55331c8b9001a1215c Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sun, 19 Mar 2023 10:25:23 +0000 Subject: runuser,drop: drop runuser, tweak drop Remove the runuser applet, leaving only drop. Move drop from util-linux to miscutils. A command of the form 'drop -c command' causes the BusyBox shell to be used, just like 'drop' without any arguments. A simple OpenSSH configuration with 'drop.exe' as DefaultShell and no DefaultShellArguments now works both for interactive login and to run a command. This is useful for older versions of OpenSSH which don't support DefaultShellArguments. Saves 208-232 bytes. --- configs/mingw32_defconfig | 4 +- configs/mingw64_defconfig | 4 +- miscutils/drop.c | 130 ++++++++++++++++++++++++++++++++++++ util-linux/runuser.c | 164 ---------------------------------------------- win32/mingw.c | 2 +- 5 files changed, 135 insertions(+), 169 deletions(-) create mode 100644 miscutils/drop.c delete mode 100644 util-linux/runuser.c diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index 611fd327a..31f72a450 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Busybox version: 1.37.0.git -# Sun Mar 12 09:41:00 2023 +# Sun Mar 19 09:51:34 2023 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -707,7 +707,7 @@ CONFIG_XXD=y # CONFIG_RENICE is not set CONFIG_REV=y # CONFIG_RTCWAKE is not set -CONFIG_RUNUSER=y +CONFIG_DROP=y # CONFIG_SCRIPT is not set # CONFIG_SCRIPTREPLAY is not set # CONFIG_SETARCH is not set diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index 6db0accbf..b5b02ba63 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit # Busybox version: 1.37.0.git -# Sun Mar 12 09:41:00 2023 +# Sun Mar 19 09:51:34 2023 # CONFIG_HAVE_DOT_CONFIG=y # CONFIG_PLATFORM_POSIX is not set @@ -707,7 +707,7 @@ CONFIG_XXD=y # CONFIG_RENICE is not set CONFIG_REV=y # CONFIG_RTCWAKE is not set -CONFIG_RUNUSER=y +CONFIG_DROP=y # CONFIG_SCRIPT is not set # CONFIG_SCRIPTREPLAY is not set # CONFIG_SETARCH is not set diff --git a/miscutils/drop.c b/miscutils/drop.c new file mode 100644 index 000000000..3e71f6180 --- /dev/null +++ b/miscutils/drop.c @@ -0,0 +1,130 @@ +/* vi: set sw=4 ts=4: */ +/* + * drop - run a command without elevated privileges. + * + * Copyright (c) 2023 Ronald M Yorston + * + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config DROP +//config: bool "drop" +//config: default y +//config: depends on PLATFORM_MINGW32 && SH_IS_ASH +//config: help +//config: Run a command without elevated privileges + +//applet:IF_DROP(APPLET(drop, BB_DIR_USR_BIN, BB_SUID_DROP)) + +//kbuild:lib-$(CONFIG_DROP) += drop.o + +//usage:#define drop_trivial_usage +//usage: "[COMMAND | -c [ARG...]]" +//usage:#define drop_full_usage "\n\n" +//usage: "Drop elevated privileges and run a command. If no COMMAND\n" +//usage: "is provided run the BusyBox shell.\n" + +#include "libbb.h" +#include +#include + +int drop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int drop_main(int argc, char **argv) +{ + const char *exe; + SAFER_LEVEL_HANDLE safer; + HANDLE token; + STARTUPINFO si; + PROCESS_INFORMATION pi; + TOKEN_MANDATORY_LABEL TIL; + // Medium integrity level S-1-16-8192 + unsigned char medium[12] = { + 0x01, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, + 0x00, 0x20, 0x00, 0x00 + }; + char *cmd, *q, *newcmd, **a; + DWORD code; + // This shouldn't be necessary but without it the binary complains + // it can't find CreateProcessAsUserA on older versions of Windows. + DECLARE_PROC_ADDR(BOOL, CreateProcessAsUserA, HANDLE, LPCSTR, LPSTR, + LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, + LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION); + + if (!INIT_PROC_ADDR(advapi32.dll, CreateProcessAsUserA)) + bb_simple_error_msg_and_die("not supported"); + + /* + * Run a shell using a token with reduced privilege. Hints from: + * + * https://stackoverflow.com/questions/17765568/ + */ + if (SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, + SAFER_LEVEL_OPEN, &safer, NULL) && + SaferComputeTokenFromLevel(safer, NULL, &token, 0, NULL)) { + + // Set medium integrity + TIL.Label.Sid = (PSID)medium; + TIL.Label.Attributes = SE_GROUP_INTEGRITY; + if (SetTokenInformation(token, TokenIntegrityLevel, &TIL, + sizeof(TOKEN_MANDATORY_LABEL))) { + int skip = 1; + + if (argc == 1 || strcmp(argv[1], "-c") == 0) { + exe = bb_busybox_exec_path; + cmd = xstrdup("sh"); + skip = 0; + } else { + char *file; + +#if ENABLE_FEATURE_PREFER_APPLETS + if (!has_path(argv[1]) && find_applet_by_name(argv[1]) >= 0) { + file = xstrdup(bb_busybox_exec_path); + } else +#endif + if (has_path(argv[1])) { + file = file_is_win32_exe(argv[1]); + } else { + file = find_first_executable(argv[1]); + } + + if (file == NULL) { + xfunc_error_retval = 127; + bb_error_msg_and_die("can't find '%s'", argv[1]); + } + + slash_to_bs(file); + exe = file; + cmd = quote_arg(argv[1]); + } + + // Build the command line + for (a = argv + 1 + skip; *a; ++a) { + q = quote_arg(*a); + newcmd = xasprintf("%s %s", cmd, q); + free(q); + free(cmd); + cmd = newcmd; + } + + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + si.hStdError = GetStdHandle(STD_ERROR_HANDLE); + si.dwFlags = STARTF_USESTDHANDLES; + + if (!CreateProcessAsUserA(token, exe, cmd, NULL, NULL, TRUE, + 0, NULL, NULL, &si, &pi)) { + xfunc_error_retval = 126; + bb_error_msg_and_die("can't execute '%s'", exe); + } + + WaitForSingleObject(pi.hProcess, INFINITE); + if (GetExitCodeProcess(pi.hProcess, &code)) { + return (int)code; + } + } + } + + return EXIT_FAILURE; +} diff --git a/util-linux/runuser.c b/util-linux/runuser.c deleted file mode 100644 index 993a4ed68..000000000 --- a/util-linux/runuser.c +++ /dev/null @@ -1,164 +0,0 @@ -/* vi: set sw=4 ts=4: */ -/* - * runuser - run a shell without elevated privileges. - * This is a much restricted, Windows-specific reimplementation of - * runuser from util-linux. - * - * Copyright (c) 2023 Ronald M Yorston - * - * Licensed under GPLv2 or later, see file LICENSE in this source tree. - */ -//config:config RUNUSER -//config: bool "runuser" -//config: default y -//config: depends on PLATFORM_MINGW32 && SH_IS_ASH -//config: help -//config: Run a shell without elevated privileges -//config: -//config:config DROP -//config: bool "drop" -//config: default y -//config: depends on PLATFORM_MINGW32 && SH_IS_ASH -//config: help -//config: Run a command without elevated privileges - -//applet:IF_RUNUSER(APPLET(runuser, BB_DIR_USR_BIN, BB_SUID_DROP)) -//applet:IF_DROP(APPLET_ODDNAME(drop, runuser, BB_DIR_USR_BIN, BB_SUID_DROP, drop)) - -//kbuild:lib-$(CONFIG_RUNUSER) += runuser.o -//kbuild:lib-$(CONFIG_DROP) += runuser.o - -//usage:#define runuser_trivial_usage -//usage: "USER [ARG...]" -//usage:#define runuser_full_usage "\n\n" -//usage: "Run a shell without elevated privileges. The user name\n" -//usage: "must be that of the user who was granted those privileges.\n" -//usage: "Any arguments are passed to the shell.\n" - -//usage:#define drop_trivial_usage -//usage: "[COMMAND [ARG...]]" -//usage:#define drop_full_usage "\n\n" -//usage: "Run a command without elevated privileges. Run the BusyBox\n" -//usage: "shell if no COMMAND is provided. Any arguments are passed\n" -//usage: "to the command.\n" - -#include "libbb.h" -#include -#include - -int runuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; -int runuser_main(int argc, char **argv) -{ -#if ENABLE_RUNUSER && ENABLE_DROP - int is_runuser = strcmp(applet_name, "runuser") == 0; -#else - const int is_runuser = ENABLE_RUNUSER; -#endif - const char *user, *exe; - SAFER_LEVEL_HANDLE safer; - HANDLE token; - STARTUPINFO si; - PROCESS_INFORMATION pi; - TOKEN_MANDATORY_LABEL TIL; - // Medium integrity level S-1-16-8192 - unsigned char medium[12] = { - 0x01, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10, - 0x00, 0x20, 0x00, 0x00 - }; - char *cmd, *q, *newcmd, **a; - DWORD code; - // This shouldn't be necessary but without it the binary complains - // it can't find CreateProcessAsUserA on older versions of Windows. - DECLARE_PROC_ADDR(BOOL, CreateProcessAsUserA, HANDLE, LPCSTR, LPSTR, - LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, - LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION); - - if (!INIT_PROC_ADDR(advapi32.dll, CreateProcessAsUserA)) - bb_simple_error_msg_and_die("not supported"); - - if (is_runuser) { - if (getuid() != 0) - bb_simple_error_msg_and_die("may not be used by non-root users"); - - if (argc < 2) - bb_show_usage(); - - user = get_user_name(); - if (user == NULL || strcmp(argv[1], user) != 0) - bb_simple_error_msg_and_die("invalid user"); - } - - /* - * Run a shell using a token with reduced privilege. Hints from: - * - * https://stackoverflow.com/questions/17765568/ - */ - if (SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, - SAFER_LEVEL_OPEN, &safer, NULL) && - SaferComputeTokenFromLevel(safer, NULL, &token, 0, NULL)) { - - // Set medium integrity - TIL.Label.Sid = (PSID)medium; - TIL.Label.Attributes = SE_GROUP_INTEGRITY; - if (SetTokenInformation(token, TokenIntegrityLevel, &TIL, - sizeof(TOKEN_MANDATORY_LABEL))) { - - if (is_runuser || argc == 1) { - exe = bb_busybox_exec_path; - cmd = xstrdup("sh"); - } else { - char *file; - -#if ENABLE_FEATURE_PREFER_APPLETS - if (!has_path(argv[1]) && find_applet_by_name(argv[1]) >= 0) { - file = xstrdup(bb_busybox_exec_path); - } else -#endif - if (has_path(argv[1])) { - file = file_is_win32_exe(argv[1]); - } else { - file = find_first_executable(argv[1]); - } - - if (file == NULL) { - xfunc_error_retval = 127; - bb_error_msg_and_die("can't find '%s'", argv[1]); - } - - slash_to_bs(file); - exe = file; - cmd = quote_arg(argv[1]); - } - - // Build the command line - for (a = argv + 1 + (argc != 1); *a; ++a) { - q = quote_arg(*a); - newcmd = xasprintf("%s %s", cmd, q); - free(q); - free(cmd); - cmd = newcmd; - } - - ZeroMemory(&si, sizeof(STARTUPINFO)); - si.cb = sizeof(STARTUPINFO); - si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); - si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - si.dwFlags = STARTF_USESTDHANDLES; - - if (!CreateProcessAsUserA(token, exe, cmd, NULL, NULL, TRUE, - 0, NULL, NULL, &si, &pi)) { - xfunc_error_retval = 126; - bb_error_msg_and_die("can't execute '%s'", exe); - } - - WaitForSingleObject(pi.hProcess, INFINITE); - if (GetExitCodeProcess(pi.hProcess, &code)) { - return (int)code; - } - } - } - - return EXIT_FAILURE; -} diff --git a/win32/mingw.c b/win32/mingw.c index fd670ebf6..1fdb8cad9 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -1136,7 +1136,7 @@ char *get_user_name(void) return user_name; } -#if ENABLE_RUNUSER || ENABLE_DROP +#if ENABLE_DROP /* * When runuser drops privileges TokenIsElevated still returns TRUE. * Use other means to determine if we're actually unprivileged. -- cgit v1.2.3-55-g6feb