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+)+)" |
