1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2016 - 2022, Steve Holme, <[email protected]>.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#if defined(WIN32)
28
29#include <curl/curl.h>
30#include "version_win32.h"
31#include "warnless.h"
32
33/* The last #include files should be: */
34#include "curl_memory.h"
35#include "memdebug.h"
36
37/* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW)
38 and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */
39struct OUR_OSVERSIONINFOEXW {
40 ULONG dwOSVersionInfoSize;
41 ULONG dwMajorVersion;
42 ULONG dwMinorVersion;
43 ULONG dwBuildNumber;
44 ULONG dwPlatformId;
45 WCHAR szCSDVersion[128];
46 USHORT wServicePackMajor;
47 USHORT wServicePackMinor;
48 USHORT wSuiteMask;
49 UCHAR wProductType;
50 UCHAR wReserved;
51};
52
53/*
54 * curlx_verify_windows_version()
55 *
56 * This is used to verify if we are running on a specific windows version.
57 *
58 * Parameters:
59 *
60 * majorVersion [in] - The major version number.
61 * minorVersion [in] - The minor version number.
62 * buildVersion [in] - The build version number. If 0, this parameter is
63 * ignored.
64 * platform [in] - The optional platform identifier.
65 * condition [in] - The test condition used to specifier whether we are
66 * checking a version less then, equal to or greater than
67 * what is specified in the major and minor version
68 * numbers.
69 *
70 * Returns TRUE if matched; otherwise FALSE.
71 */
72bool curlx_verify_windows_version(const unsigned int majorVersion,
73 const unsigned int minorVersion,
74 const unsigned int buildVersion,
75 const PlatformIdentifier platform,
76 const VersionCondition condition)
77{
78 bool matched = FALSE;
79
80#if defined(CURL_WINDOWS_APP)
81 (void)buildVersion;
82
83 /* We have no way to determine the Windows version from Windows apps,
84 so let's assume we're running on the target Windows version. */
85 const WORD fullVersion = MAKEWORD(minorVersion, majorVersion);
86 const WORD targetVersion = (WORD)_WIN32_WINNT;
87
88 switch(condition) {
89 case VERSION_LESS_THAN:
90 matched = targetVersion < fullVersion;
91 break;
92
93 case VERSION_LESS_THAN_EQUAL:
94 matched = targetVersion <= fullVersion;
95 break;
96
97 case VERSION_EQUAL:
98 matched = targetVersion == fullVersion;
99 break;
100
101 case VERSION_GREATER_THAN_EQUAL:
102 matched = targetVersion >= fullVersion;
103 break;
104
105 case VERSION_GREATER_THAN:
106 matched = targetVersion > fullVersion;
107 break;
108 }
109
110 if(matched && (platform == PLATFORM_WINDOWS)) {
111 /* we're always running on PLATFORM_WINNT */
112 matched = FALSE;
113 }
114#elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \
115 (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
116 OSVERSIONINFO osver;
117
118 memset(&osver, 0, sizeof(osver));
119 osver.dwOSVersionInfoSize = sizeof(osver);
120
121 /* Find out Windows version */
122 if(GetVersionEx(&osver)) {
123 /* Verify the Operating System version number */
124 switch(condition) {
125 case VERSION_LESS_THAN:
126 if(osver.dwMajorVersion < majorVersion ||
127 (osver.dwMajorVersion == majorVersion &&
128 osver.dwMinorVersion < minorVersion) ||
129 (buildVersion != 0 &&
130 (osver.dwMajorVersion == majorVersion &&
131 osver.dwMinorVersion == minorVersion &&
132 osver.dwBuildNumber < buildVersion)))
133 matched = TRUE;
134 break;
135
136 case VERSION_LESS_THAN_EQUAL:
137 if(osver.dwMajorVersion < majorVersion ||
138 (osver.dwMajorVersion == majorVersion &&
139 osver.dwMinorVersion < minorVersion) ||
140 (osver.dwMajorVersion == majorVersion &&
141 osver.dwMinorVersion == minorVersion &&
142 (buildVersion == 0 ||
143 osver.dwBuildNumber <= buildVersion)))
144 matched = TRUE;
145 break;
146
147 case VERSION_EQUAL:
148 if(osver.dwMajorVersion == majorVersion &&
149 osver.dwMinorVersion == minorVersion &&
150 (buildVersion == 0 ||
151 osver.dwBuildNumber == buildVersion))
152 matched = TRUE;
153 break;
154
155 case VERSION_GREATER_THAN_EQUAL:
156 if(osver.dwMajorVersion > majorVersion ||
157 (osver.dwMajorVersion == majorVersion &&
158 osver.dwMinorVersion > minorVersion) ||
159 (osver.dwMajorVersion == majorVersion &&
160 osver.dwMinorVersion == minorVersion &&
161 (buildVersion == 0 ||
162 osver.dwBuildNumber >= buildVersion)))
163 matched = TRUE;
164 break;
165
166 case VERSION_GREATER_THAN:
167 if(osver.dwMajorVersion > majorVersion ||
168 (osver.dwMajorVersion == majorVersion &&
169 osver.dwMinorVersion > minorVersion) ||
170 (buildVersion != 0 &&
171 (osver.dwMajorVersion == majorVersion &&
172 osver.dwMinorVersion == minorVersion &&
173 osver.dwBuildNumber > buildVersion)))
174 matched = TRUE;
175 break;
176 }
177
178 /* Verify the platform identifier (if necessary) */
179 if(matched) {
180 switch(platform) {
181 case PLATFORM_WINDOWS:
182 if(osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
183 matched = FALSE;
184 break;
185
186 case PLATFORM_WINNT:
187 if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
188 matched = FALSE;
189 break;
190
191 default: /* like platform == PLATFORM_DONT_CARE */
192 break;
193 }
194 }
195 }
196#else
197 ULONGLONG cm = 0;
198 struct OUR_OSVERSIONINFOEXW osver;
199 BYTE majorCondition;
200 BYTE minorCondition;
201 BYTE buildCondition;
202 BYTE spMajorCondition;
203 BYTE spMinorCondition;
204 DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION |
205 VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
206
207 typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN)
208 (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG);
209 static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo;
210 static bool onetime = true; /* safe because first call is during init */
211
212 if(onetime) {
213 pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN,
214 (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo")));
215 onetime = false;
216 }
217
218 switch(condition) {
219 case VERSION_LESS_THAN:
220 majorCondition = VER_LESS;
221 minorCondition = VER_LESS;
222 buildCondition = VER_LESS;
223 spMajorCondition = VER_LESS_EQUAL;
224 spMinorCondition = VER_LESS_EQUAL;
225 break;
226
227 case VERSION_LESS_THAN_EQUAL:
228 majorCondition = VER_LESS_EQUAL;
229 minorCondition = VER_LESS_EQUAL;
230 buildCondition = VER_LESS_EQUAL;
231 spMajorCondition = VER_LESS_EQUAL;
232 spMinorCondition = VER_LESS_EQUAL;
233 break;
234
235 case VERSION_EQUAL:
236 majorCondition = VER_EQUAL;
237 minorCondition = VER_EQUAL;
238 buildCondition = VER_EQUAL;
239 spMajorCondition = VER_GREATER_EQUAL;
240 spMinorCondition = VER_GREATER_EQUAL;
241 break;
242
243 case VERSION_GREATER_THAN_EQUAL:
244 majorCondition = VER_GREATER_EQUAL;
245 minorCondition = VER_GREATER_EQUAL;
246 buildCondition = VER_GREATER_EQUAL;
247 spMajorCondition = VER_GREATER_EQUAL;
248 spMinorCondition = VER_GREATER_EQUAL;
249 break;
250
251 case VERSION_GREATER_THAN:
252 majorCondition = VER_GREATER;
253 minorCondition = VER_GREATER;
254 buildCondition = VER_GREATER;
255 spMajorCondition = VER_GREATER_EQUAL;
256 spMinorCondition = VER_GREATER_EQUAL;
257 break;
258
259 default:
260 return FALSE;
261 }
262
263 memset(&osver, 0, sizeof(osver));
264 osver.dwOSVersionInfoSize = sizeof(osver);
265 osver.dwMajorVersion = majorVersion;
266 osver.dwMinorVersion = minorVersion;
267 osver.dwBuildNumber = buildVersion;
268 if(platform == PLATFORM_WINDOWS)
269 osver.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
270 else if(platform == PLATFORM_WINNT)
271 osver.dwPlatformId = VER_PLATFORM_WIN32_NT;
272
273 cm = VerSetConditionMask(cm, VER_MAJORVERSION, majorCondition);
274 cm = VerSetConditionMask(cm, VER_MINORVERSION, minorCondition);
275 cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, spMajorCondition);
276 cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, spMinorCondition);
277
278 if(platform != PLATFORM_DONT_CARE) {
279 cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL);
280 dwTypeMask |= VER_PLATFORMID;
281 }
282
283 /* Later versions of Windows have version functions that may not return the
284 real version of Windows unless the application is so manifested. We prefer
285 the real version always, so we use the Rtl variant of the function when
286 possible. Note though the function signatures have underlying fundamental
287 types that are the same, the return values are different. */
288 if(pRtlVerifyVersionInfo)
289 matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
290 else
291 matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm);
292
293 /* Compare the build number separately. VerifyVersionInfo normally compares
294 major.minor in hierarchical order (eg 1.9 is less than 2.0) but does not
295 do the same for build (eg 1.9 build 222 is not less than 2.0 build 111).
296 Build comparison is only needed when build numbers are equal (eg 1.9 is
297 always less than 2.0 so build comparison is not needed). */
298 if(matched && buildVersion &&
299 (condition == VERSION_EQUAL ||
300 ((condition == VERSION_GREATER_THAN_EQUAL ||
301 condition == VERSION_LESS_THAN_EQUAL) &&
302 curlx_verify_windows_version(majorVersion, minorVersion, 0,
303 platform, VERSION_EQUAL)))) {
304
305 cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition);
306 dwTypeMask = VER_BUILDNUMBER;
307 if(pRtlVerifyVersionInfo)
308 matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
309 else
310 matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver,
311 dwTypeMask, cm);
312 }
313
314#endif
315
316 return matched;
317}
318
319#endif /* WIN32 */
320