11
22#include <stdio.h>
33#include <windows.h>
4+ #include <userenv.h>
45
56#ifndef SEE_MASK_NOASYNC
67#define SEE_MASK_NOASYNC 0x00000100
@@ -24,6 +25,25 @@ static void bail(int code, char *msg) {
2425 exit (code );
2526}
2627
28+ static void wbail (int code , char * msg ) {
29+ LPVOID lpvMessageBuffer ;
30+
31+ FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
32+ FORMAT_MESSAGE_FROM_SYSTEM ,
33+ NULL , GetLastError (),
34+ MAKELANGID (LANG_NEUTRAL , SUBLANG_DEFAULT ),
35+ (LPWSTR )& lpvMessageBuffer , 0 , NULL );
36+
37+ wprintf (L"API = %s.\n" , msg );
38+ wprintf (L"error code = %d.\n" , GetLastError ());
39+ wprintf (L"message = %s.\n" , (LPWSTR )lpvMessageBuffer );
40+
41+ LocalFree (lpvMessageBuffer );
42+
43+ fprintf (stderr , "%s\n" , msg );
44+ exit (code );
45+ }
46+
2747static int getMajorOSVersion () {
2848 OSVERSIONINFO ovi ;
2949
@@ -135,6 +155,95 @@ int msiexec(int argc, char** argv) {
135155 return exec (verb , "msiexec" , parameters );
136156}
137157
158+ void toWideChar (const char * s , wchar_t * * ws ) {
159+ int wchars_num = MultiByteToWideChar (CP_UTF8 , 0 , s , -1 , NULL , 0 );
160+ * ws = malloc (wchars_num * sizeof (wchar_t ));
161+ MultiByteToWideChar (CP_UTF8 , 0 , s , -1 , * ws , wchars_num );
162+ }
163+
164+ int runas (int argc , char * * argv ) {
165+ const int MAX_ARGUMENTS_LENGTH = 65536 ;
166+ char parameters [MAX_ARGUMENTS_LENGTH ];
167+ char tmp [MAX_ARGUMENTS_LENGTH ];
168+
169+ parameters [0 ] = '\0' ;
170+
171+ if (argc < 5 ) {
172+ fprintf (stderr , "Not enough arguments, expected 6, got %d\n" , argc );
173+ msibail ();
174+ }
175+
176+ const char * user = argv [2 ];
177+ const char * password = argv [3 ];
178+ const char * command = argv [4 ];
179+
180+ for (int i = 4 ; i < argc ; i ++ ) {
181+ SAFE_APPEND ("%s\"%s\" " , argv [i ]);
182+ }
183+
184+ WCHAR * wuser ;
185+ WCHAR * wpassword ;
186+ WCHAR * wcommand ;
187+ WCHAR * wparameters ;
188+ toWideChar (user , & wuser );
189+ toWideChar (password , & wpassword );
190+ toWideChar (command , & wcommand );
191+ toWideChar (parameters , & wparameters );
192+
193+ HANDLE hToken ;
194+ LPVOID lpvEnv ;
195+
196+ WCHAR szUserProfile [256 ];
197+ ZeroMemory (szUserProfile , sizeof (szUserProfile ));
198+
199+ PROCESS_INFORMATION pi ;
200+ ZeroMemory (& pi , sizeof (PROCESS_INFORMATION ));
201+
202+ STARTUPINFOW si ;
203+ ZeroMemory (& si , sizeof (STARTUPINFOW ));
204+ si .cb = sizeof (STARTUPINFOW );
205+
206+ if (!LogonUserW (wuser , L"." , wpassword , LOGON32_LOGON_INTERACTIVE , LOGON32_PROVIDER_DEFAULT , & hToken )) {
207+ wbail (127 , "LogonUser" );
208+ }
209+ fprintf (stderr , "hToken = %p\n" , hToken );
210+
211+ if (!CreateEnvironmentBlock (& lpvEnv , hToken , TRUE)) {
212+ wbail (127 , "CreateEnvironmentBlock" );
213+ }
214+
215+ DWORD dwSize = sizeof (szUserProfile ) / sizeof (WCHAR );
216+
217+ if (!GetUserProfileDirectoryW (hToken , szUserProfile , & dwSize )) {
218+ wbail (127 , "GetUserProfileDirectory" );
219+ }
220+
221+ if (!CreateProcessWithLogonW (wuser , L"." , wpassword ,
222+ LOGON_WITH_PROFILE , wcommand , wparameters ,
223+ CREATE_UNICODE_ENVIRONMENT , lpvEnv , szUserProfile ,
224+ & si , & pi )) {
225+ wbail (127 , "CreateProcessWithLogonW" );
226+ }
227+
228+ WaitForSingleObject (pi .hProcess , INFINITE );
229+
230+ DWORD code ;
231+ if (GetExitCodeProcess (pi .hProcess , & code ) == 0 ) {
232+ // Not sure when this could ever happen.
233+ wbail (127 , "failed GetExitCodeProcess call" );
234+ }
235+
236+ if (!DestroyEnvironmentBlock (lpvEnv )) {
237+ wbail (127 , "failed DestroyEnvironmentBlock call" );
238+ }
239+
240+ CloseHandle (hToken );
241+ CloseHandle (pi .hProcess );
242+ CloseHandle (pi .hThread );
243+
244+ return code ;
245+ }
246+
138247/**
139248 * Inspired by / with some code from the following MIT-licensed projects:
140249 * - https://github.com/atom/node-runas
@@ -156,6 +265,8 @@ int main(int argc, char** argv) {
156265
157266 if (strcmp ("--msiexec" , argv [1 ]) == 0 ) {
158267 return msiexec (argc , argv );
268+ } else if (strcmp ("--runas" , argv [1 ]) == 0 ) {
269+ return runas (argc , argv );
159270 } else {
160271 return elevate (argc , argv );
161272 }
0 commit comments