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 |
49 | static BOOL gWinWrapperContractRecursionBreak = FALSE; |
50 | |
51 | |
52 | class 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 | |
132 | ULONG 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. |
137 | BOOL |
138 | WszCreateProcess( |
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. ********************************************************* |
194 | bool 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 |
197 | void 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. |
225 | BOOL 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() |
262 | static 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 | |
297 | lExit: |
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 | // |
321 | DWORD 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 | |
399 | lExit: |
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 | |
429 | typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription); |
430 | extern pfnSetThreadDescription g_pfnSetThreadDescription; |
431 | |
432 | // Dummy method if windows version does not support it |
433 | HRESULT SetThreadDescriptionDummy(HANDLE hThread, PCWSTR lpThreadDescription) |
434 | { |
435 | return NOERROR; |
436 | } |
437 | |
438 | HRESULT 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 | |
461 | pfnSetThreadDescription g_pfnSetThreadDescription = &InitializeSetThreadDescription; |
462 | |
463 | // Set unmanaged thread name which will show up in ETW and Debuggers which know how to read this data. |
464 | HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription) |
465 | { |
466 | return g_pfnSetThreadDescription(hThread, lpThreadDescription); |
467 | } |
468 | |
469 | #endif //!FEATURE_PAL |
470 | |