1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// WinWrap.cpp
6//
7
8//
9// This file contains wrapper functions for Win32 API's that take strings.
10//
11// COM+ internally uses UNICODE as the internal state and string format. This
12// file will undef the mapping macros so that one cannot mistakingly call a
13// method that isn't going to work. Instead, you have to call the correct
14// wrapper API.
15//
16//*****************************************************************************
17
18#include "stdafx.h" // Precompiled header key.
19#include "winwrap.h" // Header for macros and functions.
20#include "utilcode.h"
21#include "holder.h"
22#include "ndpversion.h"
23#include "pedecoder.h"
24
25
26// ====== READ BEFORE ADDING CONTRACTS ==================================================
27// The functions in this file propagate SetLastError codes to their callers.
28// Contracts are not guaranteed to preserve these codes (and no, we're not taking
29// the overhead hit to make them do so. Don't bother asking.)
30//
31// Most of the wrappers have a contract of the form:
32//
33// NOTHROW;
34// INJECT_FAULT(xxx);
35//
36// For such functions, use the special purpose construct:
37//
38// WINWRAPPER_NO_CONTRACT(xxx);
39//
40// For everything else, use STATIC_CONTRACT.
41//
42#undef CONTRACT
43#define CONTRACT $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$
44
45#undef CONTRACTL
46#define CONTRACTL $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$
47
48#ifdef ENABLE_CONTRACTS_IMPL
49static BOOL gWinWrapperContractRecursionBreak = FALSE;
50
51
52class WinWrapperContract
53{
54 public:
55 WinWrapperContract(const char *szFunction, const char *szFile, int lineNum)
56 {
57 CANNOT_HAVE_CONTRACT;
58
59 m_pClrDebugState = NULL;
60
61 if (gWinWrapperContractRecursionBreak)
62 {
63 return;
64 }
65
66 m_pClrDebugState = GetClrDebugState();
67
68 // Save old debug state
69 m_IncomingClrDebugState = *m_pClrDebugState;
70
71
72 m_pClrDebugState->ViolationMaskReset( ThrowsViolation );
73
74 if (m_pClrDebugState->IsFaultForbid() && !(m_pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState)))
75 {
76 gWinWrapperContractRecursionBreak = TRUE;
77
78 CONTRACT_ASSERT("INJECT_FAULT called in a FAULTFORBID region.",
79 Contract::FAULT_Forbid,
80 Contract::FAULT_Mask,
81 szFunction,
82 szFile,
83 lineNum
84 );
85 }
86
87
88 };
89
90 ~WinWrapperContract()
91 {
92 CANNOT_HAVE_CONTRACT;
93
94 //!!!!!! THIS DESTRUCTOR MUST NOT CHANGE THE GETLASTERROR VALUE !!!!!!
95
96 // Backout all changes to debug state.
97 if (m_pClrDebugState != NULL)
98 {
99 *m_pClrDebugState = m_IncomingClrDebugState;
100 }
101 }
102 private:
103 ClrDebugState *m_pClrDebugState;
104 ClrDebugState m_IncomingClrDebugState;
105
106};
107
108
109
110#endif
111
112#ifdef ENABLE_CONTRACTS_IMPL
113#define WINWRAPPER_NO_CONTRACT(stmt) \
114 STATIC_CONTRACT_NOTHROW; \
115 STATIC_CONTRACT_FAULT; \
116 STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
117 WinWrapperContract __wcontract(__FUNCTION__, __FILE__, __LINE__); \
118 if (0) {stmt} \
119
120#define STATIC_WINWRAPPER_NO_CONTRACT(stmt) \
121 STATIC_CONTRACT_NOTHROW; \
122 STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
123 STATIC_CONTRACT_FAULT; \
124 if (0) {stmt} \
125
126
127#else
128#define WINWRAPPER_NO_CONTRACT(stmt)
129#define STATIC_WINWRAPPER_NO_CONTRACT(stmt)
130#endif
131
132ULONG g_dwMaxDBCSCharByteSize = 0;
133
134// The only purpose of this function is to make a local copy of lpCommandLine.
135// Because windows implementation of CreateProcessW can actually change lpCommandLine,
136// but we'd like to keep it const.
137BOOL
138WszCreateProcess(
139 LPCWSTR lpApplicationName,
140 LPCWSTR lpCommandLine,
141 LPSECURITY_ATTRIBUTES lpProcessAttributes,
142 LPSECURITY_ATTRIBUTES lpThreadAttributes,
143 BOOL bInheritHandles,
144 DWORD dwCreationFlags,
145 LPVOID lpEnvironment,
146 LPCWSTR lpCurrentDirectory,
147 LPSTARTUPINFOW lpStartupInfo,
148 LPPROCESS_INFORMATION lpProcessInformation
149 )
150{
151 WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;);
152
153 BOOL fResult;
154 DWORD err;
155 {
156 size_t commandLineLength = wcslen(lpCommandLine) + 1;
157 NewArrayHolder<WCHAR> nonConstCommandLine(new (nothrow) WCHAR[commandLineLength]);
158 if (nonConstCommandLine == NULL)
159 {
160 SetLastError(ERROR_OUTOFMEMORY);
161 return 0;
162 }
163
164 memcpy(nonConstCommandLine, lpCommandLine, commandLineLength * sizeof(WCHAR));
165
166 fResult = CreateProcessW(lpApplicationName,
167 nonConstCommandLine,
168 lpProcessAttributes,
169 lpThreadAttributes,
170 bInheritHandles,
171 dwCreationFlags,
172 lpEnvironment,
173 (LPWSTR)lpCurrentDirectory,
174 lpStartupInfo,
175 lpProcessInformation);
176
177 // At the end of the current scope, the last error code will be overwritten by the destructor of
178 // NewArrayHolder. So we save the error code here, and restore it after the end of the current scope.
179 err = GetLastError();
180 }
181
182 SetLastError(err);
183 return fResult;
184}
185
186#ifndef FEATURE_PAL
187
188
189#include "psapi.h"
190#include "tlhelp32.h"
191#include "winnls.h"
192
193//********** Globals. *********************************************************
194bool g_fEnsureCharSetInfoInitialized = FALSE; // true if we've detected the platform's character set characteristics
195
196// Detect Unicode support of the operating system, and initialize globals
197void EnsureCharSetInfoInitialized()
198{
199 STATIC_CONTRACT_NOTHROW;
200 STATIC_CONTRACT_FORBID_FAULT;
201 STATIC_CONTRACT_CANNOT_TAKE_LOCK;
202 STATIC_CONTRACT_SO_TOLERANT;
203
204 if (!g_fEnsureCharSetInfoInitialized)
205 {
206 // NOTE: Do not use any of the Wsz* wrapper functions right now. They will have
207 // problems.
208
209 // Per Shupak, you're supposed to get the maximum size of a DBCS char
210 // dynamically to work properly on all locales (bug 2757).
211 CPINFO cpInfo;
212 if (GetCPInfo(CP_ACP, &cpInfo))
213 g_dwMaxDBCSCharByteSize = cpInfo.MaxCharSize;
214 else
215 g_dwMaxDBCSCharByteSize = 2;
216
217 VolatileStore(&g_fEnsureCharSetInfoInitialized, true);
218 }
219
220 return;
221}
222
223
224// Running with an interactive workstation.
225BOOL RunningInteractive()
226{
227 STATIC_CONTRACT_NOTHROW;
228 STATIC_CONTRACT_FORBID_FAULT;
229
230 static int fInteractive = -1;
231 if (fInteractive != -1)
232 return fInteractive != 0;
233
234#if !defined(FEATURE_CORESYSTEM)
235 HWINSTA hwinsta = NULL;
236
237 if ((hwinsta = GetProcessWindowStation() ) != NULL)
238 {
239 DWORD lengthNeeded;
240 USEROBJECTFLAGS flags;
241
242 if (GetUserObjectInformationW (hwinsta, UOI_FLAGS, &flags, sizeof(flags), &lengthNeeded))
243 {
244 if ((flags.dwFlags & WSF_VISIBLE) == 0)
245 fInteractive = 0;
246 }
247 }
248#endif // !FEATURE_CORESYSTEM
249
250 if (fInteractive != 0)
251 fInteractive = 1;
252
253 return fInteractive != 0;
254}
255
256
257// Wrapper function around CheckTokenMembership to determine if the token enables the SID "S-1-5-<rid>".
258// If hToken is NULL, this function uses the thread's impersonation token. If the thread is not impersonating, the
259// process token is used.
260//
261// If the function succeeds, it returns ERROR_SUCCESS, else it returns the error code returned by GetLastError()
262static DWORD TokenEnablesSID(IN HANDLE hToken OPTIONAL, IN DWORD rid, OUT BOOL& fResult)
263{
264 DWORD dwError;
265 SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
266 PSID pSid = NULL;
267 HMODULE hAdvApi32 = NULL;
268 typedef BOOL (WINAPI *CheckTokenMembership_t)(HANDLE TokenHandle, PSID SidToCheck, PBOOL IsMember);
269 CheckTokenMembership_t pfnCheckTokenMembership = NULL;
270
271 hAdvApi32 = WszGetModuleHandle(W("advapi32.dll"));
272 if (hAdvApi32 == NULL)
273 {
274 dwError = ERROR_MOD_NOT_FOUND;
275 goto lExit;
276 }
277 pfnCheckTokenMembership = (CheckTokenMembership_t) GetProcAddress(hAdvApi32, "CheckTokenMembership");
278 if (pfnCheckTokenMembership == NULL)
279 {
280 dwError = GetLastError();
281 goto lExit;
282 }
283
284 fResult = FALSE;
285 if (!AllocateAndInitializeSid(&SIDAuthNT, 1, rid, 0, 0, 0, 0, 0, 0, 0, &pSid))
286 {
287 dwError = GetLastError();
288 goto lExit;
289 }
290 if (!pfnCheckTokenMembership(hToken, pSid, &fResult))
291 {
292 dwError = GetLastError();
293 goto lExit;
294 }
295 dwError = ERROR_SUCCESS;
296
297lExit:
298 if (pSid) FreeSid(pSid);
299 return dwError;
300
301}
302
303// Determines if the process is running as Local System or as a service. Note that
304// the function attempts to determine the process' identity and not the thread's
305// (if the thread is impersonating).
306//
307// Parameters:
308// fIsLocalSystemOrService - TRUE if the function succeeds and the process is
309// running as SYSTEM or as a service
310//
311// Return value:
312//
313// If the function succeeds, it returns ERROR_SUCCESS, else it returns the error
314// code returned by GetLastError()
315//
316// Notes:
317//
318// This function will generally fail if the calling thread is impersonating at the
319// ANONYMOUS level; see the comments in the function.
320//
321DWORD RunningAsLocalSystemOrService(OUT BOOL& fIsLocalSystemOrService)
322{
323 STATIC_CONTRACT_NOTHROW;
324 STATIC_CONTRACT_FORBID_FAULT;
325
326 static int fLocalSystemOrService = -1;
327 if (fLocalSystemOrService != -1)
328 {
329 fIsLocalSystemOrService = fLocalSystemOrService != 0;
330 return ERROR_SUCCESS;
331 }
332
333 DWORD dwError;
334 HANDLE hThreadToken = NULL;
335 HANDLE hProcessToken = NULL;
336 HANDLE hDuplicatedProcessToken = NULL;
337 BOOL fLocalSystem = FALSE;
338 BOOL fService = FALSE;
339 BOOL bReverted = FALSE;
340
341 if (OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE, TRUE, &hThreadToken))
342 {
343 if (RevertToSelf())
344 {
345 bReverted = TRUE;
346 }
347#ifdef _DEBUG
348 else
349 {
350 // For debugging only, continue as the impersonated user; see comment below
351 dwError = GetLastError();
352 }
353#endif // #ifdef _DEBUG
354 }
355#ifdef _DEBUG
356 else
357 {
358 dwError = GetLastError();
359 if (dwError == ERROR_NO_IMPERSONATION_TOKEN || dwError == ERROR_NO_TOKEN)
360 {
361 // The thread is not impersonating; it's safe to continue
362 }
363 else
364 {
365 // The thread could be impersonating, but we won't be able to restore the impersonation
366 // token if we RevertToSelf(). Continue as the impersonated user. OpenProcessToken will
367 // fail (unless the impersonated user is SYSTEM or the same as the process' user).
368 //
369 // Note that this case will occur if the impersonation level is ANONYMOUS, the error
370 // code will be ERROR_CANT_OPEN_ANONYMOUS.
371 }
372 }
373#endif // #ifdef _DEBUG
374
375 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hProcessToken))
376 {
377 dwError = GetLastError();
378 goto lExit;
379 }
380
381 if (!DuplicateToken(hProcessToken, SecurityImpersonation, &hDuplicatedProcessToken))
382 {
383 dwError = GetLastError();
384 goto lExit;
385 }
386
387 dwError = TokenEnablesSID(hDuplicatedProcessToken, SECURITY_LOCAL_SYSTEM_RID, fLocalSystem);
388 if (dwError != ERROR_SUCCESS)
389 {
390 goto lExit;
391 }
392 if (fLocalSystem)
393 {
394 goto lExit;
395 }
396
397 dwError = TokenEnablesSID(hDuplicatedProcessToken, SECURITY_SERVICE_RID, fService);
398
399lExit:
400 if (bReverted)
401 {
402 if (!SetThreadToken(NULL, hThreadToken))
403 {
404 DWORD dwLastError = GetLastError();
405 _ASSERT("SetThreadToken failed");
406
407 TerminateProcess(GetCurrentProcess(), dwLastError);
408 }
409 }
410
411 if (hThreadToken) CloseHandle(hThreadToken);
412 if (hProcessToken) CloseHandle(hProcessToken);
413 if (hDuplicatedProcessToken) CloseHandle(hDuplicatedProcessToken);
414
415 if (dwError != ERROR_SUCCESS)
416 {
417 fIsLocalSystemOrService = FALSE; // We don't really know
418 }
419 else
420 {
421 fLocalSystemOrService = (fLocalSystem || fService)? 1 : 0;
422 fIsLocalSystemOrService = fLocalSystemOrService != 0;
423 }
424
425 return dwError;
426
427}
428
429typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
430extern pfnSetThreadDescription g_pfnSetThreadDescription;
431
432// Dummy method if windows version does not support it
433HRESULT SetThreadDescriptionDummy(HANDLE hThread, PCWSTR lpThreadDescription)
434{
435 return NOERROR;
436}
437
438HRESULT WINAPI InitializeSetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
439{
440 HMODULE hKernel32 = WszLoadLibrary(W("kernel32.dll"));
441
442 pfnSetThreadDescription pLocal = NULL;
443 if (hKernel32 != NULL)
444 {
445 // store to thread local variable to prevent data race
446 pLocal = (pfnSetThreadDescription)GetProcAddress(hKernel32, "SetThreadDescription");
447 }
448
449 if (pLocal == NULL) // method is only available with Windows 10 Creators Update or later
450 {
451 g_pfnSetThreadDescription = SetThreadDescriptionDummy;
452 }
453 else
454 {
455 g_pfnSetThreadDescription = pLocal;
456 }
457
458 return g_pfnSetThreadDescription(hThread, lpThreadDescription);
459}
460
461pfnSetThreadDescription g_pfnSetThreadDescription = &InitializeSetThreadDescription;
462
463// Set unmanaged thread name which will show up in ETW and Debuggers which know how to read this data.
464HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription)
465{
466 return g_pfnSetThreadDescription(hThread, lpThreadDescription);
467}
468
469#endif //!FEATURE_PAL
470