Skip to content

Commit fa8edb8

Browse files
committed
Merge tag 'v2.35.2.windows.1'
Git for Windows v2.35.2 Changes since Git for Windows v2.35.1(2) (February 1st 2022) This version addresses CVE-2022-24765 and CVE-2022-24767. New Features * Comes with Git v2.35.2. Bug Fixes * The uninstaller was hardened to avoid a vulnerability when running under the SYSTEM account, addressing CVE-2022-24767. Signed-off-by: Victoria Dye <[email protected]>
2 parents 00388b6 + 518ccba commit fa8edb8

14 files changed

+309
-12
lines changed

Documentation/RelNotes/2.30.3.txt

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Git v2.30.2 Release Notes
2+
=========================
3+
4+
This release addresses the security issue CVE-2022-24765.
5+
6+
Fixes since v2.30.2
7+
-------------------
8+
9+
* Build fix on Windows.
10+
11+
* Fix `GIT_CEILING_DIRECTORIES` with Windows-style root directories.
12+
13+
* CVE-2022-24765:
14+
On multi-user machines, Git users might find themselves
15+
unexpectedly in a Git worktree, e.g. when another user created a
16+
repository in `C:\.git`, in a mounted network drive or in a
17+
scratch space. Merely having a Git-aware prompt that runs `git
18+
status` (or `git diff`) and navigating to a directory which is
19+
supposedly not a Git worktree, or opening such a directory in an
20+
editor or IDE such as VS Code or Atom, will potentially run
21+
commands defined by that other user.
22+
23+
Credit for finding this vulnerability goes to 俞晨东; The fix was
24+
authored by Johannes Schindelin.

Documentation/RelNotes/2.31.2.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Git v2.31.2 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.30.3 to address
5+
the security issue CVE-2022-24765; see the release notes for that
6+
version for details.

Documentation/RelNotes/2.32.1.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Git v2.32.1 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.30.3 and
5+
v2.31.2 to address the security issue CVE-2022-24765; see the
6+
release notes for these versions for details.

Documentation/RelNotes/2.33.2.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Git v2.33.2 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.30.3, v2.31.2
5+
and v2.32.1 to address the security issue CVE-2022-24765; see
6+
the release notes for these versions for details.
7+
8+
In addition, it contains the following fixes:
9+
10+
* Squelch over-eager warning message added during this cycle.
11+
12+
* A bug in "git rebase -r" has been fixed.
13+
14+
* One CI task based on Fedora image noticed a not-quite-kosher
15+
construct recently, which has been corrected.

Documentation/RelNotes/2.34.2.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Git v2.34.2 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.30.3, v2.31.2,
5+
v2.32.1 and v2.33.2 to address the security issue CVE-2022-24765;
6+
see the release notes for these versions for details.

Documentation/RelNotes/2.35.2.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Git v2.35.2 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.30.3,
5+
v2.31.2, v2.32.1, v2.33.2 and v2.34.2 to address the security
6+
issue CVE-2022-24765; see the release notes for these versions
7+
for details.

Documentation/config.txt

+2
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,8 @@ include::config/repack.txt[]
495495

496496
include::config/rerere.txt[]
497497

498+
include::config/safe.txt[]
499+
498500
include::config/sendemail.txt[]
499501

500502
include::config/sendpack.txt[]

Documentation/config/safe.txt

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
safe.directory::
2+
These config entries specify Git-tracked directories that are
3+
considered safe even if they are owned by someone other than the
4+
current user. By default, Git will refuse to even parse a Git
5+
config of a repository owned by someone else, let alone run its
6+
hooks, and this config setting allows users to specify exceptions,
7+
e.g. for intentionally shared repositories (see the `--shared`
8+
option in linkgit:git-init[1]).
9+
+
10+
This is a multi-valued setting, i.e. you can add more than one directory
11+
via `git config --add`. To reset the list of safe directories (e.g. to
12+
override any such directories specified in the system config), add a
13+
`safe.directory` entry with an empty value.
14+
+
15+
This config setting is only respected when specified in a system or global
16+
config, not when it is specified in a repository config or via the command
17+
line option `-c safe.directory=<path>`.
18+
+
19+
The value of this setting is interpolated, i.e. `~/<path>` expands to a
20+
path relative to the home directory and `%(prefix)/<path>` expands to a
21+
path relative to Git's (runtime) prefix.
22+
+
23+
Due to the permission model on Windows where ACLs are used instead of
24+
Unix' simpler permission model, it can be a bit tricky to figure out why
25+
a directory is considered unsafe. To help with this, Git will provide
26+
more detailed information when the environment variable
27+
`GIT_TEST_DEBUG_UNSAFE_DIRECTORIES` is set to `true`.

compat/mingw.c

+118
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "../git-compat-util.h"
22
#include "win32.h"
3+
#include <aclapi.h>
4+
#include <sddl.h>
35
#include <conio.h>
46
#include <wchar.h>
57
#include <winioctl.h>
@@ -3446,6 +3448,122 @@ static void setup_windows_environment(void)
34463448
has_symlinks = 0;
34473449
}
34483450

3451+
static PSID get_current_user_sid(void)
3452+
{
3453+
HANDLE token;
3454+
DWORD len = 0;
3455+
PSID result = NULL;
3456+
3457+
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
3458+
return NULL;
3459+
3460+
if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
3461+
TOKEN_USER *info = xmalloc((size_t)len);
3462+
if (GetTokenInformation(token, TokenUser, info, len, &len)) {
3463+
len = GetLengthSid(info->User.Sid);
3464+
result = xmalloc(len);
3465+
if (!CopySid(len, result, info->User.Sid)) {
3466+
error(_("failed to copy SID (%ld)"),
3467+
GetLastError());
3468+
FREE_AND_NULL(result);
3469+
}
3470+
}
3471+
FREE_AND_NULL(info);
3472+
}
3473+
CloseHandle(token);
3474+
3475+
return result;
3476+
}
3477+
3478+
int is_path_owned_by_current_sid(const char *path)
3479+
{
3480+
WCHAR wpath[MAX_PATH];
3481+
PSID sid = NULL;
3482+
PSECURITY_DESCRIPTOR descriptor = NULL;
3483+
DWORD err;
3484+
3485+
static wchar_t home[MAX_PATH];
3486+
3487+
int result = 0;
3488+
3489+
if (xutftowcs_path(wpath, path) < 0)
3490+
return 0;
3491+
3492+
/*
3493+
* On Windows, the home directory is owned by the administrator, but for
3494+
* all practical purposes, it belongs to the user. Do pretend that it is
3495+
* owned by the user.
3496+
*/
3497+
if (!*home) {
3498+
DWORD size = ARRAY_SIZE(home);
3499+
DWORD len = GetEnvironmentVariableW(L"HOME", home, size);
3500+
if (!len || len > size)
3501+
wcscpy(home, L"::N/A::");
3502+
}
3503+
if (!wcsicmp(wpath, home))
3504+
return 1;
3505+
3506+
/* Get the owner SID */
3507+
err = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT,
3508+
OWNER_SECURITY_INFORMATION |
3509+
DACL_SECURITY_INFORMATION,
3510+
&sid, NULL, NULL, NULL, &descriptor);
3511+
3512+
if (err != ERROR_SUCCESS)
3513+
error(_("failed to get owner for '%s' (%ld)"), path, err);
3514+
else if (sid && IsValidSid(sid)) {
3515+
/* Now, verify that the SID matches the current user's */
3516+
static PSID current_user_sid;
3517+
BOOL is_member;
3518+
3519+
if (!current_user_sid)
3520+
current_user_sid = get_current_user_sid();
3521+
3522+
if (current_user_sid &&
3523+
IsValidSid(current_user_sid) &&
3524+
EqualSid(sid, current_user_sid))
3525+
result = 1;
3526+
else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) &&
3527+
CheckTokenMembership(NULL, sid, &is_member) &&
3528+
is_member)
3529+
/*
3530+
* If owned by the Administrators group, and the
3531+
* current user is an administrator, we consider that
3532+
* okay, too.
3533+
*/
3534+
result = 1;
3535+
else if (git_env_bool("GIT_TEST_DEBUG_UNSAFE_DIRECTORIES", 0)) {
3536+
LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;
3537+
3538+
if (ConvertSidToStringSidA(sid, &str1))
3539+
to_free1 = str1;
3540+
else
3541+
str1 = "(inconvertible)";
3542+
3543+
if (!current_user_sid)
3544+
str2 = "(none)";
3545+
else if (!IsValidSid(current_user_sid))
3546+
str2 = "(invalid)";
3547+
else if (ConvertSidToStringSidA(current_user_sid, &str2))
3548+
to_free2 = str2;
3549+
else
3550+
str2 = "(inconvertible)";
3551+
warning("'%s' is owned by:\n\t'%s'\nbut the current user is:\n\t'%s'", path, str1, str2);
3552+
LocalFree(to_free1);
3553+
LocalFree(to_free2);
3554+
}
3555+
}
3556+
3557+
/*
3558+
* We can release the security descriptor struct only now because `sid`
3559+
* actually points into this struct.
3560+
*/
3561+
if (descriptor)
3562+
LocalFree(descriptor);
3563+
3564+
return result;
3565+
}
3566+
34493567
int is_valid_win32_path(const char *path, int allow_literal_nul)
34503568
{
34513569
const char *p = path;

compat/mingw.h

+7
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,13 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path);
488488
#include <inttypes.h>
489489
#endif
490490

491+
/**
492+
* Verifies that the specified path is owned by the user running the
493+
* current process.
494+
*/
495+
int is_path_owned_by_current_sid(const char *path);
496+
#define is_path_owned_by_current_user is_path_owned_by_current_sid
497+
491498
/**
492499
* Verifies that the given path is a valid one on Windows.
493500
*

git-compat-util.h

+12
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,18 @@ static inline int git_offset_1st_component(const char *path)
438438
#define is_valid_path(path) 1
439439
#endif
440440

441+
#ifndef is_path_owned_by_current_user
442+
static inline int is_path_owned_by_current_uid(const char *path)
443+
{
444+
struct stat st;
445+
if (lstat(path, &st))
446+
return 0;
447+
return st.st_uid == geteuid();
448+
}
449+
450+
#define is_path_owned_by_current_user is_path_owned_by_current_uid
451+
#endif
452+
441453
#ifndef find_last_dir_sep
442454
static inline char *git_find_last_dir_sep(const char *path)
443455
{

path.c

+9-5
Original file line numberDiff line numberDiff line change
@@ -1231,11 +1231,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes)
12311231
const char *ceil = prefixes->items[i].string;
12321232
int len = strlen(ceil);
12331233

1234-
if (len == 1 && ceil[0] == '/')
1235-
len = 0; /* root matches anything, with length 0 */
1236-
else if (!strncmp(path, ceil, len) && path[len] == '/')
1237-
; /* match of length len */
1238-
else
1234+
/*
1235+
* For root directories (`/`, `C:/`, `//server/share/`)
1236+
* adjust the length to exclude the trailing slash.
1237+
*/
1238+
if (len > 0 && ceil[len - 1] == '/')
1239+
len--;
1240+
1241+
if (strncmp(path, ceil, len) ||
1242+
path[len] != '/' || !path[len + 1])
12391243
continue; /* no match */
12401244

12411245
if (len > max_len)

setup.c

+56-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "string-list.h"
66
#include "chdir-notify.h"
77
#include "promisor-remote.h"
8+
#include "quote.h"
89

910
static int inside_git_dir = -1;
1011
static int inside_work_tree = -1;
@@ -1090,6 +1091,42 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
10901091
}
10911092
}
10921093

1094+
struct safe_directory_data {
1095+
const char *path;
1096+
int is_safe;
1097+
};
1098+
1099+
static int safe_directory_cb(const char *key, const char *value, void *d)
1100+
{
1101+
struct safe_directory_data *data = d;
1102+
1103+
if (!value || !*value)
1104+
data->is_safe = 0;
1105+
else {
1106+
const char *interpolated = NULL;
1107+
1108+
if (!git_config_pathname(&interpolated, key, value) &&
1109+
!fspathcmp(data->path, interpolated ? interpolated : value))
1110+
data->is_safe = 1;
1111+
1112+
free((char *)interpolated);
1113+
}
1114+
1115+
return 0;
1116+
}
1117+
1118+
static int ensure_valid_ownership(const char *path)
1119+
{
1120+
struct safe_directory_data data = { .path = path };
1121+
1122+
if (is_path_owned_by_current_user(path))
1123+
return 1;
1124+
1125+
read_very_early_config(safe_directory_cb, &data);
1126+
1127+
return data.is_safe;
1128+
}
1129+
10931130
enum discovery_result {
10941131
GIT_DIR_NONE = 0,
10951132
GIT_DIR_EXPLICIT,
@@ -1098,7 +1135,8 @@ enum discovery_result {
10981135
/* these are errors */
10991136
GIT_DIR_HIT_CEILING = -1,
11001137
GIT_DIR_HIT_MOUNT_POINT = -2,
1101-
GIT_DIR_INVALID_GITFILE = -3
1138+
GIT_DIR_INVALID_GITFILE = -3,
1139+
GIT_DIR_INVALID_OWNERSHIP = -4
11021140
};
11031141

11041142
/*
@@ -1188,11 +1226,15 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
11881226
}
11891227
strbuf_setlen(dir, offset);
11901228
if (gitdirenv) {
1229+
if (!ensure_valid_ownership(dir->buf))
1230+
return GIT_DIR_INVALID_OWNERSHIP;
11911231
strbuf_addstr(gitdir, gitdirenv);
11921232
return GIT_DIR_DISCOVERED;
11931233
}
11941234

11951235
if (is_git_directory(dir->buf)) {
1236+
if (!ensure_valid_ownership(dir->buf))
1237+
return GIT_DIR_INVALID_OWNERSHIP;
11961238
strbuf_addstr(gitdir, ".");
11971239
return GIT_DIR_BARE;
11981240
}
@@ -1324,6 +1366,19 @@ const char *setup_git_directory_gently(int *nongit_ok)
13241366
dir.buf);
13251367
*nongit_ok = 1;
13261368
break;
1369+
case GIT_DIR_INVALID_OWNERSHIP:
1370+
if (!nongit_ok) {
1371+
struct strbuf quoted = STRBUF_INIT;
1372+
1373+
sq_quote_buf_pretty(&quoted, dir.buf);
1374+
die(_("unsafe repository ('%s' is owned by someone else)\n"
1375+
"To add an exception for this directory, call:\n"
1376+
"\n"
1377+
"\tgit config --global --add safe.directory %s"),
1378+
dir.buf, quoted.buf);
1379+
}
1380+
*nongit_ok = 1;
1381+
break;
13271382
case GIT_DIR_NONE:
13281383
/*
13291384
* As a safeguard against setup_git_directory_gently_1 returning

0 commit comments

Comments
 (0)