diff options
author | Hitendra Prajapati <hprajapati@mvista.com> | 2025-07-11 10:55:13 +0530 |
---|---|---|
committer | Armin Kuster <akuster808@gmail.com> | 2025-07-13 14:41:17 -0400 |
commit | 12375606232655c1562f69ad757d365ae711a430 (patch) | |
tree | d16e5f07f7f0f047e5fead1fe68a9a2f6fe4849f | |
parent | 9daee866d1b4567a40c7af8067f0ade213a7091d (diff) | |
download | meta-openembedded-12375606232655c1562f69ad757d365ae711a430.tar.gz |
open-vm-tools: fix CVE-2025-22247
Upstream-Status: Backport from https://github.com/vmware/open-vm-tools/blob/CVE-2025-22247.patch/CVE-2025-22247-1100-1225-VGAuth-updates.patch
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
Signed-off-by: Armin Kuster <akuster808@gmail.com>
-rw-r--r-- | meta-networking/recipes-support/open-vm-tools/open-vm-tools/CVE-2025-22247.patch | 383 | ||||
-rw-r--r-- | meta-networking/recipes-support/open-vm-tools/open-vm-tools_11.3.5.bb | 1 |
2 files changed, 384 insertions, 0 deletions
diff --git a/meta-networking/recipes-support/open-vm-tools/open-vm-tools/CVE-2025-22247.patch b/meta-networking/recipes-support/open-vm-tools/open-vm-tools/CVE-2025-22247.patch new file mode 100644 index 0000000000..e70fd9b7d7 --- /dev/null +++ b/meta-networking/recipes-support/open-vm-tools/open-vm-tools/CVE-2025-22247.patch | |||
@@ -0,0 +1,383 @@ | |||
1 | From 2a2607c6bd94ae22a937fd2adde7472d9a6d506c Mon Sep 17 00:00:00 2001 | ||
2 | From: John Wolfe <john.wolfe@broadcom.com> | ||
3 | Date: Mon, 5 May 2025 16:10:07 -0700 | ||
4 | Subject: [PATCH] Validate user names and file paths | ||
5 | |||
6 | Prevent usage of illegal characters in user names and file paths. | ||
7 | Also, disallow unexpected symlinks in file paths. | ||
8 | |||
9 | This patch contains changes to common source files not applicable | ||
10 | to open-vm-tools. | ||
11 | |||
12 | All files being updated should be consider to have the copyright to | ||
13 | be updated to: | ||
14 | |||
15 | * Copyright (c) XXXX-2025 Broadcom. All Rights Reserved. | ||
16 | * The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. | ||
17 | |||
18 | The 2025 Broadcom copyright information update is not part of this | ||
19 | patch set to allow the patch to be easily applied to previous | ||
20 | open-vm-tools source releases. | ||
21 | |||
22 | Upstream-Status: Backport [https://github.com/vmware/open-vm-tools/blob/CVE-2025-22247.patch/CVE-2025-22247-1100-1225-VGAuth-updates.patch] | ||
23 | CVE: CVE-2025-22247 | ||
24 | Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> | ||
25 | --- | ||
26 | open-vm-tools/vgauth/common/VGAuthUtil.c | 33 +++++++++ | ||
27 | open-vm-tools/vgauth/common/VGAuthUtil.h | 2 + | ||
28 | open-vm-tools/vgauth/common/prefs.h | 3 + | ||
29 | open-vm-tools/vgauth/common/usercheck.c | 28 +++++-- | ||
30 | open-vm-tools/vgauth/serviceImpl/alias.c | 74 ++++++++++++++++++- | ||
31 | open-vm-tools/vgauth/serviceImpl/service.c | 27 +++++++ | ||
32 | open-vm-tools/vgauth/serviceImpl/serviceInt.h | 1 + | ||
33 | 7 files changed, 160 insertions(+), 8 deletions(-) | ||
34 | |||
35 | diff --git a/open-vm-tools/vgauth/common/VGAuthUtil.c b/open-vm-tools/vgauth/common/VGAuthUtil.c | ||
36 | index 76383c462..9c2adb8d0 100644 | ||
37 | --- a/open-vm-tools/vgauth/common/VGAuthUtil.c | ||
38 | +++ b/open-vm-tools/vgauth/common/VGAuthUtil.c | ||
39 | @@ -309,3 +309,36 @@ Util_Assert(const char *cond, | ||
40 | #endif | ||
41 | g_assert(0); | ||
42 | } | ||
43 | + | ||
44 | + | ||
45 | +/* | ||
46 | + ****************************************************************************** | ||
47 | + * Util_Utf8CaseCmp -- */ /** | ||
48 | + * | ||
49 | + * Case insensitive comparison for utf8 strings which can have non-ascii | ||
50 | + * characters. | ||
51 | + * | ||
52 | + * @param[in] str1 Null terminated utf8 string. | ||
53 | + * @param[in] str2 Null terminated utf8 string. | ||
54 | + * | ||
55 | + ****************************************************************************** | ||
56 | + */ | ||
57 | + | ||
58 | +int | ||
59 | +Util_Utf8CaseCmp(const gchar *str1, | ||
60 | + const gchar *str2) | ||
61 | +{ | ||
62 | + int ret; | ||
63 | + gchar *str1Case; | ||
64 | + gchar *str2Case; | ||
65 | + | ||
66 | + str1Case = g_utf8_casefold(str1, -1); | ||
67 | + str2Case = g_utf8_casefold(str2, -1); | ||
68 | + | ||
69 | + ret = g_strcmp0(str1Case, str2Case); | ||
70 | + | ||
71 | + g_free(str1Case); | ||
72 | + g_free(str2Case); | ||
73 | + | ||
74 | + return ret; | ||
75 | +} | ||
76 | diff --git a/open-vm-tools/vgauth/common/VGAuthUtil.h b/open-vm-tools/vgauth/common/VGAuthUtil.h | ||
77 | index f7f3aa216..ef32a91da 100644 | ||
78 | --- a/open-vm-tools/vgauth/common/VGAuthUtil.h | ||
79 | +++ b/open-vm-tools/vgauth/common/VGAuthUtil.h | ||
80 | @@ -105,4 +105,6 @@ gboolean Util_CheckExpiration(const GTimeVal *start, unsigned int duration); | ||
81 | |||
82 | void Util_Assert(const char *cond, const char *file, int lineNum); | ||
83 | |||
84 | +int Util_Utf8CaseCmp(const gchar *str1, const gchar *str2); | ||
85 | + | ||
86 | #endif | ||
87 | diff --git a/open-vm-tools/vgauth/common/prefs.h b/open-vm-tools/vgauth/common/prefs.h | ||
88 | index 87ccc9b37..689b9e2cf 100644 | ||
89 | --- a/open-vm-tools/vgauth/common/prefs.h | ||
90 | +++ b/open-vm-tools/vgauth/common/prefs.h | ||
91 | @@ -167,6 +167,9 @@ msgCatalog = /etc/vmware-tools/vgauth/messages | ||
92 | /** Where the localized version of the messages were installed. */ | ||
93 | #define VGAUTH_PREF_LOCALIZATION_DIR "msgCatalog" | ||
94 | |||
95 | +/** If symlinks or junctions are allowed in alias store file path */ | ||
96 | +#define VGAUTH_PREF_ALLOW_SYMLINKS "allowSymlinks" | ||
97 | + | ||
98 | /* | ||
99 | * Pref values | ||
100 | */ | ||
101 | diff --git a/open-vm-tools/vgauth/common/usercheck.c b/open-vm-tools/vgauth/common/usercheck.c | ||
102 | index 31eeb5a77..145f1f056 100644 | ||
103 | --- a/open-vm-tools/vgauth/common/usercheck.c | ||
104 | +++ b/open-vm-tools/vgauth/common/usercheck.c | ||
105 | @@ -78,6 +78,8 @@ | ||
106 | * Solaris as well, but that path is untested. | ||
107 | */ | ||
108 | |||
109 | +#define MAX_USER_NAME_LEN 256 | ||
110 | + | ||
111 | /* | ||
112 | * A single retry works for the LDAP case, but try more often in case NIS | ||
113 | * or something else has a related issue. Note that a bad username/uid won't | ||
114 | @@ -354,17 +356,29 @@ Usercheck_UsernameIsLegal(const gchar *userName) | ||
115 | * | ||
116 | */ | ||
117 | size_t len; | ||
118 | -#ifdef _WIN32 | ||
119 | - // allow '\' in for Windows domain usernames | ||
120 | - char *illegalChars = "<>/"; | ||
121 | -#else | ||
122 | - char *illegalChars = "\\<>/"; | ||
123 | -#endif | ||
124 | + size_t i = 0; | ||
125 | + int backSlashCnt = 0; | ||
126 | + /* | ||
127 | + * As user names are used to generate its alias store file name/path, it | ||
128 | + * should not contain path traversal characters ('/' and '\'). | ||
129 | + */ | ||
130 | + char *illegalChars = "<>/\\"; | ||
131 | |||
132 | len = strlen(userName); | ||
133 | - if (strcspn(userName, illegalChars) != len) { | ||
134 | + if (len > MAX_USER_NAME_LEN) { | ||
135 | return FALSE; | ||
136 | } | ||
137 | + | ||
138 | + while ((i += strcspn(userName + i, illegalChars)) < len) { | ||
139 | + /* | ||
140 | + * One backward slash is allowed for domain\username separator. | ||
141 | + */ | ||
142 | + if (userName[i] != '\\' || ++backSlashCnt > 1) { | ||
143 | + return FALSE; | ||
144 | + } | ||
145 | + ++i; | ||
146 | + } | ||
147 | + | ||
148 | return TRUE; | ||
149 | } | ||
150 | |||
151 | diff --git a/open-vm-tools/vgauth/serviceImpl/alias.c b/open-vm-tools/vgauth/serviceImpl/alias.c | ||
152 | index b28351eea..687d1b373 100644 | ||
153 | --- a/open-vm-tools/vgauth/serviceImpl/alias.c | ||
154 | +++ b/open-vm-tools/vgauth/serviceImpl/alias.c | ||
155 | @@ -41,6 +41,7 @@ | ||
156 | #include "certverify.h" | ||
157 | #include "VGAuthProto.h" | ||
158 | #include "vmxlog.h" | ||
159 | +#include "VGAuthUtil.h" | ||
160 | |||
161 | // puts the identity store in an easy to find place | ||
162 | #undef WIN_TEST_MODE | ||
163 | @@ -66,6 +67,7 @@ | ||
164 | #define ALIASSTORE_FILE_PREFIX "user-" | ||
165 | #define ALIASSTORE_FILE_SUFFIX ".xml" | ||
166 | |||
167 | +static gboolean allowSymlinks = FALSE; | ||
168 | static gchar *aliasStoreRootDir = DEFAULT_ALIASSTORE_ROOT_DIR; | ||
169 | |||
170 | #ifdef _WIN32 | ||
171 | @@ -252,6 +254,12 @@ mapping file layout: | ||
172 | |||
173 | */ | ||
174 | |||
175 | +#ifdef _WIN32 | ||
176 | +#define ISPATHSEP(c) ((c) == '\\' || (c) == '/') | ||
177 | +#else | ||
178 | +#define ISPATHSEP(c) ((c) == '/') | ||
179 | +#endif | ||
180 | + | ||
181 | |||
182 | /* | ||
183 | ****************************************************************************** | ||
184 | @@ -466,6 +474,7 @@ ServiceLoadFileContentsWin(const gchar *fileName, | ||
185 | gunichar2 *fileNameW = NULL; | ||
186 | BOOL ok; | ||
187 | DWORD bytesRead; | ||
188 | + gchar *realPath = NULL; | ||
189 | |||
190 | *fileSize = 0; | ||
191 | *contents = NULL; | ||
192 | @@ -622,6 +631,22 @@ ServiceLoadFileContentsWin(const gchar *fileName, | ||
193 | goto done; | ||
194 | } | ||
195 | |||
196 | + if (!allowSymlinks) { | ||
197 | + /* | ||
198 | + * Check if fileName is real path. | ||
199 | + */ | ||
200 | + if ((realPath = ServiceFileGetPathByHandle(hFile)) == NULL) { | ||
201 | + err = VGAUTH_E_FAIL; | ||
202 | + goto done; | ||
203 | + } | ||
204 | + if (Util_Utf8CaseCmp(realPath, fileName) != 0) { | ||
205 | + Warning("%s: Real path (%s) is not same as file path (%s)\n", | ||
206 | + __FUNCTION__, realPath, fileName); | ||
207 | + err = VGAUTH_E_FAIL; | ||
208 | + goto done; | ||
209 | + } | ||
210 | + } | ||
211 | + | ||
212 | /* | ||
213 | * Now finally read the contents. | ||
214 | */ | ||
215 | @@ -650,6 +675,7 @@ done: | ||
216 | CloseHandle(hFile); | ||
217 | } | ||
218 | g_free(fileNameW); | ||
219 | + g_free(realPath); | ||
220 | |||
221 | return err; | ||
222 | } | ||
223 | @@ -672,6 +698,7 @@ ServiceLoadFileContentsPosix(const gchar *fileName, | ||
224 | gchar *buf; | ||
225 | gchar *bp; | ||
226 | int fd = -1; | ||
227 | + gchar realPath[PATH_MAX] = { 0 }; | ||
228 | |||
229 | *fileSize = 0; | ||
230 | *contents = NULL; | ||
231 | @@ -817,6 +844,23 @@ ServiceLoadFileContentsPosix(const gchar *fileName, | ||
232 | goto done; | ||
233 | } | ||
234 | |||
235 | + if (!allowSymlinks) { | ||
236 | + /* | ||
237 | + * Check if fileName is real path. | ||
238 | + */ | ||
239 | + if (realpath(fileName, realPath) == NULL) { | ||
240 | + Warning("%s: realpath() failed. errno (%d)\n", __FUNCTION__, errno); | ||
241 | + err = VGAUTH_E_FAIL; | ||
242 | + goto done; | ||
243 | + } | ||
244 | + if (g_strcmp0(realPath, fileName) != 0) { | ||
245 | + Warning("%s: Real path (%s) is not same as file path (%s)\n", | ||
246 | + __FUNCTION__, realPath, fileName); | ||
247 | + err = VGAUTH_E_FAIL; | ||
248 | + goto done; | ||
249 | + } | ||
250 | + } | ||
251 | + | ||
252 | /* | ||
253 | * All sanity checks passed; read the bits. | ||
254 | */ | ||
255 | @@ -2803,8 +2847,13 @@ ServiceAliasRemoveAlias(const gchar *reqUserName, | ||
256 | |||
257 | /* | ||
258 | * We don't verify the user exists in a Remove operation, to allow | ||
259 | - * cleanup of deleted user's stores. | ||
260 | + * cleanup of deleted user's stores, but we do check whether the | ||
261 | + * user name is legal or not. | ||
262 | */ | ||
263 | + if (!Usercheck_UsernameIsLegal(userName)) { | ||
264 | + Warning("%s: Illegal user name '%s'\n", __FUNCTION__, userName); | ||
265 | + return VGAUTH_E_FAIL; | ||
266 | + } | ||
267 | |||
268 | if (!CertVerify_IsWellFormedPEMCert(pemCert)) { | ||
269 | return VGAUTH_E_INVALID_CERTIFICATE; | ||
270 | @@ -3036,6 +3085,16 @@ ServiceAliasQueryAliases(const gchar *userName, | ||
271 | } | ||
272 | #endif | ||
273 | |||
274 | + /* | ||
275 | + * We don't verify the user exists in a Query operation to allow | ||
276 | + * cleaning up after a deleted user, but we do check whether the | ||
277 | + * user name is legal or not. | ||
278 | + */ | ||
279 | + if (!Usercheck_UsernameIsLegal(userName)) { | ||
280 | + Warning("%s: Illegal user name '%s'\n", __FUNCTION__, userName); | ||
281 | + return VGAUTH_E_FAIL; | ||
282 | + } | ||
283 | + | ||
284 | err = AliasLoadAliases(userName, num, aList); | ||
285 | if (VGAUTH_E_OK != err) { | ||
286 | Warning("%s: failed to load Aliases for '%s'\n", __FUNCTION__, userName); | ||
287 | @@ -3294,6 +3353,7 @@ ServiceAliasInitAliasStore(void) | ||
288 | VGAuthError err = VGAUTH_E_OK; | ||
289 | gboolean saveBadDir = FALSE; | ||
290 | char *defaultDir = NULL; | ||
291 | + size_t len; | ||
292 | |||
293 | #ifdef _WIN32 | ||
294 | { | ||
295 | @@ -3324,6 +3384,10 @@ ServiceAliasInitAliasStore(void) | ||
296 | defaultDir = g_strdup(DEFAULT_ALIASSTORE_ROOT_DIR); | ||
297 | #endif | ||
298 | |||
299 | + allowSymlinks = Pref_GetBool(gPrefs, | ||
300 | + VGAUTH_PREF_ALLOW_SYMLINKS, | ||
301 | + VGAUTH_PREF_GROUP_NAME_SERVICE, | ||
302 | + FALSE); | ||
303 | /* | ||
304 | * Find the alias store directory. This allows an installer to put | ||
305 | * it somewhere else if necessary. | ||
306 | @@ -3337,6 +3401,14 @@ ServiceAliasInitAliasStore(void) | ||
307 | VGAUTH_PREF_GROUP_NAME_SERVICE, | ||
308 | defaultDir); | ||
309 | |||
310 | + /* | ||
311 | + * Remove the trailing separator if any from aliasStoreRootDir path. | ||
312 | + */ | ||
313 | + len = strlen(aliasStoreRootDir); | ||
314 | + if (ISPATHSEP(aliasStoreRootDir[len - 1])) { | ||
315 | + aliasStoreRootDir[len - 1] = '\0'; | ||
316 | + } | ||
317 | + | ||
318 | Log("Using '%s' for alias store root directory\n", aliasStoreRootDir); | ||
319 | |||
320 | g_free(defaultDir); | ||
321 | diff --git a/open-vm-tools/vgauth/serviceImpl/service.c b/open-vm-tools/vgauth/serviceImpl/service.c | ||
322 | index d4716526c..e053ed0fa 100644 | ||
323 | --- a/open-vm-tools/vgauth/serviceImpl/service.c | ||
324 | +++ b/open-vm-tools/vgauth/serviceImpl/service.c | ||
325 | @@ -28,6 +28,7 @@ | ||
326 | #include "VGAuthUtil.h" | ||
327 | #ifdef _WIN32 | ||
328 | #include "winUtil.h" | ||
329 | +#include <glib.h> | ||
330 | #endif | ||
331 | |||
332 | static ServiceStartListeningForIOFunc startListeningIOFunc = NULL; | ||
333 | @@ -283,9 +284,35 @@ static gchar * | ||
334 | ServiceUserNameToPipeName(const char *userName) | ||
335 | { | ||
336 | gchar *escapedName = ServiceEncodeUserName(userName); | ||
337 | +#ifdef _WIN32 | ||
338 | + /* | ||
339 | + * Adding below pragma only in windows to suppress the compile time warning | ||
340 | + * about unavailability of g_uuid_string_random() since compiler flag | ||
341 | + * GLIB_VERSION_MAX_ALLOWED is defined to GLIB_VERSION_2_34. | ||
342 | + * TODO: Remove below pragma when GLIB_VERSION_MAX_ALLOWED is bumped up to | ||
343 | + * or greater than GLIB_VERSION_2_52. | ||
344 | + */ | ||
345 | +#pragma warning(suppress : 4996) | ||
346 | + gchar *uuidStr = g_uuid_string_random(); | ||
347 | + /* | ||
348 | + * Add a unique suffix to avoid a name collision with an existing named pipe | ||
349 | + * created by someone else (intentionally or by accident). | ||
350 | + * This is not needed for Linux; name collisions on sockets are already | ||
351 | + * avoided there since (1) file system paths to VGAuthService sockets are in | ||
352 | + * a directory that is writable only by root and (2) VGAuthService unlinks a | ||
353 | + * socket path before binding it to a newly created socket. | ||
354 | + */ | ||
355 | + gchar *pipeName = g_strdup_printf("%s-%s-%s", | ||
356 | + SERVICE_PUBLIC_PIPE_NAME, | ||
357 | + escapedName, | ||
358 | + uuidStr); | ||
359 | + | ||
360 | + g_free(uuidStr); | ||
361 | +#else | ||
362 | gchar *pipeName = g_strdup_printf("%s-%s", | ||
363 | SERVICE_PUBLIC_PIPE_NAME, | ||
364 | escapedName); | ||
365 | +#endif | ||
366 | |||
367 | g_free(escapedName); | ||
368 | return pipeName; | ||
369 | diff --git a/open-vm-tools/vgauth/serviceImpl/serviceInt.h b/open-vm-tools/vgauth/serviceImpl/serviceInt.h | ||
370 | index ef49f42c2..c37f42fa6 100644 | ||
371 | --- a/open-vm-tools/vgauth/serviceImpl/serviceInt.h | ||
372 | +++ b/open-vm-tools/vgauth/serviceImpl/serviceInt.h | ||
373 | @@ -441,6 +441,7 @@ VGAuthError ServiceFileVerifyAdminGroupOwnedByHandle(const HANDLE hFile); | ||
374 | VGAuthError ServiceFileVerifyEveryoneReadableByHandle(const HANDLE hFile); | ||
375 | VGAuthError ServiceFileVerifyUserAccessByHandle(const HANDLE hFile, | ||
376 | const char *userName); | ||
377 | +gchar *ServiceFileGetPathByHandle(HANDLE hFile); | ||
378 | #else | ||
379 | VGAuthError ServiceFileVerifyFileOwnerAndPerms(const char *fileName, | ||
380 | const char *userName, | ||
381 | -- | ||
382 | 2.49.0 | ||
383 | |||
diff --git a/meta-networking/recipes-support/open-vm-tools/open-vm-tools_11.3.5.bb b/meta-networking/recipes-support/open-vm-tools/open-vm-tools_11.3.5.bb index 762ac4c0e9..b58b3ddb90 100644 --- a/meta-networking/recipes-support/open-vm-tools/open-vm-tools_11.3.5.bb +++ b/meta-networking/recipes-support/open-vm-tools/open-vm-tools_11.3.5.bb | |||
@@ -49,6 +49,7 @@ SRC_URI = "git://github.com/vmware/open-vm-tools.git;protocol=https;branch=maste | |||
49 | file://CVE-2023-20900.patch;patchdir=.. \ | 49 | file://CVE-2023-20900.patch;patchdir=.. \ |
50 | file://CVE-2023-34058.patch;patchdir=.. \ | 50 | file://CVE-2023-34058.patch;patchdir=.. \ |
51 | file://CVE-2023-34059.patch;patchdir=.. \ | 51 | file://CVE-2023-34059.patch;patchdir=.. \ |
52 | file://CVE-2025-22247.patch;patchdir=.. \ | ||
52 | " | 53 | " |
53 | 54 | ||
54 | UPSTREAM_CHECK_GITTAGREGEX = "stable-(?P<pver>\d+(\.\d+)+)" | 55 | UPSTREAM_CHECK_GITTAGREGEX = "stable-(?P<pver>\d+(\.\d+)+)" |