aboutsummaryrefslogtreecommitdiff
path: root/miscutils/drop.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--miscutils/drop.c220
1 files changed, 220 insertions, 0 deletions
diff --git a/miscutils/drop.c b/miscutils/drop.c
new file mode 100644
index 000000000..bef1fa52b
--- /dev/null
+++ b/miscutils/drop.c
@@ -0,0 +1,220 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * drop - run a command without elevated privileges.
4 *
5 * Copyright (c) 2023 Ronald M Yorston
6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */
9//config:config DROP
10//config: bool "drop"
11//config: default y
12//config: depends on PLATFORM_MINGW32 && SH_IS_ASH
13//config: help
14//config: Run a command without elevated privileges
15
16//config:config CDROP
17//config: bool "cdrop"
18//config: default y
19//config: depends on PLATFORM_MINGW32
20//config: help
21//config: Run a command without elevated privileges using cmd.exe
22
23//config:config PDROP
24//config: bool "pdrop"
25//config: default y
26//config: depends on PLATFORM_MINGW32
27//config: help
28//config: Run a command without elevated privileges using PowerShell
29
30//applet:IF_DROP(APPLET(drop, BB_DIR_USR_BIN, BB_SUID_DROP))
31//applet:IF_CDROP(APPLET_ODDNAME(cdrop, drop, BB_DIR_USR_BIN, BB_SUID_DROP, cdrop))
32//applet:IF_PDROP(APPLET_ODDNAME(pdrop, drop, BB_DIR_USR_BIN, BB_SUID_DROP, pdrop))
33
34//kbuild:lib-$(CONFIG_DROP) += drop.o
35//kbuild:lib-$(CONFIG_CDROP) += drop.o
36//kbuild:lib-$(CONFIG_PDROP) += drop.o
37
38//usage:#define drop_trivial_usage
39//usage: "[COMMAND [ARG...] | [-s SHELL] -c CMD_STRING [ARG...]]"
40//usage:#define drop_full_usage "\n\n"
41//usage: "Drop elevated privileges and run a command. If no command\n"
42//usage: "is provided run a shell (by default, the BusyBox shell).\n"
43
44//usage:#define cdrop_trivial_usage
45//usage: "[COMMAND [ARG...] | -c CMD_STRING [ARG...]]"
46//usage:#define cdrop_full_usage "\n\n"
47//usage: "Drop elevated privileges and run a command. If no command\n"
48//usage: "is provided run cmd.exe.\n"
49
50//usage:#define pdrop_trivial_usage
51//usage: "[COMMAND [ARG...] | -c CMD_STRING [ARG...]]"
52//usage:#define pdrop_full_usage "\n\n"
53//usage: "Drop elevated privileges and run a command. If no command\n"
54//usage: "is provided run PowerShell.\n"
55
56#include "libbb.h"
57#include <winsafer.h>
58#include <lazyload.h>
59#include "NUM_APPLETS.h"
60
61// Set an environment variable to the name of the unprivileged user,
62// but only if it was previously unset or contained "root".
63static void setenv_name(const char *key)
64{
65 const char *name = get_user_name();
66 const char *oldname = getenv(key);
67
68 if (name && (!oldname || strcmp(oldname, "root") == 0)) {
69 setenv(key, name, 1);
70 }
71}
72
73int drop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
74int drop_main(int argc UNUSED_PARAM, char **argv)
75{
76 SAFER_LEVEL_HANDLE safer;
77 HANDLE token;
78 STARTUPINFO si;
79 PROCESS_INFORMATION pi;
80 TOKEN_MANDATORY_LABEL TIL;
81 // Medium integrity level S-1-16-8192
82 unsigned char medium[12] = {
83 0x01, 0x01, 0x00, 0x00,
84 0x00, 0x00, 0x00, 0x10,
85 0x00, 0x20, 0x00, 0x00
86 };
87 DWORD code;
88 // This shouldn't be necessary but without it the binary complains
89 // it can't find CreateProcessAsUserA on older versions of Windows.
90 DECLARE_PROC_ADDR(BOOL, CreateProcessAsUserA, HANDLE, LPCSTR, LPSTR,
91 LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD,
92 LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION);
93
94 if (!INIT_PROC_ADDR(advapi32.dll, CreateProcessAsUserA))
95 bb_simple_error_msg_and_die("not supported");
96
97 /*
98 * Run a shell using a token with reduced privilege. Hints from:
99 *
100 * https://stackoverflow.com/questions/17765568/
101 */
102 if (SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER,
103 SAFER_LEVEL_OPEN, &safer, NULL) &&
104 SaferComputeTokenFromLevel(safer, NULL, &token, 0, NULL)) {
105
106 // Set medium integrity
107 TIL.Label.Sid = (PSID)medium;
108 TIL.Label.Attributes = SE_GROUP_INTEGRITY;
109 if (SetTokenInformation(token, TokenIntegrityLevel, &TIL,
110 sizeof(TOKEN_MANDATORY_LABEL))) {
111 char *opt_command = NULL;
112 char *opt_shell = NULL;
113 char **a;
114 const char *opt, *arg;
115 char *exe, *cmd, *q;
116
117 if (*applet_name == 'd')
118 getopt32(argv, "c:s:", &opt_command, &opt_shell);
119 else
120 getopt32(argv, "c:", &opt_command);
121 a = argv + optind;
122 opt = "-c";
123
124 if (*a == NULL || opt_command) {
125 switch (*applet_name) {
126#if ENABLE_PDROP
127 case 'p':
128 arg = "powershell.exe";
129 exe = find_first_executable(arg);
130 break;
131#endif
132#if ENABLE_CDROP
133 case 'c':
134 opt = "/c";
135 arg = "cmd.exe";
136 exe = find_first_executable(arg);
137 break;
138#endif
139#if ENABLE_DROP
140 case 'd':
141 if (opt_shell) {
142 arg = bb_basename(opt_shell);
143 if (strcasecmp(arg, "cmd.exe") == 0)
144 opt = "/c";
145 exe = file_is_win32_exe(opt_shell);
146 } else {
147 arg = "sh";
148 exe = xstrdup(bb_busybox_exec_path);
149 }
150 break;
151#endif
152 default:
153 // Never executed, just to silence warnings.
154 arg = argv[0];
155 exe = NULL;
156 break;
157 }
158 } else {
159 arg = *a++;
160
161#if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1
162 if (!has_path(arg) && find_applet_by_name(arg) >= 0) {
163 exe = xstrdup(bb_busybox_exec_path);
164 } else
165#endif
166 if (has_path(arg)) {
167 exe = file_is_win32_exe(arg);
168 } else {
169 exe = find_first_executable(arg);
170 }
171 }
172
173 if (exe == NULL) {
174 xfunc_error_retval = 127;
175 bb_error_msg_and_die("can't find '%s'", arg);
176 }
177
178 slash_to_bs(exe);
179 cmd = quote_arg(arg);
180 if (opt_command) {
181 cmd = xappendword(cmd, opt);
182 q = quote_arg(opt_command);
183 cmd = xappendword(cmd, q);
184 free(q);
185 }
186
187 // Build the command line
188 while (*a) {
189 q = quote_arg(*a++);
190 cmd = xappendword(cmd, q);
191 free(q);
192 }
193
194 ZeroMemory(&si, sizeof(STARTUPINFO));
195 si.cb = sizeof(STARTUPINFO);
196 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
197 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
198 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
199 si.dwFlags = STARTF_USESTDHANDLES;
200
201 setenv_name("USER");
202 setenv_name("LOGNAME");
203
204 if (!CreateProcessAsUserA(token, exe, cmd, NULL, NULL, TRUE,
205 0, NULL, NULL, &si, &pi)) {
206 xfunc_error_retval = 126;
207 bb_error_msg_and_die("can't execute '%s'", exe);
208 }
209
210 kill_child_ctrl_handler(pi.dwProcessId);
211 SetConsoleCtrlHandler(kill_child_ctrl_handler, TRUE);
212 WaitForSingleObject(pi.hProcess, INFINITE);
213 if (GetExitCodeProcess(pi.hProcess, &code)) {
214 return exit_code_to_posix(code);
215 }
216 }
217 }
218
219 return EXIT_FAILURE;
220}