1 | // Copyright (c) 2017-2023, The Khronos Group Inc. |
2 | // Copyright (c) 2017-2019 Valve Corporation |
3 | // Copyright (c) 2017-2019 LunarG, Inc. |
4 | // |
5 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
6 | // |
7 | // Initial Authors: Mark Young <marky@lunarg.com>, Dave Houlton <daveh@lunarg.com> |
8 | // |
9 | |
10 | #pragma once |
11 | |
12 | #include "xr_dependencies.h" |
13 | #include <string> |
14 | #include <stdint.h> |
15 | #include <stdlib.h> |
16 | |
17 | // OpenXR paths and registry key locations |
18 | #define OPENXR_RELATIVE_PATH "openxr/" |
19 | #define OPENXR_IMPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/implicit.d" |
20 | #define OPENXR_EXPLICIT_API_LAYER_RELATIVE_PATH "/api_layers/explicit.d" |
21 | #ifdef XR_OS_WINDOWS |
22 | #define OPENXR_REGISTRY_LOCATION "SOFTWARE\\Khronos\\OpenXR\\" |
23 | #define OPENXR_IMPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Implicit" |
24 | #define OPENXR_EXPLICIT_API_LAYER_REGISTRY_LOCATION "\\ApiLayers\\Explicit" |
25 | #endif |
26 | |
27 | // OpenXR Loader environment variables of interest |
28 | #define OPENXR_RUNTIME_JSON_ENV_VAR "XR_RUNTIME_JSON" |
29 | #define OPENXR_API_LAYER_PATH_ENV_VAR "XR_API_LAYER_PATH" |
30 | |
31 | // This is a CMake generated file with #defines for any functions/includes |
32 | // that it found present and build-time configuration. |
33 | // If you don't have this file, on non-Windows you'll need to define |
34 | // one of HAVE_SECURE_GETENV or HAVE___SECURE_GETENV depending on which |
35 | // of secure_getenv or __secure_getenv are present |
36 | #ifdef OPENXR_HAVE_COMMON_CONFIG |
37 | #include "common_config.h" |
38 | #endif // OPENXR_HAVE_COMMON_CONFIG |
39 | |
40 | // Consumers of this file must ensure this function is implemented. For example, the loader will implement this function so that it |
41 | // can route messages through the loader's logging system. |
42 | void LogPlatformUtilsError(const std::string& message); |
43 | |
44 | // Environment variables |
45 | #if defined(XR_OS_LINUX) || defined(XR_OS_APPLE) |
46 | |
47 | #include <unistd.h> |
48 | #include <fcntl.h> |
49 | #include <iostream> |
50 | |
51 | namespace detail { |
52 | |
53 | static inline char* ImplGetEnv(const char* name) { return getenv(name); } |
54 | |
55 | static inline int ImplSetEnv(const char* name, const char* value, int overwrite) { return setenv(name, value, overwrite); } |
56 | |
57 | static inline char* ImplGetSecureEnv(const char* name) { |
58 | #ifdef HAVE_SECURE_GETENV |
59 | return secure_getenv(name); |
60 | #elif defined(HAVE___SECURE_GETENV) |
61 | return __secure_getenv(name); |
62 | #else |
63 | // clang-format off |
64 | #pragma message( \ |
65 | "Warning: Falling back to non-secure getenv for environmental" \ |
66 | "lookups! Consider updating to a different libc.") |
67 | // clang-format on |
68 | |
69 | return ImplGetEnv(name); |
70 | #endif |
71 | } |
72 | } // namespace detail |
73 | |
74 | #endif // defined(XR_OS_LINUX) || defined(XR_OS_APPLE) |
75 | #if defined(XR_OS_LINUX) |
76 | |
77 | static inline std::string PlatformUtilsGetEnv(const char* name) { |
78 | auto str = detail::ImplGetEnv(name); |
79 | if (str == nullptr) { |
80 | return {}; |
81 | } |
82 | return str; |
83 | } |
84 | |
85 | static inline std::string PlatformUtilsGetSecureEnv(const char* name) { |
86 | auto str = detail::ImplGetSecureEnv(name); |
87 | if (str == nullptr) { |
88 | str = detail::ImplGetEnv(name); |
89 | if (str != nullptr && !std::string(str).empty()) { |
90 | LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable " ) + name + |
91 | " is being ignored due to running with secure execution. The value '" + str + |
92 | "' will NOT be used." ); |
93 | } |
94 | return {}; |
95 | } |
96 | return str; |
97 | } |
98 | |
99 | static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } |
100 | |
101 | static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { |
102 | const int shouldOverwrite = 1; |
103 | int result = detail::ImplSetEnv(name, value, shouldOverwrite); |
104 | return (result == 0); |
105 | } |
106 | |
107 | #elif defined(XR_OS_APPLE) |
108 | |
109 | static inline std::string PlatformUtilsGetEnv(const char* name) { |
110 | auto str = detail::ImplGetEnv(name); |
111 | if (str == nullptr) { |
112 | return {}; |
113 | } |
114 | return str; |
115 | } |
116 | |
117 | static inline std::string PlatformUtilsGetSecureEnv(const char* name) { |
118 | auto str = detail::ImplGetSecureEnv(name); |
119 | if (str == nullptr) { |
120 | return {}; |
121 | } |
122 | return str; |
123 | } |
124 | |
125 | static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; } |
126 | |
127 | static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { |
128 | const int shouldOverwrite = 1; |
129 | int result = detail::ImplSetEnv(name, value, shouldOverwrite); |
130 | return (result == 0); |
131 | } |
132 | |
133 | // Prefix for the Apple global runtime JSON file name |
134 | static const std::string rt_dir_prefix = "/usr/local/share/openxr/" ; |
135 | static const std::string rt_filename = "/active_runtime.json" ; |
136 | |
137 | static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { |
138 | file_name = rt_dir_prefix; |
139 | file_name += std::to_string(major_version); |
140 | file_name += rt_filename; |
141 | return true; |
142 | } |
143 | |
144 | #elif defined(XR_OS_WINDOWS) |
145 | |
146 | inline std::wstring utf8_to_wide(const std::string& utf8Text) { |
147 | if (utf8Text.empty()) { |
148 | return {}; |
149 | } |
150 | |
151 | std::wstring wideText; |
152 | const int wideLength = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), nullptr, 0); |
153 | if (wideLength == 0) { |
154 | LogPlatformUtilsError("utf8_to_wide get size error: " + std::to_string(::GetLastError())); |
155 | return {}; |
156 | } |
157 | |
158 | // MultiByteToWideChar returns number of chars of the input buffer, regardless of null terminitor |
159 | wideText.resize(wideLength, 0); |
160 | wchar_t* wideString = const_cast<wchar_t*>(wideText.data()); // mutable data() only exists in c++17 |
161 | const int length = ::MultiByteToWideChar(CP_UTF8, 0, utf8Text.data(), (int)utf8Text.size(), wideString, wideLength); |
162 | if (length != wideLength) { |
163 | LogPlatformUtilsError("utf8_to_wide convert string error: " + std::to_string(::GetLastError())); |
164 | return {}; |
165 | } |
166 | |
167 | return wideText; |
168 | } |
169 | |
170 | inline std::string wide_to_utf8(const std::wstring& wideText) { |
171 | if (wideText.empty()) { |
172 | return {}; |
173 | } |
174 | |
175 | std::string narrowText; |
176 | int narrowLength = ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), nullptr, 0, nullptr, nullptr); |
177 | if (narrowLength == 0) { |
178 | LogPlatformUtilsError("wide_to_utf8 get size error: " + std::to_string(::GetLastError())); |
179 | return {}; |
180 | } |
181 | |
182 | // WideCharToMultiByte returns number of chars of the input buffer, regardless of null terminitor |
183 | narrowText.resize(narrowLength, 0); |
184 | char* narrowString = const_cast<char*>(narrowText.data()); // mutable data() only exists in c++17 |
185 | const int length = |
186 | ::WideCharToMultiByte(CP_UTF8, 0, wideText.data(), (int)wideText.size(), narrowString, narrowLength, nullptr, nullptr); |
187 | if (length != narrowLength) { |
188 | LogPlatformUtilsError("wide_to_utf8 convert string error: " + std::to_string(::GetLastError())); |
189 | return {}; |
190 | } |
191 | |
192 | return narrowText; |
193 | } |
194 | |
195 | // Returns true if the current process has an integrity level > SECURITY_MANDATORY_MEDIUM_RID. |
196 | static inline bool IsHighIntegrityLevel() { |
197 | // Execute this check once and save the value as a static bool. |
198 | static bool isHighIntegrityLevel = ([] { |
199 | HANDLE processToken; |
200 | if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &processToken)) { |
201 | // Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD. |
202 | uint8_t mandatoryLabelBuffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)]{}; |
203 | DWORD bufferSize; |
204 | if (GetTokenInformation(processToken, TokenIntegrityLevel, mandatoryLabelBuffer, sizeof(mandatoryLabelBuffer), |
205 | &bufferSize) != 0) { |
206 | const auto mandatoryLabel = reinterpret_cast<const TOKEN_MANDATORY_LABEL*>(mandatoryLabelBuffer); |
207 | if (mandatoryLabel->Label.Sid != 0) { |
208 | const DWORD subAuthorityCount = *GetSidSubAuthorityCount(mandatoryLabel->Label.Sid); |
209 | const DWORD integrityLevel = *GetSidSubAuthority(mandatoryLabel->Label.Sid, subAuthorityCount - 1); |
210 | CloseHandle(processToken); |
211 | return integrityLevel > SECURITY_MANDATORY_MEDIUM_RID; |
212 | } |
213 | } |
214 | |
215 | CloseHandle(processToken); |
216 | } |
217 | |
218 | return false; |
219 | })(); |
220 | |
221 | return isHighIntegrityLevel; |
222 | } |
223 | |
224 | // Returns true if the given environment variable exists. |
225 | // The name is a case-sensitive UTF8 string. |
226 | static inline bool PlatformUtilsGetEnvSet(const char* name) { |
227 | const std::wstring wname = utf8_to_wide(name); |
228 | const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0); |
229 | // GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error. |
230 | return 0 != valSize; |
231 | } |
232 | |
233 | // Returns the environment variable value for the given name. |
234 | // Returns an empty string if the environment variable doesn't exist or if it exists but is empty. |
235 | // Use PlatformUtilsGetEnvSet to tell if it exists. |
236 | // The name is a case-sensitive UTF8 string. |
237 | static inline std::string PlatformUtilsGetEnv(const char* name) { |
238 | const std::wstring wname = utf8_to_wide(name); |
239 | const DWORD valSize = ::GetEnvironmentVariableW(wname.c_str(), nullptr, 0); |
240 | // GetEnvironmentVariable returns 0 when environment variable does not exist or there is an error. |
241 | // The size includes the null-terminator, so a size of 1 is means the variable was explicitly set to empty. |
242 | if (valSize == 0 || valSize == 1) { |
243 | return {}; |
244 | } |
245 | |
246 | // GetEnvironmentVariable returns size including null terminator for "query size" call. |
247 | std::wstring wValue(valSize, 0); |
248 | wchar_t* wValueData = &wValue[0]; |
249 | |
250 | // GetEnvironmentVariable returns string length, excluding null terminator for "get value" |
251 | // call if there was enough capacity. Else it returns the required capacity (including null terminator). |
252 | const DWORD length = ::GetEnvironmentVariableW(wname.c_str(), wValueData, (DWORD)wValue.size()); |
253 | if ((length == 0) || (length >= wValue.size())) { // If error or the variable increased length between calls... |
254 | LogPlatformUtilsError("GetEnvironmentVariable get value error: " + std::to_string(::GetLastError())); |
255 | return {}; |
256 | } |
257 | |
258 | wValue.resize(length); // Strip the null terminator. |
259 | |
260 | return wide_to_utf8(wValue); |
261 | } |
262 | |
263 | // Acts the same as PlatformUtilsGetEnv except returns an empty string if IsHighIntegrityLevel. |
264 | static inline std::string PlatformUtilsGetSecureEnv(const char* name) { |
265 | // No secure version for Windows so the below integrity check is needed. |
266 | const std::string envValue = PlatformUtilsGetEnv(name); |
267 | |
268 | // Do not allow high integrity processes to act on data that can be controlled by medium integrity processes. |
269 | if (IsHighIntegrityLevel()) { |
270 | if (!envValue.empty()) { |
271 | LogPlatformUtilsError(std::string("!!! WARNING !!! Environment variable " ) + name + |
272 | " is being ignored due to running from an elevated context. The value '" + envValue + |
273 | "' will NOT be used." ); |
274 | } |
275 | return {}; |
276 | } |
277 | |
278 | return envValue; |
279 | } |
280 | |
281 | // Sets an environment variable via UTF8 strings. |
282 | // The name is case-sensitive. |
283 | // Overwrites the variable if it already exists. |
284 | // Returns true if it could be set. |
285 | static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { |
286 | const std::wstring wname = utf8_to_wide(name); |
287 | const std::wstring wvalue = utf8_to_wide(value); |
288 | BOOL result = ::SetEnvironmentVariableW(wname.c_str(), wvalue.c_str()); |
289 | return (result != 0); |
290 | } |
291 | |
292 | #elif defined(XR_OS_ANDROID) |
293 | |
294 | static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { |
295 | // Stub func |
296 | return false; |
297 | } |
298 | |
299 | static inline std::string PlatformUtilsGetEnv(const char* /* name */) { |
300 | // Stub func |
301 | return {}; |
302 | } |
303 | |
304 | static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { |
305 | // Stub func |
306 | return {}; |
307 | } |
308 | |
309 | static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { |
310 | // Stub func |
311 | return false; |
312 | } |
313 | |
314 | #include <sys/stat.h> |
315 | |
316 | // Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases |
317 | static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { |
318 | // Prefix for the runtime JSON file name |
319 | static const char* rt_dir_prefixes[] = {"/product" , "/odm" , "/oem" , "/vendor" , "/system" }; |
320 | static const std::string rt_filename = "/active_runtime.json" ; |
321 | static const std::string subdir = "/etc/openxr/" ; |
322 | for (const auto prefix : rt_dir_prefixes) { |
323 | auto path = prefix + subdir + std::to_string(major_version) + rt_filename; |
324 | struct stat buf; |
325 | if (0 == stat(path.c_str(), &buf)) { |
326 | file_name = path; |
327 | return true; |
328 | } |
329 | } |
330 | return false; |
331 | } |
332 | #else // Not Linux, Apple, nor Windows |
333 | |
334 | static inline bool PlatformUtilsGetEnvSet(const char* /* name */) { |
335 | // Stub func |
336 | return false; |
337 | } |
338 | |
339 | static inline std::string PlatformUtilsGetEnv(const char* /* name */) { |
340 | // Stub func |
341 | return {}; |
342 | } |
343 | |
344 | static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) { |
345 | // Stub func |
346 | return {}; |
347 | } |
348 | |
349 | static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) { |
350 | // Stub func |
351 | return false; |
352 | } |
353 | |
354 | static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) { |
355 | // Stub func |
356 | return false; |
357 | } |
358 | |
359 | #endif |
360 | |