1 /*
2 * The PCI Library -- Win32 helper functions
3 *
4 * Copyright (c) 2023 Pali Rohár <[email protected]>
5 *
6 * Can be freely distributed and used under the terms of the GNU GPL v2+
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include <windows.h>
12
13 #include <stdio.h> /* for sprintf() */
14
15 #include "win32-helpers.h"
16
17 /* Unfortunately i586-mingw32msvc toolchain does not provide this constant. */
18 #ifndef PROCESS_QUERY_LIMITED_INFORMATION
19 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
20 #endif
21
22 #ifndef IMAGE_FILE_MACHINE_ARMNT
23 #define IMAGE_FILE_MACHINE_ARMNT 0x01c4
24 #endif
25 #ifndef IMAGE_FILE_MACHINE_IA64
26 #define IMAGE_FILE_MACHINE_IA64 0x0200
27 #endif
28 #ifndef IMAGE_FILE_MACHINE_AMD64
29 #define IMAGE_FILE_MACHINE_AMD64 0x8664
30 #endif
31 #ifndef IMAGE_FILE_MACHINE_ARM64
32 #define IMAGE_FILE_MACHINE_ARM64 0xaa64
33 #endif
34
35 #ifndef PROCESSOR_ARCHITECTURE_INTEL
36 #define PROCESSOR_ARCHITECTURE_INTEL 0
37 #endif
38 #ifndef PROCESSOR_ARCHITECTURE_ARM
39 #define PROCESSOR_ARCHITECTURE_ARM 5
40 #endif
41 #ifndef PROCESSOR_ARCHITECTURE_IA64
42 #define PROCESSOR_ARCHITECTURE_IA64 6
43 #endif
44 #ifndef PROCESSOR_ARCHITECTURE_AMD64
45 #define PROCESSOR_ARCHITECTURE_AMD64 9
46 #endif
47 #ifndef PROCESSOR_ARCHITECTURE_ARM64
48 #define PROCESSOR_ARCHITECTURE_ARM64 12
49 #endif
50 #ifndef PROCESSOR_ARCHITECTURE_UNKNOWN
51 #define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff
52 #endif
53
54 #if WINVER < 0x0400
55 #define wProcessorArchitecture dwOemId
56 #endif
57
58 /* Unfortunately some toolchains do not provide this constant. */
59 #ifndef SE_IMPERSONATE_NAME
60 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
61 #endif
62
63 /* Unfortunately some toolchains do not provide these constants. */
64 #ifndef SE_DACL_AUTO_INHERIT_REQ
65 #define SE_DACL_AUTO_INHERIT_REQ 0x0100
66 #endif
67 #ifndef SE_SACL_AUTO_INHERIT_REQ
68 #define SE_SACL_AUTO_INHERIT_REQ 0x0200
69 #endif
70 #ifndef SE_DACL_AUTO_INHERITED
71 #define SE_DACL_AUTO_INHERITED 0x0400
72 #endif
73 #ifndef SE_SACL_AUTO_INHERITED
74 #define SE_SACL_AUTO_INHERITED 0x0800
75 #endif
76
77 /* Older SDK versions do not provide NtCurrentTeb symbol for X86, header files have only function declaration. */
78 #if defined(_MSC_VER) && defined(_M_IX86) && !defined(PcTeb)
79 #define PcTeb 0x18
80 #if _MSC_VER >= 1400
81 #pragma intrinsic(__readfsdword)
NtCurrentTeb(void)82 __inline struct _TEB *NtCurrentTeb(void) { return (struct _TEB *)__readfsdword(PcTeb); }
83 #else
NtCurrentTeb(void)84 __inline struct _TEB *NtCurrentTeb(void) { __asm mov eax, fs:[PcTeb] }
85 #endif
86 #endif
87
88 /* Offset to ULONG HardErrorMode field in TEB structure, it is architecture specific. */
89 #if defined(_M_IX86) || defined(__i386__)
90 #define TEB_HARD_ERROR_MODE_OFFSET 0x0F28
91 #elif defined(_M_AMD64) || defined(__x86_64__)
92 #define TEB_HARD_ERROR_MODE_OFFSET 0x16B0
93 #endif
94
95 /*
96 * These psapi functions are available in kernel32.dll library with K32 prefix
97 * on Windows 7 and higher systems. On older Windows systems these functions are
98 * available in psapi.dll libary without K32 prefix. So resolve pointers to
99 * these functions dynamically at runtime from the available system library.
100 * Function GetProcessImageFileNameW() is not available on Windows 2000 and
101 * older systems.
102 */
103 typedef BOOL (WINAPI *EnumProcessesProt)(DWORD *lpidProcess, DWORD cb, DWORD *cbNeeded);
104 typedef DWORD (WINAPI *GetProcessImageFileNameWProt)(HANDLE hProcess, LPWSTR lpImageFileName, DWORD nSize);
105 typedef DWORD (WINAPI *GetModuleFileNameExWProt)(HANDLE hProcess, HMODULE hModule, LPWSTR lpImageFileName, DWORD nSize);
106
107 /*
108 * These aclapi function is available in advapi.dll library on Windows 2000
109 * and higher systems.
110 */
111 typedef BOOL (WINAPI *SetSecurityDescriptorControlProt)(PSECURITY_DESCRIPTOR pSecurityDescriptor, SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
112
113 /*
114 * This errhandlingapi function is available in kernel32.dll library on
115 * Windows 7 and higher systems.
116 */
117 typedef BOOL (WINAPI *SetThreadErrorModeProt)(DWORD dwNewMode, LPDWORD lpOldMode);
118
119
120 static DWORD
format_message_from_system(DWORD win32_error_id,DWORD lang_id,LPSTR buffer,DWORD size)121 format_message_from_system(DWORD win32_error_id, DWORD lang_id, LPSTR buffer, DWORD size)
122 {
123 return FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, win32_error_id, lang_id, buffer, size, NULL);
124 }
125
126 const char *
win32_strerror(DWORD win32_error_id)127 win32_strerror(DWORD win32_error_id)
128 {
129 /*
130 * Use static buffer which is large enough.
131 * Hopefully no Win32 API error message string is longer than 4 kB.
132 */
133 static char buffer[4096];
134 DWORD len;
135
136 /*
137 * If it is possible show error messages in US English language.
138 * International Windows editions do not have to provide error
139 * messages in English language, so fallback to the language
140 * which system provides (neutral).
141 */
142 len = format_message_from_system(win32_error_id, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buffer, sizeof(buffer));
143 if (!len)
144 len = format_message_from_system(win32_error_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, sizeof(buffer));
145
146 /* FormatMessage() automatically appends ".\r\n" to the error message. */
147 if (len && buffer[len-1] == '\n')
148 buffer[--len] = '\0';
149 if (len && buffer[len-1] == '\r')
150 buffer[--len] = '\0';
151 if (len && buffer[len-1] == '.')
152 buffer[--len] = '\0';
153
154 if (!len)
155 sprintf(buffer, "Unknown Win32 error %lu", win32_error_id);
156
157 return buffer;
158 }
159
160 USHORT
win32_get_process_machine(void)161 win32_get_process_machine(void)
162 {
163 IMAGE_DOS_HEADER *dos_header;
164 IMAGE_NT_HEADERS *nt_header;
165
166 dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
167 if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
168 return IMAGE_FILE_MACHINE_UNKNOWN;
169
170 nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
171 if (nt_header->Signature != IMAGE_NT_SIGNATURE)
172 return IMAGE_FILE_MACHINE_UNKNOWN;
173
174 return nt_header->FileHeader.Machine;
175 }
176
177 BOOL
win32_is_non_nt_system(void)178 win32_is_non_nt_system(void)
179 {
180 OSVERSIONINFOA version;
181 version.dwOSVersionInfoSize = sizeof(version);
182 return GetVersionExA(&version) && version.dwPlatformId < VER_PLATFORM_WIN32_NT;
183 }
184
185 BOOL
win32_is_32bit_on_64bit_system(void)186 win32_is_32bit_on_64bit_system(void)
187 {
188 #ifdef _WIN64
189 return FALSE;
190 #else
191 BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
192 HMODULE kernel32;
193 BOOL is_wow64;
194
195 /*
196 * 32-bit process running on 64-bit system is called Wow64 process.
197 * So AMD64 process running on ARM64 system is not Wow64 process.
198 * Check for Wow64 process via IsWow64Process() function exported
199 * from 32-bit kernel32.dll library available on the 64-bit systems.
200 * Resolve pointer to this function at runtime as this code path is
201 * primary running on 32-bit systems where are not available 64-bit
202 * functions.
203 */
204
205 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
206 if (!kernel32)
207 return FALSE;
208
209 MyIsWow64Process = (void *)GetProcAddress(kernel32, "IsWow64Process");
210 if (!MyIsWow64Process)
211 return FALSE;
212
213 if (!MyIsWow64Process(GetCurrentProcess(), &is_wow64))
214 return FALSE;
215
216 return is_wow64;
217 #endif
218 }
219
220 BOOL
win32_is_32bit_on_win8_64bit_system(void)221 win32_is_32bit_on_win8_64bit_system(void)
222 {
223 #ifdef _WIN64
224 return FALSE;
225 #else
226 OSVERSIONINFOA version;
227
228 /* Check for Windows 8 (NT 6.2). */
229 version.dwOSVersionInfoSize = sizeof(version);
230 if (!GetVersionExA(&version) ||
231 version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
232 version.dwMajorVersion < 6 ||
233 (version.dwMajorVersion == 6 && version.dwMinorVersion < 2))
234 return FALSE;
235
236 return win32_is_32bit_on_64bit_system();
237 #endif
238 }
239
240 BOOL
win32_is_not_native_process(USHORT * native_machine_ptr)241 win32_is_not_native_process(USHORT *native_machine_ptr)
242 {
243 BOOL (WINAPI *MyIsWow64Process2)(HANDLE, PUSHORT, PUSHORT);
244 void (WINAPI *MyGetNativeSystemInfo)(LPSYSTEM_INFO);
245 SYSTEM_INFO system_info;
246 USHORT process_machine;
247 USHORT native_machine;
248 HMODULE kernel32;
249
250 /*
251 * Process is not native if the process architecture does not match the
252 * native machine architecture. Every Wow64 process is not native (which
253 * means 32-bit process on 64-bit system) but there are also non-Wow64
254 * processes which are not native (e.g. AMD64 process on ARM64 system).
255 */
256
257 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
258 if (!kernel32)
259 return FALSE;
260
261 /*
262 * First try to use IsWow64Process2() function to determinate if the process
263 * is native or not. This function is available since Windows 10.
264 */
265 MyIsWow64Process2 = (void *)GetProcAddress(kernel32, "IsWow64Process2");
266 if (MyIsWow64Process2 && MyIsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine))
267 {
268 /*
269 * Return value from IsWow64Process2() does not indicate if the process
270 * is Wow64, but rather it indicates if the function succeed or not and
271 * filled process_machine and native_machine values.
272 * Process is Wow64 if process_machine is not IMAGE_FILE_MACHINE_UNKNOWN.
273 * For non-Wow64 processes this function does not provide information
274 * about process architecture, so it cannot be used for detecting if the
275 * non-Wow64 process is native or not.
276 */
277 if (process_machine != IMAGE_FILE_MACHINE_UNKNOWN)
278 {
279 if (native_machine_ptr)
280 *native_machine_ptr = native_machine;
281 return TRUE;
282 }
283
284 /*
285 * For non-Wow64 processes retrieve process architecture and compare it
286 * with native machine architecture. This will distinguish between native
287 * and non-native non-Wow64 processes.
288 */
289 process_machine = win32_get_process_machine();
290 if (process_machine != native_machine)
291 {
292 if (native_machine_ptr)
293 *native_machine_ptr = native_machine;
294 return TRUE;
295 }
296 return FALSE;
297 }
298
299 /*
300 * If function IsWow64Process2() is not available or failed then fallback to
301 * IsWow64Process() via win32_is_32bit_on_64bit_system() wrapper. For Wow64
302 * processes is function GetNativeSystemInfo() returning the correct native
303 * machine architecture. For non-Wow64 it is same as GetSystemInfo() and
304 * therefore does NOT return native system information, despite the name
305 * (this happens for example for AMD64 process on ARM64 system).
306 */
307 if (win32_is_32bit_on_64bit_system())
308 {
309 system_info.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
310 MyGetNativeSystemInfo = (void *)GetProcAddress(kernel32, "GetNativeSystemInfo");
311 if (MyGetNativeSystemInfo)
312 MyGetNativeSystemInfo(&system_info);
313 switch (system_info.wProcessorArchitecture)
314 {
315 case PROCESSOR_ARCHITECTURE_INTEL:
316 *native_machine_ptr = IMAGE_FILE_MACHINE_I386;
317 break;
318 case PROCESSOR_ARCHITECTURE_IA64:
319 *native_machine_ptr = IMAGE_FILE_MACHINE_IA64;
320 break;
321 case PROCESSOR_ARCHITECTURE_ARM:
322 *native_machine_ptr = IMAGE_FILE_MACHINE_ARMNT;
323 break;
324 case PROCESSOR_ARCHITECTURE_AMD64:
325 *native_machine_ptr = IMAGE_FILE_MACHINE_AMD64;
326 break;
327 case PROCESSOR_ARCHITECTURE_ARM64:
328 *native_machine_ptr = IMAGE_FILE_MACHINE_ARM64;
329 break;
330 default:
331 *native_machine_ptr = IMAGE_FILE_MACHINE_UNKNOWN;
332 }
333 return TRUE;
334 }
335
336 /*
337 * It looks like that IsWow64Process2() is currently the only function which
338 * can determinate if the non-Wow64 process is native or not. So if the
339 * IsWow64Process2() function is not available (or failed) and process in not
340 * Wow64 then expects that it is native process.
341 */
342 return FALSE;
343 }
344
345 /*
346 * Change error mode of the current thread. If it is not possible then change
347 * error mode of the whole process. Always returns previous error mode.
348 */
349 UINT
win32_change_error_mode(UINT new_mode)350 win32_change_error_mode(UINT new_mode)
351 {
352 SetThreadErrorModeProt MySetThreadErrorMode = NULL;
353 OSVERSIONINFOA version;
354 HMODULE kernel32;
355 HMODULE ntdll;
356 DWORD old_mode;
357
358 /*
359 * Function SetThreadErrorMode() was introduced in Windows 7, so use
360 * GetProcAddress() for compatibility with older systems.
361 */
362 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
363 if (kernel32)
364 MySetThreadErrorMode = (SetThreadErrorModeProt)(void(*)(void))GetProcAddress(kernel32, "SetThreadErrorMode");
365
366 /*
367 * Function RtlSetThreadErrorMode() was introduced in Windows XP x64
368 * and Windows Server 2003. Use GetProcAddress() as it is in ntdll.dll.
369 */
370 if (!MySetThreadErrorMode)
371 {
372 ntdll = GetModuleHandle(TEXT("ntdll.dll"));
373 if (ntdll)
374 MySetThreadErrorMode = (SetThreadErrorModeProt)(void(*)(void))GetProcAddress(ntdll, "RtlSetThreadErrorMode");
375 }
376
377 if (MySetThreadErrorMode &&
378 MySetThreadErrorMode(new_mode, &old_mode))
379 return old_mode;
380
381 #ifdef TEB_HARD_ERROR_MODE_OFFSET
382 /*
383 * On Windows NT 4.0+ systems fallback to thread HardErrorMode API.
384 * It depends on architecture specific offset for HardErrorMode field in TEB.
385 */
386 version.dwOSVersionInfoSize = sizeof(version);
387 if (GetVersionExA(&version) &&
388 version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
389 version.dwMajorVersion >= 4)
390 {
391 ULONG *hard_error_mode_ptr = (ULONG *)((BYTE *)NtCurrentTeb() + TEB_HARD_ERROR_MODE_OFFSET);
392 old_mode = *hard_error_mode_ptr;
393 *hard_error_mode_ptr = new_mode;
394 return old_mode;
395 }
396 #endif
397
398 /*
399 * Fallback to function SetErrorMode() which modifies error mode of the
400 * whole process and returns old mode.
401 */
402 return SetErrorMode(new_mode);
403 }
404
405 /*
406 * Check if the current thread has particular privilege in current active access
407 * token. Case when it not possible to determinate it (e.g. current thread does
408 * not have permission to open its own current active access token) is evaluated
409 * as thread does not have that privilege.
410 */
411 BOOL
win32_have_privilege(LUID luid_privilege)412 win32_have_privilege(LUID luid_privilege)
413 {
414 PRIVILEGE_SET priv;
415 HANDLE token;
416 BOOL ret;
417
418 /*
419 * If the current thread does not have active access token then thread
420 * uses primary process access token for all permission checks.
421 */
422 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
423 (GetLastError() != ERROR_NO_TOKEN ||
424 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
425 return FALSE;
426
427 priv.PrivilegeCount = 1;
428 priv.Control = PRIVILEGE_SET_ALL_NECESSARY;
429 priv.Privilege[0].Luid = luid_privilege;
430 priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
431
432 if (!PrivilegeCheck(token, &priv, &ret))
433 return FALSE;
434
435 return ret;
436 }
437
438 /*
439 * Enable or disable particular privilege in specified access token.
440 *
441 * Note that it is not possible to disable privilege in access token with
442 * SE_PRIVILEGE_ENABLED_BY_DEFAULT attribute. This function does not check
443 * this case and incorrectly returns no error even when disabling failed.
444 * Rationale for this decision: Simplification of this function as WinAPI
445 * call AdjustTokenPrivileges() does not signal error in this case too.
446 */
447 static BOOL
set_privilege(HANDLE token,LUID luid_privilege,BOOL enable)448 set_privilege(HANDLE token, LUID luid_privilege, BOOL enable)
449 {
450 TOKEN_PRIVILEGES token_privileges;
451
452 token_privileges.PrivilegeCount = 1;
453 token_privileges.Privileges[0].Luid = luid_privilege;
454 token_privileges.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
455
456 /*
457 * WinAPI function AdjustTokenPrivileges() success also when not all
458 * privileges were enabled. It is always required to check for failure
459 * via GetLastError() call. AdjustTokenPrivileges() always sets error
460 * also when it success, as opposite to other WinAPI functions.
461 */
462 if (!AdjustTokenPrivileges(token, FALSE, &token_privileges, sizeof(token_privileges), NULL, NULL) ||
463 GetLastError() != ERROR_SUCCESS)
464 return FALSE;
465
466 return TRUE;
467 }
468
469 /*
470 * Change access token for the current thread to new specified access token.
471 * Previously active access token is stored in old_token variable and can be
472 * used for reverting to this access token. It is set to NULL if the current
473 * thread previously used primary process access token.
474 */
475 BOOL
win32_change_token(HANDLE new_token,HANDLE * old_token)476 win32_change_token(HANDLE new_token, HANDLE *old_token)
477 {
478 HANDLE token;
479
480 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
481 {
482 if (GetLastError() != ERROR_NO_TOKEN)
483 return FALSE;
484 token = NULL;
485 }
486
487 if (!ImpersonateLoggedOnUser(new_token))
488 {
489 if (token)
490 CloseHandle(token);
491 return FALSE;
492 }
493
494 *old_token = token;
495 return TRUE;
496 }
497
498 /*
499 * Change access token for the current thread to the primary process access
500 * token. This function fails also when the current thread already uses primary
501 * process access token.
502 */
503 static BOOL
change_token_to_primary(HANDLE * old_token)504 change_token_to_primary(HANDLE *old_token)
505 {
506 HANDLE token;
507
508 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &token))
509 return FALSE;
510
511 RevertToSelf();
512
513 *old_token = token;
514 return TRUE;
515 }
516
517 /*
518 * Revert to the specified access token for the current thread. When access
519 * token is specified as NULL then revert to the primary process access token.
520 * Use to revert after win32_change_token() or change_token_to_primary() call.
521 */
522 VOID
win32_revert_to_token(HANDLE token)523 win32_revert_to_token(HANDLE token)
524 {
525 /*
526 * If SetThreadToken() call fails then there is no option to revert to
527 * the specified previous thread access token. So in this case revert to
528 * the primary process access token.
529 */
530 if (!token || !SetThreadToken(NULL, token))
531 RevertToSelf();
532 if (token)
533 CloseHandle(token);
534 }
535
536 /*
537 * Enable particular privilege for the current thread. And set method how to
538 * revert this privilege (if to revert whole token or only privilege).
539 */
540 BOOL
win32_enable_privilege(LUID luid_privilege,HANDLE * revert_token,BOOL * revert_only_privilege)541 win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege)
542 {
543 HANDLE thread_token;
544 HANDLE new_token;
545
546 if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &thread_token))
547 {
548 if (set_privilege(thread_token, luid_privilege, TRUE))
549 {
550 /*
551 * Indicate that correct revert method is just to
552 * disable privilege in access token.
553 */
554 if (revert_token && revert_only_privilege)
555 {
556 *revert_token = thread_token;
557 *revert_only_privilege = TRUE;
558 }
559 else
560 {
561 CloseHandle(thread_token);
562 }
563 return TRUE;
564 }
565 CloseHandle(thread_token);
566 /*
567 * If enabling privilege failed then try to enable it via
568 * primary process access token.
569 */
570 }
571
572 /*
573 * If the current thread has already active thread access token then
574 * open it with just impersonate right as it would be used only for
575 * future revert.
576 */
577 if (revert_token && revert_only_privilege)
578 {
579 if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &thread_token))
580 {
581 if (GetLastError() != ERROR_NO_TOKEN)
582 return FALSE;
583 thread_token = NULL;
584 }
585
586 /*
587 * If current thread has no access token (and uses primary
588 * process access token) or it does not have permission to
589 * adjust privileges or it does not have specified privilege
590 * then create a copy of the primary process access token,
591 * assign it for the current thread (= impersonate self)
592 * and then try adjusting privilege again.
593 */
594 if (!ImpersonateSelf(SecurityImpersonation))
595 {
596 if (thread_token)
597 CloseHandle(thread_token);
598 return FALSE;
599 }
600 }
601
602 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE, &new_token))
603 {
604 /* thread_token is set only when we were asked for revert method. */
605 if (revert_token && revert_only_privilege)
606 win32_revert_to_token(thread_token);
607 return FALSE;
608 }
609
610 if (!set_privilege(new_token, luid_privilege, TRUE))
611 {
612 CloseHandle(new_token);
613 /* thread_token is set only when we were asked for revert method. */
614 if (revert_token && revert_only_privilege)
615 win32_revert_to_token(thread_token);
616 return FALSE;
617 }
618
619 /*
620 * Indicate that correct revert method is to change to the previous
621 * access token. Either to the primary process access token or to the
622 * previous thread access token.
623 */
624 if (revert_token && revert_only_privilege)
625 {
626 *revert_token = thread_token;
627 *revert_only_privilege = FALSE;
628 }
629 return TRUE;
630 }
631
632 /*
633 * Revert particular privilege for the current thread was previously enabled by
634 * win32_enable_privilege() call. Either disable privilege in specified access token
635 * or revert to previous access token.
636 */
637 VOID
win32_revert_privilege(LUID luid_privilege,HANDLE revert_token,BOOL revert_only_privilege)638 win32_revert_privilege(LUID luid_privilege, HANDLE revert_token, BOOL revert_only_privilege)
639 {
640 if (revert_only_privilege)
641 {
642 set_privilege(revert_token, luid_privilege, FALSE);
643 CloseHandle(revert_token);
644 }
645 else
646 {
647 win32_revert_to_token(revert_token);
648 }
649 }
650
651 /*
652 * Return owner of the access token used by the current thread. Buffer for
653 * returned owner needs to be released by LocalFree() call.
654 */
655 static TOKEN_OWNER *
get_current_token_owner(VOID)656 get_current_token_owner(VOID)
657 {
658 HANDLE token;
659 DWORD length;
660 TOKEN_OWNER *owner;
661
662 /*
663 * If the current thread does not have active access token then thread
664 * uses primary process access token for all permission checks.
665 */
666 if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) &&
667 (GetLastError() != ERROR_NO_TOKEN ||
668 !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)))
669 return NULL;
670
671 if (!GetTokenInformation(token, TokenOwner, NULL, 0, &length) &&
672 GetLastError() != ERROR_INSUFFICIENT_BUFFER)
673 {
674 CloseHandle(token);
675 return NULL;
676 }
677
678 retry:
679 owner = (TOKEN_OWNER *)LocalAlloc(LPTR, length);
680 if (!owner)
681 {
682 CloseHandle(token);
683 return NULL;
684 }
685
686 if (!GetTokenInformation(token, TokenOwner, owner, length, &length))
687 {
688 /*
689 * Length of token owner (SID) buffer between two get calls may
690 * changes (e.g. by another thread of process), so retry.
691 */
692 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
693 {
694 LocalFree(owner);
695 goto retry;
696 }
697 LocalFree(owner);
698 CloseHandle(token);
699 return NULL;
700 }
701
702 CloseHandle(token);
703 return owner;
704 }
705
706 /*
707 * Create a new security descriptor in absolute form from relative form.
708 * Newly created security descriptor in absolute form is stored in linear buffer.
709 */
710 static PSECURITY_DESCRIPTOR
create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor)711 create_relsd_from_abssd(PSECURITY_DESCRIPTOR rel_security_descriptor)
712 {
713 PBYTE abs_security_descriptor_buffer;
714 DWORD abs_security_descriptor_size=0, abs_dacl_size=0, abs_sacl_size=0, abs_owner_size=0, abs_primary_group_size=0;
715
716 if (!MakeAbsoluteSD(rel_security_descriptor,
717 NULL, &abs_security_descriptor_size,
718 NULL, &abs_dacl_size,
719 NULL, &abs_sacl_size,
720 NULL, &abs_owner_size,
721 NULL, &abs_primary_group_size) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
722 return NULL;
723
724 abs_security_descriptor_buffer = (PBYTE)LocalAlloc(LPTR, abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size+abs_primary_group_size);
725 if (!abs_security_descriptor_buffer)
726 return NULL;
727
728 if (!MakeAbsoluteSD(rel_security_descriptor,
729 (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer, &abs_security_descriptor_size,
730 (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size), &abs_dacl_size,
731 (PACL)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size), &abs_sacl_size,
732 (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size), &abs_owner_size,
733 (PSID)(abs_security_descriptor_buffer+abs_security_descriptor_size+abs_dacl_size+abs_sacl_size+abs_owner_size), &abs_primary_group_size))
734 return NULL;
735
736 return (PSECURITY_DESCRIPTOR)abs_security_descriptor_buffer;
737 }
738
739 /*
740 * Prepare security descriptor obtained by GetKernelObjectSecurity() so it can be
741 * passed to SetKernelObjectSecurity() as identity operation. It modifies control
742 * flags of security descriptor, which is needed for Windows 2000 and new.
743 */
744 static BOOL
prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor)745 prepare_security_descriptor_for_set_operation(PSECURITY_DESCRIPTOR security_descriptor)
746 {
747 SetSecurityDescriptorControlProt MySetSecurityDescriptorControl;
748 SECURITY_DESCRIPTOR_CONTROL bits_mask;
749 SECURITY_DESCRIPTOR_CONTROL bits_set;
750 SECURITY_DESCRIPTOR_CONTROL control;
751 OSVERSIONINFO version;
752 HMODULE advapi32;
753 DWORD revision;
754
755 /*
756 * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are flags introduced in
757 * Windows 2000 to control client-side automatic inheritance (client - user
758 * process - is responsible for propagating inherited ACEs to subobjects).
759 * To prevent applications which do not understand client-side automatic
760 * inheritance (applications created prior Windows 2000 or which use low
761 * level API like SetKernelObjectSecurity()) to unintentionally set those
762 * SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED control flags when
763 * coping them from other security descriptor.
764 *
765 * As we are not modifying existing ACEs, we are compatible with Windows 2000
766 * client-side automatic inheritance model and therefore prepare security
767 * descriptor for SetKernelObjectSecurity() to not clear existing automatic
768 * inheritance control flags.
769 *
770 * Control flags SE_DACL_AUTO_INHERITED and SE_SACL_AUTO_INHERITED are set
771 * into security object only when they are set together with set-only flags
772 * SE_DACL_AUTO_INHERIT_REQ and SE_SACL_AUTO_INHERIT_REQ. Those flags are
773 * never received by GetKernelObjectSecurity() and are just commands for
774 * SetKernelObjectSecurity() how to interpret SE_DACL_AUTO_INHERITED and
775 * SE_SACL_AUTO_INHERITED flags.
776 *
777 * Function symbol SetSecurityDescriptorControl is not available in the
778 * older versions of advapi32.dll library, so resolve it at runtime.
779 */
780
781 version.dwOSVersionInfoSize = sizeof(version);
782 if (!GetVersionEx(&version) ||
783 version.dwPlatformId != VER_PLATFORM_WIN32_NT ||
784 version.dwMajorVersion < 5)
785 return TRUE;
786
787 if (!GetSecurityDescriptorControl(security_descriptor, &control, &revision))
788 return FALSE;
789
790 bits_mask = 0;
791 bits_set = 0;
792
793 if (control & SE_DACL_AUTO_INHERITED)
794 {
795 bits_mask |= SE_DACL_AUTO_INHERIT_REQ;
796 bits_set |= SE_DACL_AUTO_INHERIT_REQ;
797 }
798
799 if (control & SE_SACL_AUTO_INHERITED)
800 {
801 bits_mask |= SE_SACL_AUTO_INHERIT_REQ;
802 bits_set |= SE_SACL_AUTO_INHERIT_REQ;
803 }
804
805 if (!bits_mask)
806 return TRUE;
807
808 advapi32 = GetModuleHandle(TEXT("advapi32.dll"));
809 if (!advapi32)
810 return FALSE;
811
812 MySetSecurityDescriptorControl = (SetSecurityDescriptorControlProt)(void(*)(void))GetProcAddress(advapi32, "SetSecurityDescriptorControl");
813 if (!MySetSecurityDescriptorControl)
814 return FALSE;
815
816 if (!MySetSecurityDescriptorControl(security_descriptor, bits_mask, bits_set))
817 return FALSE;
818
819 return TRUE;
820 }
821
822 /*
823 * Grant particular permissions in the primary access token of the specified
824 * process for the owner of current thread token and set old DACL of the
825 * process access token for reverting permissions. Security descriptor is
826 * just memory buffer for old DACL.
827 */
828 static BOOL
grant_process_token_dacl_permissions(HANDLE process,DWORD permissions,HANDLE * token,PSECURITY_DESCRIPTOR * old_security_descriptor)829 grant_process_token_dacl_permissions(HANDLE process, DWORD permissions, HANDLE *token, PSECURITY_DESCRIPTOR *old_security_descriptor)
830 {
831 TOKEN_OWNER *owner;
832 PACL old_dacl;
833 BOOL old_dacl_present;
834 BOOL old_dacl_defaulted;
835 PACL new_dacl;
836 WORD new_dacl_size;
837 PSECURITY_DESCRIPTOR new_security_descriptor;
838 DWORD length;
839
840 owner = get_current_token_owner();
841 if (!owner)
842 return FALSE;
843
844 /*
845 * READ_CONTROL is required for GetSecurityInfo(DACL_SECURITY_INFORMATION)
846 * and WRITE_DAC is required for SetSecurityInfo(DACL_SECURITY_INFORMATION).
847 */
848 if (!OpenProcessToken(process, READ_CONTROL | WRITE_DAC, token))
849 {
850 LocalFree(owner);
851 return FALSE;
852 }
853
854 if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, NULL, 0, &length) && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
855 {
856 LocalFree(owner);
857 CloseHandle(*token);
858 return FALSE;
859 }
860
861 retry:
862 *old_security_descriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, length);
863 if (!*old_security_descriptor)
864 {
865 LocalFree(owner);
866 CloseHandle(*token);
867 return FALSE;
868 }
869
870 if (!GetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, *old_security_descriptor, length, &length))
871 {
872 /*
873 * Length of the security descriptor between two get calls
874 * may changes (e.g. by another thread of process), so retry.
875 */
876 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
877 {
878 LocalFree(*old_security_descriptor);
879 goto retry;
880 }
881 LocalFree(*old_security_descriptor);
882 LocalFree(owner);
883 CloseHandle(*token);
884 return FALSE;
885 }
886
887 if (!prepare_security_descriptor_for_set_operation(*old_security_descriptor))
888 {
889 LocalFree(*old_security_descriptor);
890 LocalFree(owner);
891 CloseHandle(*token);
892 return FALSE;
893 }
894
895 /* Retrieve the current DACL from security descriptor including present and defaulted properties. */
896 if (!GetSecurityDescriptorDacl(*old_security_descriptor, &old_dacl_present, &old_dacl, &old_dacl_defaulted))
897 {
898 LocalFree(*old_security_descriptor);
899 LocalFree(owner);
900 CloseHandle(*token);
901 return FALSE;
902 }
903
904 /*
905 * If DACL is not present then system grants full access to everyone. It this
906 * case do not modify DACL as it just adds one ACL allow rule for us, which
907 * automatically disallow access to anybody else which had access before.
908 */
909 if (!old_dacl_present || !old_dacl)
910 {
911 LocalFree(*old_security_descriptor);
912 LocalFree(owner);
913 *old_security_descriptor = NULL;
914 return TRUE;
915 }
916
917 /* Create new DACL which would be copy of the current old one. */
918 new_dacl_size = old_dacl->AclSize + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(owner->Owner) - sizeof(DWORD);
919 new_dacl = (PACL)LocalAlloc(LPTR, new_dacl_size);
920 if (!new_dacl)
921 {
922 LocalFree(*old_security_descriptor);
923 LocalFree(owner);
924 CloseHandle(*token);
925 return FALSE;
926 }
927
928 /*
929 * Initialize new DACL structure to the same format as was the old one.
930 * Set new explicit access for the owner of the current thread access
931 * token with non-inherited granting access to specified permissions.
932 * This permission is added in the first ACE, so has the highest priority.
933 */
934 if (!InitializeAcl(new_dacl, new_dacl_size, old_dacl->AclRevision) ||
935 !AddAccessAllowedAce(new_dacl, ACL_REVISION2, permissions, owner->Owner))
936 {
937 LocalFree(new_dacl);
938 LocalFree(*old_security_descriptor);
939 LocalFree(owner);
940 CloseHandle(*token);
941 return FALSE;
942 }
943
944 /*
945 * Now (after setting our new permissions) append all ACE entries from the
946 * old DACL to the new DACL, which preserve all other existing permissions.
947 */
948 if (old_dacl->AceCount > 0)
949 {
950 WORD ace_index;
951 LPVOID ace;
952
953 for (ace_index = 0; ace_index < old_dacl->AceCount; ace_index++)
954 {
955 if (!GetAce(old_dacl, ace_index, &ace) ||
956 !AddAce(new_dacl, old_dacl->AclRevision, MAXDWORD, ace, ((PACE_HEADER)ace)->AceSize))
957 {
958 LocalFree(new_dacl);
959 LocalFree(*old_security_descriptor);
960 LocalFree(owner);
961 CloseHandle(*token);
962 return FALSE;
963 }
964 }
965 }
966
967 /*
968 * Create copy of the old security descriptor, so we can modify its DACL.
969 * Function SetSecurityDescriptorDacl() works only with security descriptors
970 * in absolute format. So use our helper function create_relsd_from_abssd()
971 * for converting security descriptor from relative format (which is returned
972 * by GetKernelObjectSecurity() function) to the absolute format.
973 */
974 new_security_descriptor = create_relsd_from_abssd(*old_security_descriptor);
975 if (!new_security_descriptor)
976 {
977 LocalFree(new_dacl);
978 LocalFree(*old_security_descriptor);
979 LocalFree(owner);
980 CloseHandle(*token);
981 return FALSE;
982 }
983
984 /*
985 * In the new security descriptor replace old DACL by the new DACL (which has
986 * new permissions) and then set this new security descriptor to the token,
987 * so token would have new access permissions.
988 */
989 if (!SetSecurityDescriptorDacl(new_security_descriptor, TRUE, new_dacl, FALSE) ||
990 !SetKernelObjectSecurity(*token, DACL_SECURITY_INFORMATION, new_security_descriptor))
991 {
992 LocalFree(new_security_descriptor);
993 LocalFree(new_dacl);
994 LocalFree(*old_security_descriptor);
995 LocalFree(owner);
996 CloseHandle(*token);
997 return FALSE;
998 }
999
1000 LocalFree(new_security_descriptor);
1001 LocalFree(new_dacl);
1002 LocalFree(owner);
1003 return TRUE;
1004 }
1005
1006 /*
1007 * Revert particular granted permissions in specified access token done by
1008 * grant_process_token_dacl_permissions() call.
1009 */
1010 static VOID
revert_token_dacl_permissions(HANDLE token,PSECURITY_DESCRIPTOR old_security_descriptor)1011 revert_token_dacl_permissions(HANDLE token, PSECURITY_DESCRIPTOR old_security_descriptor)
1012 {
1013 SetKernelObjectSecurity(token, DACL_SECURITY_INFORMATION, old_security_descriptor);
1014 LocalFree(old_security_descriptor);
1015 CloseHandle(token);
1016 }
1017
1018 /*
1019 * Open process handle specified by the process id with the query right and
1020 * optionally also with vm read right.
1021 */
1022 static HANDLE
open_process_for_query(DWORD pid,BOOL with_vm_read)1023 open_process_for_query(DWORD pid, BOOL with_vm_read)
1024 {
1025 BOOL revert_only_privilege;
1026 LUID luid_debug_privilege;
1027 OSVERSIONINFO version;
1028 DWORD process_right;
1029 HANDLE revert_token;
1030 HANDLE process;
1031
1032 /*
1033 * Some processes on Windows Vista and higher systems can be opened only
1034 * with PROCESS_QUERY_LIMITED_INFORMATION right. This right is enough
1035 * for accessing primary process token. But this right is not supported
1036 * on older pre-Vista systems. When the current thread on these older
1037 * systems does not have Debug privilege then OpenProcess() fails with
1038 * ERROR_ACCESS_DENIED. If the current thread has Debug privilege then
1039 * OpenProcess() success and returns handle to requested process.
1040 * Problem is that this handle does not have PROCESS_QUERY_INFORMATION
1041 * right and so cannot be used for accessing primary process token
1042 * on those older systems. Moreover it has zero rights and therefore
1043 * such handle is fully useless. So never try to use open process with
1044 * PROCESS_QUERY_LIMITED_INFORMATION right on older systems than
1045 * Windows Vista (NT 6.0).
1046 */
1047 version.dwOSVersionInfoSize = sizeof(version);
1048 if (GetVersionEx(&version) &&
1049 version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
1050 version.dwMajorVersion >= 6)
1051 process_right = PROCESS_QUERY_LIMITED_INFORMATION;
1052 else
1053 process_right = PROCESS_QUERY_INFORMATION;
1054
1055 if (with_vm_read)
1056 process_right |= PROCESS_VM_READ;
1057
1058 process = OpenProcess(process_right, FALSE, pid);
1059 if (process)
1060 return process;
1061
1062 /*
1063 * It is possible to open only processes to which owner of the current
1064 * thread access token has permissions. For opening other processing it
1065 * is required to have Debug privilege enabled. By default local
1066 * administrators have this privilege, but it is disabled. So try to
1067 * enable it and then try to open process again.
1068 */
1069
1070 if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid_debug_privilege))
1071 return NULL;
1072
1073 if (!win32_enable_privilege(luid_debug_privilege, &revert_token, &revert_only_privilege))
1074 return NULL;
1075
1076 process = OpenProcess(process_right, FALSE, pid);
1077
1078 win32_revert_privilege(luid_debug_privilege, revert_token, revert_only_privilege);
1079
1080 return process;
1081 }
1082
1083 /*
1084 * Check if process image path name (wide string) matches exe file name
1085 * (7-bit ASCII string). Do case-insensitive string comparison. Process
1086 * image path name can be in any namespace format (DOS, Win32, UNC, ...).
1087 */
1088 static BOOL
check_process_name(LPCWSTR path,DWORD path_length,LPCSTR exe_file)1089 check_process_name(LPCWSTR path, DWORD path_length, LPCSTR exe_file)
1090 {
1091 DWORD exe_file_length;
1092 WCHAR c1;
1093 UCHAR c2;
1094 DWORD i;
1095
1096 exe_file_length = 0;
1097 while (exe_file[exe_file_length] != '\0')
1098 exe_file_length++;
1099
1100 /* Path must have backslash before exe file name. */
1101 if (exe_file_length >= path_length ||
1102 path[path_length-exe_file_length-1] != L'\\')
1103 return FALSE;
1104
1105 for (i = 0; i < exe_file_length; i++)
1106 {
1107 c1 = path[path_length-exe_file_length+i];
1108 c2 = exe_file[i];
1109 /*
1110 * Input string for comparison is 7-bit ASCII and file name part
1111 * of path must not contain backslash as it is path separator.
1112 */
1113 if (c1 >= 0x80 || c2 >= 0x80 || c1 == L'\\')
1114 return FALSE;
1115 if (c1 >= L'a' && c1 <= L'z')
1116 c1 -= L'a' - L'A';
1117 if (c2 >= 'a' && c2 <= 'z')
1118 c2 -= 'a' - 'A';
1119 if (c1 != c2)
1120 return FALSE;
1121 }
1122
1123 return TRUE;
1124 }
1125
1126 /* Open process handle with the query right specified by process exe file. */
1127 HANDLE
win32_find_and_open_process_for_query(LPCSTR exe_file)1128 win32_find_and_open_process_for_query(LPCSTR exe_file)
1129 {
1130 GetProcessImageFileNameWProt MyGetProcessImageFileNameW;
1131 GetModuleFileNameExWProt MyGetModuleFileNameExW;
1132 EnumProcessesProt MyEnumProcesses;
1133 HMODULE kernel32, psapi;
1134 UINT prev_error_mode;
1135 DWORD partial_retry;
1136 BOOL found_process;
1137 DWORD size, length;
1138 DWORD *processes;
1139 HANDLE process;
1140 LPWSTR path;
1141 DWORD error;
1142 DWORD count;
1143 DWORD i;
1144
1145 psapi = NULL;
1146 kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
1147 if (!kernel32)
1148 return NULL;
1149
1150 /*
1151 * On Windows 7 and higher systems these functions are available in
1152 * kernel32.dll library with K32 prefix.
1153 */
1154 MyGetModuleFileNameExW = NULL;
1155 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(void(*)(void))GetProcAddress(kernel32, "K32GetProcessImageFileNameW");
1156 MyEnumProcesses = (EnumProcessesProt)(void(*)(void))GetProcAddress(kernel32, "K32EnumProcesses");
1157 if (!MyGetProcessImageFileNameW || !MyEnumProcesses)
1158 {
1159 /*
1160 * On older NT-based systems these functions are available in
1161 * psapi.dll library without K32 prefix.
1162 */
1163 prev_error_mode = win32_change_error_mode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
1164 psapi = LoadLibrary(TEXT("psapi.dll"));
1165 win32_change_error_mode(prev_error_mode);
1166
1167 if (!psapi)
1168 return NULL;
1169
1170 /*
1171 * Function GetProcessImageFileNameW() is available in
1172 * Windows XP and higher systems. On older versions is
1173 * available function GetModuleFileNameExW().
1174 */
1175 MyGetProcessImageFileNameW = (GetProcessImageFileNameWProt)(void(*)(void))GetProcAddress(psapi, "GetProcessImageFileNameW");
1176 MyGetModuleFileNameExW = (GetModuleFileNameExWProt)(void(*)(void))GetProcAddress(psapi, "GetModuleFileNameExW");
1177 MyEnumProcesses = (EnumProcessesProt)(void(*)(void))GetProcAddress(psapi, "EnumProcesses");
1178 if ((!MyGetProcessImageFileNameW && !MyGetModuleFileNameExW) || !MyEnumProcesses)
1179 {
1180 FreeLibrary(psapi);
1181 return NULL;
1182 }
1183 }
1184
1185 /* Make initial buffer size for 1024 processes. */
1186 size = 1024 * sizeof(*processes);
1187
1188 retry:
1189 processes = (DWORD *)LocalAlloc(LPTR, size);
1190 if (!processes)
1191 {
1192 if (psapi)
1193 FreeLibrary(psapi);
1194 return NULL;
1195 }
1196
1197 if (!MyEnumProcesses(processes, size, &length))
1198 {
1199 LocalFree(processes);
1200 if (psapi)
1201 FreeLibrary(psapi);
1202 return NULL;
1203 }
1204 else if (size == length)
1205 {
1206 /*
1207 * There is no indication given when the buffer is too small to
1208 * store all process identifiers. Therefore if returned length
1209 * is same as buffer size there can be more processes. Call
1210 * again with larger buffer.
1211 */
1212 LocalFree(processes);
1213 size *= 2;
1214 goto retry;
1215 }
1216
1217 process = NULL;
1218 count = length / sizeof(*processes);
1219
1220 for (i = 0; i < count; i++)
1221 {
1222 /* Skip System Idle Process. */
1223 if (processes[i] == 0)
1224 continue;
1225
1226 /*
1227 * Function GetModuleFileNameExW() requires additional
1228 * PROCESS_VM_READ right as opposite to function
1229 * GetProcessImageFileNameW() which does not need it.
1230 */
1231 process = open_process_for_query(processes[i], MyGetProcessImageFileNameW ? FALSE : TRUE);
1232 if (!process)
1233 continue;
1234
1235 /*
1236 * Set initial buffer size to 256 (wide) characters.
1237 * Final path length on the modern NT-based systems can be also larger.
1238 */
1239 size = 256;
1240 found_process = FALSE;
1241 partial_retry = 0;
1242
1243 retry_path:
1244 path = (LPWSTR)LocalAlloc(LPTR, size * sizeof(*path));
1245 if (!path)
1246 goto end_path;
1247
1248 if (MyGetProcessImageFileNameW)
1249 length = MyGetProcessImageFileNameW(process, path, size);
1250 else
1251 length = MyGetModuleFileNameExW(process, NULL, path, size);
1252
1253 error = GetLastError();
1254
1255 /*
1256 * GetModuleFileNameEx() returns zero and signal error ERROR_PARTIAL_COPY
1257 * when remote process is in the middle of updating its module table.
1258 * Sleep 10 ms and try again, max 10 attempts.
1259 */
1260 if (!MyGetProcessImageFileNameW)
1261 {
1262 if (length == 0 && error == ERROR_PARTIAL_COPY && partial_retry++ < 10)
1263 {
1264 Sleep(10);
1265 goto retry_path;
1266 }
1267 partial_retry = 0;
1268 }
1269
1270 /*
1271 * When buffer is too small then function GetModuleFileNameEx() returns
1272 * its size argument on older systems (Windows XP) or its size minus
1273 * argument one on new systems (Windows 10) without signalling any error.
1274 * Function GetProcessImageFileNameW() on the other hand returns zero
1275 * value and signals error ERROR_INSUFFICIENT_BUFFER. So in all these
1276 * cases call function again with larger buffer.
1277 */
1278
1279 if (MyGetProcessImageFileNameW && length == 0 && error != ERROR_INSUFFICIENT_BUFFER)
1280 goto end_path;
1281
1282 if ((MyGetProcessImageFileNameW && length == 0) ||
1283 (!MyGetProcessImageFileNameW && (length == size || length == size-1)))
1284 {
1285 LocalFree(path);
1286 size *= 2;
1287 goto retry_path;
1288 }
1289
1290 if (length && check_process_name(path, length, exe_file))
1291 found_process = TRUE;
1292
1293 end_path:
1294 if (path)
1295 {
1296 LocalFree(path);
1297 path = NULL;
1298 }
1299
1300 if (found_process)
1301 break;
1302
1303 CloseHandle(process);
1304 process = NULL;
1305 }
1306
1307 LocalFree(processes);
1308
1309 if (psapi)
1310 FreeLibrary(psapi);
1311
1312 return process;
1313 }
1314
1315 /*
1316 * Try to open primary access token of the particular process with specified
1317 * rights. Before opening access token try to adjust DACL permissions of the
1318 * primary process access token, so following open does not fail on error
1319 * related to no open permissions. Revert DACL permissions after open attempt.
1320 * As following steps are not atomic, try to execute them more times in case
1321 * of possible race conditions caused by other threads or processes.
1322 */
1323 static HANDLE
try_grant_permissions_and_open_process_token(HANDLE process,DWORD rights)1324 try_grant_permissions_and_open_process_token(HANDLE process, DWORD rights)
1325 {
1326 PSECURITY_DESCRIPTOR old_security_descriptor;
1327 HANDLE grant_token;
1328 HANDLE token;
1329 DWORD retry;
1330 DWORD error;
1331
1332 /*
1333 * This code is not atomic. Between grant and open calls can other
1334 * thread or process change or revert permissions. So try to execute
1335 * it more times.
1336 */
1337 for (retry = 0; retry < 10; retry++)
1338 {
1339 if (!grant_process_token_dacl_permissions(process, rights, &grant_token, &old_security_descriptor))
1340 return NULL;
1341 if (!OpenProcessToken(process, rights, &token))
1342 {
1343 token = NULL;
1344 error = GetLastError();
1345 }
1346 if (old_security_descriptor)
1347 revert_token_dacl_permissions(grant_token, old_security_descriptor);
1348 if (token)
1349 return token;
1350 else if (error != ERROR_ACCESS_DENIED)
1351 return NULL;
1352 }
1353
1354 return NULL;
1355 }
1356
1357 /*
1358 * Open primary access token of particular process handle with specified rights.
1359 * If permissions for specified rights are missing then try to grant them.
1360 */
1361 HANDLE
win32_open_process_token_with_rights(HANDLE process,DWORD rights)1362 win32_open_process_token_with_rights(HANDLE process, DWORD rights)
1363 {
1364 HANDLE old_token;
1365 HANDLE token;
1366
1367 /* First try to open primary access token of process handle directly. */
1368 if (OpenProcessToken(process, rights, &token))
1369 return token;
1370
1371 /*
1372 * If opening failed then it means that owner of the current thread
1373 * access token does not have permission for it. Try it again with
1374 * primary process access token.
1375 */
1376 if (change_token_to_primary(&old_token))
1377 {
1378 if (!OpenProcessToken(process, rights, &token))
1379 token = NULL;
1380 win32_revert_to_token(old_token);
1381 if (token)
1382 return token;
1383 }
1384
1385 /*
1386 * If opening is still failing then try to grant specified permissions
1387 * for the current thread and try to open it again.
1388 */
1389 token = try_grant_permissions_and_open_process_token(process, rights);
1390 if (token)
1391 return token;
1392
1393 /*
1394 * And if it is still failing then try it again with granting
1395 * permissions for the primary process token of the current process.
1396 */
1397 if (change_token_to_primary(&old_token))
1398 {
1399 token = try_grant_permissions_and_open_process_token(process, rights);
1400 win32_revert_to_token(old_token);
1401 if (token)
1402 return token;
1403 }
1404
1405 /*
1406 * TODO: Sorry, no other option for now...
1407 * It could be possible to use Take Ownership Name privilege to
1408 * temporary change token owner of specified process to the owner of
1409 * the current thread token, grant permissions for current thread in
1410 * that process token, change ownership back to original one, open
1411 * that process token and revert granted permissions. But this is
1412 * not implemented yet.
1413 */
1414 return NULL;
1415 }
1416
1417 /*
1418 * Call supplied function with its argument and if it fails with
1419 * ERROR_PRIVILEGE_NOT_HELD then try to enable Tcb privilege and
1420 * call function with its argument again.
1421 */
1422 BOOL
win32_call_func_with_tcb_privilege(BOOL (* function)(LPVOID),LPVOID argument)1423 win32_call_func_with_tcb_privilege(BOOL (*function)(LPVOID), LPVOID argument)
1424 {
1425 LUID luid_tcb_privilege;
1426 LUID luid_impersonate_privilege;
1427
1428 HANDLE revert_token_tcb_privilege;
1429 BOOL revert_only_tcb_privilege;
1430
1431 HANDLE revert_token_impersonate_privilege;
1432 BOOL revert_only_impersonate_privilege;
1433
1434 BOOL impersonate_privilege_enabled;
1435
1436 BOOL revert_to_old_token;
1437 HANDLE old_token;
1438
1439 HANDLE lsass_process;
1440 HANDLE lsass_token;
1441
1442 DWORD error;
1443 BOOL ret;
1444
1445 impersonate_privilege_enabled = FALSE;
1446 revert_to_old_token = FALSE;
1447 lsass_token = NULL;
1448 old_token = NULL;
1449
1450 /* Call supplied function. */
1451 ret = function(argument);
1452 if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1453 goto ret;
1454
1455 /*
1456 * If function call failed with ERROR_PRIVILEGE_NOT_HELD
1457 * error then it means that the current thread token does not have
1458 * Tcb privilege enabled. Try to enable it.
1459 */
1460
1461 if (!LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid_tcb_privilege))
1462 goto err_privilege_not_held;
1463
1464 /*
1465 * If the current thread has already Tcb privilege enabled then there
1466 * is some additional unhanded restriction.
1467 */
1468 if (win32_have_privilege(luid_tcb_privilege))
1469 goto err_privilege_not_held;
1470
1471 /* Try to enable Tcb privilege and try function call again. */
1472 if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1473 {
1474 ret = function(argument);
1475 win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1476 goto ret;
1477 }
1478
1479 /*
1480 * If enabling of Tcb privilege failed then it means that current thread
1481 * does not have this privilege. But current process may have it. So try it
1482 * again with primary process access token.
1483 */
1484
1485 /*
1486 * If system supports Impersonate privilege (Windows 2000 SP4 or higher) then
1487 * all future actions in this function require this Impersonate privilege.
1488 * So try to enable it in case it is currently disabled.
1489 */
1490 if (LookupPrivilegeValue(NULL, SE_IMPERSONATE_NAME, &luid_impersonate_privilege) &&
1491 !win32_have_privilege(luid_impersonate_privilege))
1492 {
1493 /*
1494 * If current thread does not have Impersonate privilege enabled
1495 * then first try to enable it just for the current thread. If
1496 * it is not possible to enable it just for the current thread
1497 * then try it to enable globally for whole process (which
1498 * affects all process threads). Both actions will be reverted
1499 * at the end of this function.
1500 */
1501 if (win32_enable_privilege(luid_impersonate_privilege, &revert_token_impersonate_privilege, &revert_only_impersonate_privilege))
1502 {
1503 impersonate_privilege_enabled = TRUE;
1504 }
1505 else if (win32_enable_privilege(luid_impersonate_privilege, NULL, NULL))
1506 {
1507 impersonate_privilege_enabled = TRUE;
1508 revert_token_impersonate_privilege = NULL;
1509 revert_only_impersonate_privilege = TRUE;
1510 }
1511 else
1512 {
1513 goto err_privilege_not_held;
1514 }
1515
1516 /*
1517 * Now when Impersonate privilege is enabled, try to enable Tcb
1518 * privilege again. Enabling other privileges for the current
1519 * thread requires Impersonate privilege, so enabling Tcb again
1520 * could now pass.
1521 */
1522 if (win32_enable_privilege(luid_tcb_privilege, &revert_token_tcb_privilege, &revert_only_tcb_privilege))
1523 {
1524 ret = function(argument);
1525 win32_revert_privilege(luid_tcb_privilege, revert_token_tcb_privilege, revert_only_tcb_privilege);
1526 goto ret;
1527 }
1528 }
1529
1530 /*
1531 * If enabling Tcb privilege failed then it means that the current
1532 * thread access token does not have this privilege or does not
1533 * have permission to adjust privileges.
1534 *
1535 * Try to use more privileged token from Local Security Authority
1536 * Subsystem Service process (lsass.exe) which has Tcb privilege.
1537 * Retrieving this more privileged token is possible for local
1538 * administrators (unless it was disabled by local administrators).
1539 */
1540
1541 lsass_process = win32_find_and_open_process_for_query("lsass.exe");
1542 if (!lsass_process)
1543 goto err_privilege_not_held;
1544
1545 /*
1546 * Open primary lsass.exe process access token with query and duplicate
1547 * rights. Just these two rights are required for impersonating other
1548 * primary process token (impersonate right is really not required!).
1549 */
1550 lsass_token = win32_open_process_token_with_rights(lsass_process, TOKEN_QUERY | TOKEN_DUPLICATE);
1551
1552 CloseHandle(lsass_process);
1553
1554 if (!lsass_token)
1555 goto err_privilege_not_held;
1556
1557 /*
1558 * After successful open of the primary lsass.exe process access token,
1559 * assign its copy for the current thread.
1560 */
1561 if (!win32_change_token(lsass_token, &old_token))
1562 goto err_privilege_not_held;
1563
1564 revert_to_old_token = TRUE;
1565
1566 ret = function(argument);
1567 if (ret || GetLastError() != ERROR_PRIVILEGE_NOT_HELD)
1568 goto ret;
1569
1570 /*
1571 * Now current thread is not using primary process token anymore
1572 * but is using custom access token. There is no need to revert
1573 * enabled Tcb privilege as the whole custom access token would
1574 * be reverted. So there is no need to setup revert method for
1575 * enabling privilege.
1576 */
1577 if (win32_have_privilege(luid_tcb_privilege) ||
1578 !win32_enable_privilege(luid_tcb_privilege, NULL, NULL))
1579 goto err_privilege_not_held;
1580
1581 ret = function(argument);
1582 goto ret;
1583
1584 err_privilege_not_held:
1585 SetLastError(ERROR_PRIVILEGE_NOT_HELD);
1586 ret = FALSE;
1587 goto ret;
1588
1589 ret:
1590 error = GetLastError();
1591
1592 if (revert_to_old_token)
1593 win32_revert_to_token(old_token);
1594
1595 if (impersonate_privilege_enabled)
1596 win32_revert_privilege(luid_impersonate_privilege, revert_token_impersonate_privilege, revert_only_impersonate_privilege);
1597
1598 if (lsass_token)
1599 CloseHandle(lsass_token);
1600
1601 SetLastError(error);
1602
1603 return ret;
1604 }
1605