xref: /reactos/base/applications/runas/runas.c (revision 1de09c47)
1 /*
2  * PROJECT:     ReactOS runas utility
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/applications/runas/runas.c
5  * COPYRIGHT:   Copyright 2022 Eric Kohl <eric.kohl@reactos.org>
6  */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <limits.h>
11 #include <stdarg.h>
12 
13 #define WIN32_NO_STATUS
14 #include <windef.h>
15 #include <winbase.h>
16 #include <winnls.h>
17 #include <wincon.h>
18 #include <winsvc.h>
19 #include <conutils.h>
20 
21 #include "resource.h"
22 
23 #define NDEBUG
24 #include <debug.h>
25 
26 #define MAX_PASSWORD_LENGTH 64
27 
28 static
29 VOID
30 Usage(VOID)
31 {
32     int i;
33     for (i = IDS_USAGE01; i <= IDS_USAGE_MAX; i++)
34         ConResPuts(StdOut, i);
35 }
36 
37 
38 static
39 VOID
40 ConInString(
41     _In_ PWSTR pInput,
42     _In_ DWORD dwLength)
43 {
44     DWORD dwOldMode;
45     DWORD dwRead = 0;
46     HANDLE hFile;
47     PWSTR p;
48     PCHAR pBuf;
49 
50     pBuf = (PCHAR)HeapAlloc(GetProcessHeap(), 0, dwLength - 1);
51     ZeroMemory(pInput, dwLength * sizeof(WCHAR));
52     hFile = GetStdHandle(STD_INPUT_HANDLE);
53     GetConsoleMode(hFile, &dwOldMode);
54 
55     SetConsoleMode(hFile, ENABLE_LINE_INPUT /*| ENABLE_ECHO_INPUT*/);
56 
57     ReadFile(hFile, (PVOID)pBuf, dwLength - 1, &dwRead, NULL);
58 
59     MultiByteToWideChar(GetConsoleCP(), 0, pBuf, dwRead, pInput, dwLength - 1);
60     HeapFree(GetProcessHeap(), 0, pBuf);
61 
62     for (p = pInput; *p; p++)
63     {
64         if (*p == L'\x0d')
65         {
66             *p = UNICODE_NULL;
67             break;
68         }
69     }
70 
71     SetConsoleMode(hFile, dwOldMode);
72 }
73 
74 
75 int
76 wmain(
77     int argc,
78     LPCWSTR argv[])
79 {
80     LPCWSTR pszArg;
81     int i, result = 0;
82     BOOL bProfile = FALSE, bNoProfile = FALSE;
83     BOOL bEnv = FALSE, bNetOnly = FALSE;
84     PWSTR pszUserName = NULL;
85     PWSTR pszDomain = NULL;
86     PWSTR pszCommandLine = NULL;
87     PWSTR pszPassword = NULL;
88     PWSTR pszCurrentDirectory = NULL;
89     PWSTR pszEnvironment = NULL;
90     PWSTR ptr;
91     STARTUPINFOW StartupInfo;
92     PROCESS_INFORMATION ProcessInfo;
93     DWORD dwLogonFlags = LOGON_WITH_PROFILE;
94     DWORD dwCreateFlags = 0;
95     BOOL rc;
96 
97     /* Initialize the Console Standard Streams */
98     ConInitStdStreams();
99 
100     if (argc == 1)
101     {
102         Usage();
103         return 0;
104     }
105 
106     ZeroMemory(&StartupInfo, sizeof(StartupInfo));
107     ZeroMemory(&ProcessInfo, sizeof(ProcessInfo));
108 
109     for (i = 1; i < argc; i++)
110     {
111         pszArg = argv[i];
112         if (*pszArg == L'-' || *pszArg == L'/')
113         {
114             pszArg++;
115             if (wcscmp(pszArg, L"?") == 0)
116             {
117                 Usage();
118                 result = 0;
119                 goto done;
120             }
121             else if (_wcsicmp(pszArg, L"profile") == 0)
122             {
123                 bProfile = TRUE;
124             }
125             else if (_wcsicmp(pszArg, L"netonly") == 0)
126             {
127                 bNetOnly = TRUE;
128             }
129             else if (_wcsicmp(pszArg, L"noprofile") == 0)
130             {
131                 bNoProfile = TRUE;
132             }
133             else if (_wcsicmp(pszArg, L"env") == 0)
134             {
135                 bEnv = TRUE;
136             }
137             else if (_wcsnicmp(pszArg, L"user:", 5) == 0)
138             {
139                 pszArg += 5;
140                 ptr = wcschr(pszArg, L'@');
141                 if (ptr != NULL)
142                 {
143                     /* User@Domain */
144                     pszUserName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ((ptr - pszArg) + 1) * sizeof(WCHAR));
145                     if (pszUserName == NULL)
146                     {
147                         ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
148                         result = -1;
149                         goto done;
150                     }
151 
152                     wcsncpy(pszUserName, pszArg, (ptr - pszArg));
153 
154                     ptr++;
155                     pszDomain = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (wcslen(ptr) + 1) * sizeof(WCHAR));
156                     if (pszDomain == NULL)
157                     {
158                         ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
159                         result = -1;
160                         goto done;
161                     }
162 
163                     wcscpy(pszDomain, ptr);
164                 }
165                 else
166                 {
167                     ptr = wcschr(pszArg, L'\\');
168                     if (ptr != NULL)
169                     {
170                         /* Domain\User */
171                         pszUserName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (wcslen(ptr + 1) + 1)* sizeof(WCHAR));
172                         if (pszUserName == NULL)
173                         {
174                             ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
175                             result = -1;
176                             goto done;
177                         }
178 
179                         wcscpy(pszUserName, (ptr + 1));
180 
181                         pszDomain = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ((ptr - pszArg) + 1) * sizeof(WCHAR));
182                         if (pszDomain == NULL)
183                         {
184                             ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
185                             result = -1;
186                             goto done;
187                         }
188 
189                         wcsncpy(pszDomain, pszArg, (ptr - pszArg));
190                     }
191                     else
192                     {
193                         /* User */
194                         pszUserName = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (wcslen(pszArg) + 1) * sizeof(WCHAR));
195                         if (pszUserName == NULL)
196                         {
197                             ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
198                             result = -1;
199                             goto done;
200                         }
201 
202                         wcscpy(pszUserName, pszArg);
203                     }
204                 }
205             }
206             else
207             {
208                 Usage();
209                 result = -1;
210                 goto done;
211             }
212         }
213         else
214         {
215             if (pszCommandLine == NULL)
216             {
217                 pszCommandLine = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (wcslen(pszArg) + 1) * sizeof(WCHAR));
218                 if (pszCommandLine == NULL)
219                 {
220                     ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
221                     result = -1;
222                     goto done;
223                 }
224 
225                 wcscpy(pszCommandLine, pszArg);
226                 break;
227             }
228         }
229     }
230 
231     /* Check for incompatible options */
232     if ((bProfile && bNoProfile) ||
233         (bProfile && bNetOnly))
234     {
235         Usage();
236         result = -1;
237         goto done;
238     }
239 
240     /* Check for existing command line and user name */
241     if (pszCommandLine == NULL || pszUserName == NULL)
242     {
243         Usage();
244         result = -1;
245         goto done;
246     }
247 
248     if (bProfile)
249         dwLogonFlags |= LOGON_WITH_PROFILE;
250 
251     if (bNoProfile)
252         dwLogonFlags &= ~LOGON_WITH_PROFILE;
253 
254     if (bNetOnly)
255     {
256         dwLogonFlags  |= LOGON_NETCREDENTIALS_ONLY;
257         dwLogonFlags  &= ~LOGON_WITH_PROFILE;
258     }
259 
260     DPRINT("User: %S\n", pszUserName);
261     DPRINT("Domain: %S\n", pszDomain);
262     DPRINT("CommandLine: %S\n", pszCommandLine);
263 
264     if (pszDomain == NULL)
265     {
266         DWORD dwLength = MAX_COMPUTERNAME_LENGTH + 1;
267         pszDomain = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(WCHAR));
268         if (pszDomain == NULL)
269         {
270             ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
271             result = -1;
272             goto done;
273         }
274 
275         GetComputerNameW(pszDomain, &dwLength);
276     }
277 
278     if (bEnv)
279     {
280         pszEnvironment = GetEnvironmentStringsW();
281         pszCurrentDirectory = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (MAX_PATH + 1) * sizeof(WCHAR));
282         if (pszCurrentDirectory == NULL)
283         {
284             ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
285             result = -1;
286             goto done;
287         }
288 
289         GetCurrentDirectory(MAX_PATH + 1, pszCurrentDirectory);
290         dwCreateFlags |= CREATE_UNICODE_ENVIRONMENT;
291     }
292 
293     pszPassword = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (MAX_PASSWORD_LENGTH + 1) * sizeof(WCHAR));
294     if (pszPassword == NULL)
295     {
296         ConResPrintf(StdOut, IDS_INTERNAL_ERROR, ERROR_OUTOFMEMORY);
297         result = -1;
298         goto done;
299     }
300 
301     /* Query the password */
302     ConResPrintf(StdOut, IDS_PASSWORD, pszDomain, pszUserName);
303     ConInString(pszPassword, MAX_PASSWORD_LENGTH + 1);
304     ConPuts(StdOut, L"\n");
305 
306     ConResPrintf(StdOut, IDS_START, pszCommandLine, pszDomain, pszUserName);
307 
308     rc = CreateProcessWithLogonW(pszUserName,
309                                  pszDomain,
310                                  pszPassword,
311                                  dwLogonFlags,
312                                  NULL,
313                                  pszCommandLine,
314                                  dwCreateFlags,
315                                  pszEnvironment,
316                                  pszCurrentDirectory,
317                                  &StartupInfo,
318                                  &ProcessInfo);
319     if (rc == FALSE)
320     {
321         ConResPrintf(StdOut, IDS_RUN_ERROR, pszCommandLine);
322         ConPrintf(StdOut, L"%lu\n", GetLastError());
323     }
324 
325 done:
326     if (ProcessInfo.hThread)
327         CloseHandle(ProcessInfo.hThread);
328 
329     if (ProcessInfo.hProcess)
330         CloseHandle(ProcessInfo.hProcess);
331 
332     if (pszPassword)
333         HeapFree(GetProcessHeap(), 0, pszPassword);
334 
335     /* NOTE: Do NOT free pszEnvironment */
336 
337     if (pszCurrentDirectory)
338         HeapFree(GetProcessHeap(), 0, pszCurrentDirectory);
339 
340     if (pszCommandLine)
341         HeapFree(GetProcessHeap(), 0, pszCommandLine);
342 
343     if (pszUserName)
344         HeapFree(GetProcessHeap(), 0, pszUserName);
345 
346     if (pszDomain)
347         HeapFree(GetProcessHeap(), 0, pszDomain);
348 
349     return result;
350 }
351