diff options
Diffstat (limited to 'loginutils/suw32.c')
-rw-r--r-- | loginutils/suw32.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/loginutils/suw32.c b/loginutils/suw32.c new file mode 100644 index 000000000..77c038582 --- /dev/null +++ b/loginutils/suw32.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | ||
3 | * Mini su implementation for busybox-w32 | ||
4 | * | ||
5 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
6 | */ | ||
7 | //config:config SUW32 | ||
8 | //config: bool "su for Microsoft Windows" | ||
9 | //config: default y | ||
10 | //config: depends on PLATFORM_MINGW32 && ASH | ||
11 | //config: help | ||
12 | //config: su runs a shell with elevated privileges. | ||
13 | |||
14 | //applet:IF_SUW32(APPLET_ODDNAME(su, suw32, BB_DIR_BIN, BB_SUID_DROP, suw32)) | ||
15 | |||
16 | //kbuild:lib-$(CONFIG_SUW32) += suw32.o | ||
17 | |||
18 | //usage:#define suw32_trivial_usage | ||
19 | //usage: "[-tW] [-N|-s SHELL] [root]\n" | ||
20 | //usage: "or: su [-tW] [-N|-s SHELL] -c CMD_STRING [[--] root [ARG0 [ARG...]]]\n" | ||
21 | //usage: "or: su [-tW] [-N|-s SHELL] [--] root [arbitrary sh arguments]" | ||
22 | //usage:#define suw32_full_usage "\n\n" | ||
23 | //usage: "Run shell with elevated privileges\n" | ||
24 | //usage: "\n -c CMD Command to pass to 'sh -c'" | ||
25 | //usage: "\n -s SHELL Use specified shell" | ||
26 | //usage: "\n -N Don't close console when shell exits" | ||
27 | //usage: "\n -t Test mode, no elevation, implies -W" | ||
28 | //usage: "\n -W Wait for shell exit code" | ||
29 | |||
30 | #include "libbb.h" | ||
31 | #include "lazyload.h" | ||
32 | |||
33 | enum { | ||
34 | OPT_c = (1 << 0), | ||
35 | OPT_s = (1 << 1), | ||
36 | OPT_t = (1 << 2), | ||
37 | OPT_N = (1 << 3), | ||
38 | OPT_W = (1 << 4) | ||
39 | }; | ||
40 | |||
41 | #define test_mode (opt & OPT_t) | ||
42 | |||
43 | int suw32_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
44 | int suw32_main(int argc UNUSED_PARAM, char **argv) | ||
45 | { | ||
46 | int ret = 0; | ||
47 | unsigned opt; | ||
48 | char *opt_command = NULL; | ||
49 | char *opt_shell = NULL; | ||
50 | SHELLEXECUTEINFO info; | ||
51 | const char *bb_path; | ||
52 | char *cwd, *realcwd, *q, *args; | ||
53 | |||
54 | opt = getopt32(argv, "^c:s:tNW" "\0" "s--N:N--s", &opt_command, &opt_shell); | ||
55 | argv += optind; | ||
56 | if (argv[0]) { | ||
57 | if (!test_mode && strcmp(argv[0], "root") != 0) { | ||
58 | bb_error_msg_and_die("unknown user '%s'", argv[0]); | ||
59 | } | ||
60 | ++argv; | ||
61 | } | ||
62 | |||
63 | #if ENABLE_DROP || ENABLE_CDROP || ENABLE_PDROP | ||
64 | // If privilege has been dropped (ELEVATED_PRIVILEGE but not | ||
65 | // ADMIN_ENABLED) ShellExecuteEx() thinks we already have elevated | ||
66 | // privilege and doesn't raise privilege. In that case, give up. | ||
67 | if (!test_mode && elevation_state() == ELEVATED_PRIVILEGE) { | ||
68 | xfunc_error_retval = 2; | ||
69 | bb_error_msg_and_die("unable to restore privilege"); | ||
70 | } | ||
71 | #endif | ||
72 | |||
73 | if (opt_shell) { | ||
74 | bb_path = file_is_win32_exe(opt_shell); | ||
75 | if (!bb_path) | ||
76 | bb_error_msg_and_die("%s: Not found", opt_shell); | ||
77 | args = NULL; | ||
78 | } else { | ||
79 | bb_path = bb_busybox_exec_path; | ||
80 | args = xstrdup("--busybox ash"); | ||
81 | if (!test_mode) | ||
82 | args = xappendword(args, "-t \"BusyBox ash (Admin)\""); | ||
83 | } | ||
84 | |||
85 | memset(&info, 0, sizeof(SHELLEXECUTEINFO)); | ||
86 | info.cbSize = sizeof(SHELLEXECUTEINFO); | ||
87 | /* info.fMask = SEE_MASK_DEFAULT; */ | ||
88 | if (opt & (OPT_t|OPT_W)) | ||
89 | info.fMask |= SEE_MASK_NOCLOSEPROCESS; | ||
90 | if (test_mode) | ||
91 | info.fMask |= SEE_MASK_NO_CONSOLE; | ||
92 | /* info.hwnd = NULL; */ | ||
93 | info.lpVerb = !test_mode ? "runas" : "open"; | ||
94 | info.lpFile = bb_path; | ||
95 | /* | ||
96 | * It seems that when ShellExecuteEx() runs binaries residing in | ||
97 | * certain 'system' directories it sets the current directory of | ||
98 | * the process to %SYSTEMROOT%\System32. Override this by passing | ||
99 | * the directory we want to the shell. | ||
100 | * | ||
101 | * Canonicalise the directory now: if it's in a drive mapped to | ||
102 | * a network share it may not be available once we have elevated | ||
103 | * privileges. | ||
104 | */ | ||
105 | if (opt_shell == NULL) { | ||
106 | cwd = getcwd(NULL, 0); | ||
107 | realcwd = cwd ? xmalloc_realpath(cwd) : NULL; | ||
108 | if (realcwd || cwd) { | ||
109 | args = xappendword(args, "-d"); | ||
110 | q = quote_arg(realcwd ?: cwd); | ||
111 | args = xappendword(args, q); | ||
112 | free(q); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | if (opt & OPT_N) | ||
117 | args = xappendword(args, "-N"); | ||
118 | |||
119 | if (opt_command) { | ||
120 | args = xappendword(args, | ||
121 | (opt_shell && strcasecmp(bb_basename(bb_path), "cmd.exe") == 0) ? | ||
122 | "/c" : "-c"); | ||
123 | q = quote_arg(opt_command); | ||
124 | args = xappendword(args, q); | ||
125 | free(q); | ||
126 | } | ||
127 | |||
128 | while (*argv) { | ||
129 | q = quote_arg(*argv++); | ||
130 | args = xappendword(args, q); | ||
131 | free(q); | ||
132 | } | ||
133 | |||
134 | info.lpParameters = args; | ||
135 | /* info.lpDirectory = NULL; */ | ||
136 | info.nShow = SW_SHOWNORMAL; | ||
137 | |||
138 | if (!mingw_shell_execute(&info)) { | ||
139 | ret = 1; | ||
140 | goto end; | ||
141 | } | ||
142 | |||
143 | if (opt & (OPT_t|OPT_W)) { | ||
144 | DWORD r; | ||
145 | |||
146 | WaitForSingleObject(info.hProcess, INFINITE); | ||
147 | if (!GetExitCodeProcess(info.hProcess, &r)) | ||
148 | ret = 1; | ||
149 | else | ||
150 | ret = exit_code_to_posix(r); | ||
151 | CloseHandle(info.hProcess); | ||
152 | } | ||
153 | end: | ||
154 | if (ENABLE_FEATURE_CLEAN_UP) { | ||
155 | if (bb_path != bb_busybox_exec_path) | ||
156 | free((void *)bb_path); | ||
157 | free(cwd); | ||
158 | free(realcwd); | ||
159 | free(args); | ||
160 | } | ||
161 | |||
162 | return ret; | ||
163 | } | ||