aboutsummaryrefslogtreecommitdiff
path: root/util-linux
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2023-03-13 10:25:56 +0000
committerRon Yorston <rmy@pobox.com>2023-03-13 10:25:56 +0000
commit385decd6bf62c116565ece1e0992ff7a79d48474 (patch)
tree30065938d54231dcadf54cc3e22206f4985a7180 /util-linux
parent6eeb5240974bb304830319e9fa5afbc4d6194fc0 (diff)
downloadbusybox-w32-385decd6bf62c116565ece1e0992ff7a79d48474.tar.gz
busybox-w32-385decd6bf62c116565ece1e0992ff7a79d48474.tar.bz2
busybox-w32-385decd6bf62c116565ece1e0992ff7a79d48474.zip
runuser: new applet
Add a cut down, Windows-specific implementation of `runuser` from util-linux. This allows elevated privileges to be dropped when running in an SSH session. It also works when using `su` or starting busybox-w32 'as administrator'. There are complications: - The method used to drop privileges leaves the access token in the TokenIsElevated state. Detecting this is likely to be fragile. - The unprivileged shell is started by CreateProcessAsUserA(). In older versions of Windows this has to be loaded dynamically. Adds about 900 bytes. (GitHub issue #240)
Diffstat (limited to 'util-linux')
-rw-r--r--util-linux/runuser.c117
1 files changed, 117 insertions, 0 deletions
diff --git a/util-linux/runuser.c b/util-linux/runuser.c
new file mode 100644
index 000000000..f6abd9a74
--- /dev/null
+++ b/util-linux/runuser.c
@@ -0,0 +1,117 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * runuser - run a shell without elevated privileges.
4 * This is a much restricted, Windows-specific reimplementation of
5 * runuser from util-linux.
6 *
7 * Copyright (c) 2023 Ronald M Yorston
8 *
9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 */
11//config:config RUNUSER
12//config: bool "runuser"
13//config: default y
14//config: depends on PLATFORM_MINGW32 && SH_IS_ASH
15//config: help
16//config: Run a shell without elevated privileges
17
18//applet:IF_RUNUSER(APPLET(runuser, BB_DIR_USR_BIN, BB_SUID_DROP))
19
20//kbuild:lib-$(CONFIG_RUNUSER) += runuser.o
21
22//usage:#define runuser_trivial_usage
23//usage: "USER [ARG...]"
24//usage:#define runuser_full_usage "\n\n"
25//usage: "Run a shell without elevated privileges. The user name\n"
26//usage: "must be that of the user who was granted those privileges.\n"
27//usage: "Any arguments are passed to the shell.\n"
28
29#include "libbb.h"
30#include <winsafer.h>
31#include <lazyload.h>
32
33int runuser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
34int runuser_main(int argc UNUSED_PARAM, char **argv)
35{
36 const char *user;
37 SAFER_LEVEL_HANDLE safer;
38 HANDLE token;
39 STARTUPINFO si;
40 PROCESS_INFORMATION pi;
41 TOKEN_MANDATORY_LABEL TIL;
42 // Medium integrity level S-1-16-8192
43 unsigned char medium[12] = {
44 0x01, 0x01, 0x00, 0x00,
45 0x00, 0x00, 0x00, 0x10,
46 0x00, 0x20, 0x00, 0x00
47 };
48 char *cmd, **a;
49 DWORD code;
50 // This shouldn't be necessary but without it the binary complains
51 // it can't find CreateProcessAsUserA on older versions of Windows.
52 DECLARE_PROC_ADDR(BOOL, CreateProcessAsUserA, HANDLE, LPCSTR, LPSTR,
53 LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD,
54 LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
55
56 if (!INIT_PROC_ADDR(advapi32.dll, CreateProcessAsUserA))
57 bb_simple_error_msg_and_die("not supported");
58
59 if (getuid() != 0)
60 bb_simple_error_msg_and_die("may not be used by non-root users");
61
62 if (argc < 2)
63 bb_show_usage();
64
65 user = get_user_name();
66 if (user == NULL || strcmp(argv[1], user) != 0)
67 bb_simple_error_msg_and_die("invalid user");
68
69 /*
70 * Run a shell using a token with reduced privilege. Hints from:
71 *
72 * https://stackoverflow.com/questions/17765568/
73 */
74 if (SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER,
75 SAFER_LEVEL_OPEN, &safer, NULL) &&
76 SaferComputeTokenFromLevel(safer, NULL, &token, 0, NULL)) {
77
78 // Set medium integrity
79 TIL.Label.Sid = (PSID)medium;
80 TIL.Label.Attributes = SE_GROUP_INTEGRITY;
81 if (SetTokenInformation(token, TokenIntegrityLevel, &TIL,
82 sizeof(TOKEN_MANDATORY_LABEL))) {
83
84 ZeroMemory(&si, sizeof(STARTUPINFO));
85 si.cb = sizeof(STARTUPINFO);
86 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
87 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
88 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
89 si.dwFlags = STARTF_USESTDHANDLES;
90
91 // Build the command line
92 cmd = xstrdup("sh");
93 for (a = argv + 2; *a; ++a) {
94 char *q = quote_arg(*a);
95 char *newcmd = xasprintf("%s %s", cmd, q);
96 if (q != *a)
97 free(q);
98 free(cmd);
99 cmd = newcmd;
100 }
101
102 if (!CreateProcessAsUserA(token, bb_busybox_exec_path,
103 cmd, NULL, NULL, TRUE, 0, NULL,
104 NULL, &si, &pi)) {
105 errno = err_win_to_posix();
106 bb_perror_msg_and_die("can't execute 'sh'");
107 }
108
109 WaitForSingleObject(pi.hProcess, INFINITE);
110 if (GetExitCodeProcess(pi.hProcess, &code)) {
111 return (int)code;
112 }
113 }
114 }
115
116 return EXIT_FAILURE;
117}