summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta-networking/recipes-support/open-vm-tools/open-vm-tools/CVE-2025-22247.patch378
-rw-r--r--meta-networking/recipes-support/open-vm-tools/open-vm-tools_12.3.5.bb1
2 files changed, 379 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..4db79ef96d
--- /dev/null
+++ b/meta-networking/recipes-support/open-vm-tools/open-vm-tools/CVE-2025-22247.patch
@@ -0,0 +1,378 @@
1From 7874e572b5aac5a418551dc5e3935c1e74bf6f1f Mon Sep 17 00:00:00 2001
2From: John Wolfe <john.wolfe@broadcom.com>
3Date: Mon, 5 May 2025 15:58:03 -0700
4Subject: [PATCH] Validate user names and file paths
5
6Prevent usage of illegal characters in user names and file paths.
7Also, disallow unexpected symlinks in file paths.
8
9This patch contains changes to common source files not applicable
10to open-vm-tools.
11
12All files being updated should be consider to have the copyright to
13be 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
18The 2025 Broadcom copyright information update is not part of this
19patch set to allow the patch to be easily applied to previous
20open-vm-tools source releases.
21
22Upstream-Status: Backport [https://github.com/vmware/open-vm-tools/blob/CVE-2025-22247.patch/CVE-2025-22247-1230-1250-VGAuth-updates.patch]
23CVE: CVE-2025-22247
24Signed-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 | 23 +++++-
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(+), 3 deletions(-)
34
35diff --git a/open-vm-tools/vgauth/common/VGAuthUtil.c b/open-vm-tools/vgauth/common/VGAuthUtil.c
36index 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+}
76diff --git a/open-vm-tools/vgauth/common/VGAuthUtil.h b/open-vm-tools/vgauth/common/VGAuthUtil.h
77index 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
87diff --git a/open-vm-tools/vgauth/common/prefs.h b/open-vm-tools/vgauth/common/prefs.h
88index 6c58f3f4b..3299eb26c 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 */
101diff --git a/open-vm-tools/vgauth/common/usercheck.c b/open-vm-tools/vgauth/common/usercheck.c
102index 3beede2e8..340aa0411 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,12 +356,29 @@ Usercheck_UsernameIsLegal(const gchar *userName)
115 * restricted list for local usernames.
116 */
117 size_t len;
118- char *illegalChars = "<>/";
119+ size_t i = 0;
120+ int backSlashCnt = 0;
121+ /*
122+ * As user names are used to generate its alias store file name/path, it
123+ * should not contain path traversal characters ('/' and '\').
124+ */
125+ char *illegalChars = "<>/\\";
126
127 len = strlen(userName);
128- if (strcspn(userName, illegalChars) != len) {
129+ if (len > MAX_USER_NAME_LEN) {
130 return FALSE;
131 }
132+
133+ while ((i += strcspn(userName + i, illegalChars)) < len) {
134+ /*
135+ * One backward slash is allowed for domain\username separator.
136+ */
137+ if (userName[i] != '\\' || ++backSlashCnt > 1) {
138+ return FALSE;
139+ }
140+ ++i;
141+ }
142+
143 return TRUE;
144 }
145
146diff --git a/open-vm-tools/vgauth/serviceImpl/alias.c b/open-vm-tools/vgauth/serviceImpl/alias.c
147index 4e170202c..c7040ebff 100644
148--- a/open-vm-tools/vgauth/serviceImpl/alias.c
149+++ b/open-vm-tools/vgauth/serviceImpl/alias.c
150@@ -41,6 +41,7 @@
151 #include "certverify.h"
152 #include "VGAuthProto.h"
153 #include "vmxlog.h"
154+#include "VGAuthUtil.h"
155
156 // puts the identity store in an easy to find place
157 #undef WIN_TEST_MODE
158@@ -66,6 +67,7 @@
159 #define ALIASSTORE_FILE_PREFIX "user-"
160 #define ALIASSTORE_FILE_SUFFIX ".xml"
161
162+static gboolean allowSymlinks = FALSE;
163 static gchar *aliasStoreRootDir = DEFAULT_ALIASSTORE_ROOT_DIR;
164
165 #ifdef _WIN32
166@@ -252,6 +254,12 @@ mapping file layout:
167
168 */
169
170+#ifdef _WIN32
171+#define ISPATHSEP(c) ((c) == '\\' || (c) == '/')
172+#else
173+#define ISPATHSEP(c) ((c) == '/')
174+#endif
175+
176
177 /*
178 ******************************************************************************
179@@ -466,6 +474,7 @@ ServiceLoadFileContentsWin(const gchar *fileName,
180 gunichar2 *fileNameW = NULL;
181 BOOL ok;
182 DWORD bytesRead;
183+ gchar *realPath = NULL;
184
185 *fileSize = 0;
186 *contents = NULL;
187@@ -622,6 +631,22 @@ ServiceLoadFileContentsWin(const gchar *fileName,
188 goto done;
189 }
190
191+ if (!allowSymlinks) {
192+ /*
193+ * Check if fileName is real path.
194+ */
195+ if ((realPath = ServiceFileGetPathByHandle(hFile)) == NULL) {
196+ err = VGAUTH_E_FAIL;
197+ goto done;
198+ }
199+ if (Util_Utf8CaseCmp(realPath, fileName) != 0) {
200+ Warning("%s: Real path (%s) is not same as file path (%s)\n",
201+ __FUNCTION__, realPath, fileName);
202+ err = VGAUTH_E_FAIL;
203+ goto done;
204+ }
205+ }
206+
207 /*
208 * Now finally read the contents.
209 */
210@@ -650,6 +675,7 @@ done:
211 CloseHandle(hFile);
212 }
213 g_free(fileNameW);
214+ g_free(realPath);
215
216 return err;
217 }
218@@ -672,6 +698,7 @@ ServiceLoadFileContentsPosix(const gchar *fileName,
219 gchar *buf;
220 gchar *bp;
221 int fd = -1;
222+ gchar realPath[PATH_MAX] = { 0 };
223
224 *fileSize = 0;
225 *contents = NULL;
226@@ -817,6 +844,23 @@ ServiceLoadFileContentsPosix(const gchar *fileName,
227 goto done;
228 }
229
230+ if (!allowSymlinks) {
231+ /*
232+ * Check if fileName is real path.
233+ */
234+ if (realpath(fileName, realPath) == NULL) {
235+ Warning("%s: realpath() failed. errno (%d)\n", __FUNCTION__, errno);
236+ err = VGAUTH_E_FAIL;
237+ goto done;
238+ }
239+ if (g_strcmp0(realPath, fileName) != 0) {
240+ Warning("%s: Real path (%s) is not same as file path (%s)\n",
241+ __FUNCTION__, realPath, fileName);
242+ err = VGAUTH_E_FAIL;
243+ goto done;
244+ }
245+ }
246+
247 /*
248 * All confidence checks passed; read the bits.
249 */
250@@ -2803,8 +2847,13 @@ ServiceAliasRemoveAlias(const gchar *reqUserName,
251
252 /*
253 * We don't verify the user exists in a Remove operation, to allow
254- * cleanup of deleted user's stores.
255+ * cleanup of deleted user's stores, but we do check whether the
256+ * user name is legal or not.
257 */
258+ if (!Usercheck_UsernameIsLegal(userName)) {
259+ Warning("%s: Illegal user name '%s'\n", __FUNCTION__, userName);
260+ return VGAUTH_E_FAIL;
261+ }
262
263 if (!CertVerify_IsWellFormedPEMCert(pemCert)) {
264 return VGAUTH_E_INVALID_CERTIFICATE;
265@@ -3036,6 +3085,16 @@ ServiceAliasQueryAliases(const gchar *userName,
266 }
267 #endif
268
269+ /*
270+ * We don't verify the user exists in a Query operation to allow
271+ * cleaning up after a deleted user, but we do check whether the
272+ * user name is legal or not.
273+ */
274+ if (!Usercheck_UsernameIsLegal(userName)) {
275+ Warning("%s: Illegal user name '%s'\n", __FUNCTION__, userName);
276+ return VGAUTH_E_FAIL;
277+ }
278+
279 err = AliasLoadAliases(userName, num, aList);
280 if (VGAUTH_E_OK != err) {
281 Warning("%s: failed to load Aliases for '%s'\n", __FUNCTION__, userName);
282@@ -3294,6 +3353,7 @@ ServiceAliasInitAliasStore(void)
283 VGAuthError err = VGAUTH_E_OK;
284 gboolean saveBadDir = FALSE;
285 char *defaultDir = NULL;
286+ size_t len;
287
288 #ifdef _WIN32
289 {
290@@ -3324,6 +3384,10 @@ ServiceAliasInitAliasStore(void)
291 defaultDir = g_strdup(DEFAULT_ALIASSTORE_ROOT_DIR);
292 #endif
293
294+ allowSymlinks = Pref_GetBool(gPrefs,
295+ VGAUTH_PREF_ALLOW_SYMLINKS,
296+ VGAUTH_PREF_GROUP_NAME_SERVICE,
297+ FALSE);
298 /*
299 * Find the alias store directory. This allows an installer to put
300 * it somewhere else if necessary.
301@@ -3337,6 +3401,14 @@ ServiceAliasInitAliasStore(void)
302 VGAUTH_PREF_GROUP_NAME_SERVICE,
303 defaultDir);
304
305+ /*
306+ * Remove the trailing separator if any from aliasStoreRootDir path.
307+ */
308+ len = strlen(aliasStoreRootDir);
309+ if (ISPATHSEP(aliasStoreRootDir[len - 1])) {
310+ aliasStoreRootDir[len - 1] = '\0';
311+ }
312+
313 Log("Using '%s' for alias store root directory\n", aliasStoreRootDir);
314
315 g_free(defaultDir);
316diff --git a/open-vm-tools/vgauth/serviceImpl/service.c b/open-vm-tools/vgauth/serviceImpl/service.c
317index d4716526c..e053ed0fa 100644
318--- a/open-vm-tools/vgauth/serviceImpl/service.c
319+++ b/open-vm-tools/vgauth/serviceImpl/service.c
320@@ -28,6 +28,7 @@
321 #include "VGAuthUtil.h"
322 #ifdef _WIN32
323 #include "winUtil.h"
324+#include <glib.h>
325 #endif
326
327 static ServiceStartListeningForIOFunc startListeningIOFunc = NULL;
328@@ -283,9 +284,35 @@ static gchar *
329 ServiceUserNameToPipeName(const char *userName)
330 {
331 gchar *escapedName = ServiceEncodeUserName(userName);
332+#ifdef _WIN32
333+ /*
334+ * Adding below pragma only in windows to suppress the compile time warning
335+ * about unavailability of g_uuid_string_random() since compiler flag
336+ * GLIB_VERSION_MAX_ALLOWED is defined to GLIB_VERSION_2_34.
337+ * TODO: Remove below pragma when GLIB_VERSION_MAX_ALLOWED is bumped up to
338+ * or greater than GLIB_VERSION_2_52.
339+ */
340+#pragma warning(suppress : 4996)
341+ gchar *uuidStr = g_uuid_string_random();
342+ /*
343+ * Add a unique suffix to avoid a name collision with an existing named pipe
344+ * created by someone else (intentionally or by accident).
345+ * This is not needed for Linux; name collisions on sockets are already
346+ * avoided there since (1) file system paths to VGAuthService sockets are in
347+ * a directory that is writable only by root and (2) VGAuthService unlinks a
348+ * socket path before binding it to a newly created socket.
349+ */
350+ gchar *pipeName = g_strdup_printf("%s-%s-%s",
351+ SERVICE_PUBLIC_PIPE_NAME,
352+ escapedName,
353+ uuidStr);
354+
355+ g_free(uuidStr);
356+#else
357 gchar *pipeName = g_strdup_printf("%s-%s",
358 SERVICE_PUBLIC_PIPE_NAME,
359 escapedName);
360+#endif
361
362 g_free(escapedName);
363 return pipeName;
364diff --git a/open-vm-tools/vgauth/serviceImpl/serviceInt.h b/open-vm-tools/vgauth/serviceImpl/serviceInt.h
365index 5f420192b..f4f88547d 100644
366--- a/open-vm-tools/vgauth/serviceImpl/serviceInt.h
367+++ b/open-vm-tools/vgauth/serviceImpl/serviceInt.h
368@@ -441,6 +441,7 @@ VGAuthError ServiceFileVerifyAdminGroupOwnedByHandle(const HANDLE hFile);
369 VGAuthError ServiceFileVerifyEveryoneReadableByHandle(const HANDLE hFile);
370 VGAuthError ServiceFileVerifyUserAccessByHandle(const HANDLE hFile,
371 const char *userName);
372+gchar *ServiceFileGetPathByHandle(HANDLE hFile);
373 #else
374 VGAuthError ServiceFileVerifyFileOwnerAndPerms(const char *fileName,
375 const char *userName,
376--
3772.49.0
378
diff --git a/meta-networking/recipes-support/open-vm-tools/open-vm-tools_12.3.5.bb b/meta-networking/recipes-support/open-vm-tools/open-vm-tools_12.3.5.bb
index 82aab051f1..34ac34ba20 100644
--- a/meta-networking/recipes-support/open-vm-tools/open-vm-tools_12.3.5.bb
+++ b/meta-networking/recipes-support/open-vm-tools/open-vm-tools_12.3.5.bb
@@ -43,6 +43,7 @@ SRC_URI = "git://github.com/vmware/open-vm-tools.git;protocol=https;branch=stabl
43 file://0012-hgfsServerLinux-Consider-64bit-time_t-possibility.patch;patchdir=.. \ 43 file://0012-hgfsServerLinux-Consider-64bit-time_t-possibility.patch;patchdir=.. \
44 file://0013-open-vm-tools-Correct-include-path-for-poll.h.patch;patchdir=.. \ 44 file://0013-open-vm-tools-Correct-include-path-for-poll.h.patch;patchdir=.. \
45 file://0014-timeSync-Portable-way-to-print-64bit-time_t.patch;patchdir=.. \ 45 file://0014-timeSync-Portable-way-to-print-64bit-time_t.patch;patchdir=.. \
46 file://CVE-2025-22247.patch;patchdir=.. \
46 " 47 "
47 48
48UPSTREAM_CHECK_GITTAGREGEX = "stable-(?P<pver>\d+(\.\d+)+)" 49UPSTREAM_CHECK_GITTAGREGEX = "stable-(?P<pver>\d+(\.\d+)+)"