Skip to content

Commit

Permalink
Merge tag 'v2.35.2.windows.1'
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
vdye committed Apr 12, 2022
2 parents 00388b6 + 518ccba commit fa8edb8
Show file tree
Hide file tree
Showing 14 changed files with 309 additions and 12 deletions.
24 changes: 24 additions & 0 deletions Documentation/RelNotes/2.30.3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Git v2.30.2 Release Notes
=========================

This release addresses the security issue CVE-2022-24765.

Fixes since v2.30.2
-------------------

* Build fix on Windows.

* Fix `GIT_CEILING_DIRECTORIES` with Windows-style root directories.

* CVE-2022-24765:
On multi-user machines, Git users might find themselves
unexpectedly in a Git worktree, e.g. when another user created a
repository in `C:\.git`, in a mounted network drive or in a
scratch space. Merely having a Git-aware prompt that runs `git
status` (or `git diff`) and navigating to a directory which is
supposedly not a Git worktree, or opening such a directory in an
editor or IDE such as VS Code or Atom, will potentially run
commands defined by that other user.

Credit for finding this vulnerability goes to 俞晨东; The fix was
authored by Johannes Schindelin.
6 changes: 6 additions & 0 deletions Documentation/RelNotes/2.31.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Git v2.31.2 Release Notes
=========================

This release merges up the fixes that appear in v2.30.3 to address
the security issue CVE-2022-24765; see the release notes for that
version for details.
6 changes: 6 additions & 0 deletions Documentation/RelNotes/2.32.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Git v2.32.1 Release Notes
=========================

This release merges up the fixes that appear in v2.30.3 and
v2.31.2 to address the security issue CVE-2022-24765; see the
release notes for these versions for details.
15 changes: 15 additions & 0 deletions Documentation/RelNotes/2.33.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Git v2.33.2 Release Notes
=========================

This release merges up the fixes that appear in v2.30.3, v2.31.2
and v2.32.1 to address the security issue CVE-2022-24765; see
the release notes for these versions for details.

In addition, it contains the following fixes:

* Squelch over-eager warning message added during this cycle.

* A bug in "git rebase -r" has been fixed.

* One CI task based on Fedora image noticed a not-quite-kosher
construct recently, which has been corrected.
6 changes: 6 additions & 0 deletions Documentation/RelNotes/2.34.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Git v2.34.2 Release Notes
=========================

This release merges up the fixes that appear in v2.30.3, v2.31.2,
v2.32.1 and v2.33.2 to address the security issue CVE-2022-24765;
see the release notes for these versions for details.
7 changes: 7 additions & 0 deletions Documentation/RelNotes/2.35.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Git v2.35.2 Release Notes
=========================

This release merges up the fixes that appear in v2.30.3,
v2.31.2, v2.32.1, v2.33.2 and v2.34.2 to address the security
issue CVE-2022-24765; see the release notes for these versions
for details.
2 changes: 2 additions & 0 deletions Documentation/config.txt
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,8 @@ include::config/repack.txt[]

include::config/rerere.txt[]

include::config/safe.txt[]

include::config/sendemail.txt[]

include::config/sendpack.txt[]
Expand Down
27 changes: 27 additions & 0 deletions Documentation/config/safe.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
safe.directory::
These config entries specify Git-tracked directories that are
considered safe even if they are owned by someone other than the
current user. By default, Git will refuse to even parse a Git
config of a repository owned by someone else, let alone run its
hooks, and this config setting allows users to specify exceptions,
e.g. for intentionally shared repositories (see the `--shared`
option in linkgit:git-init[1]).
+
This is a multi-valued setting, i.e. you can add more than one directory
via `git config --add`. To reset the list of safe directories (e.g. to
override any such directories specified in the system config), add a
`safe.directory` entry with an empty value.
+
This config setting is only respected when specified in a system or global
config, not when it is specified in a repository config or via the command
line option `-c safe.directory=<path>`.
+
The value of this setting is interpolated, i.e. `~/<path>` expands to a
path relative to the home directory and `%(prefix)/<path>` expands to a
path relative to Git's (runtime) prefix.
+
Due to the permission model on Windows where ACLs are used instead of
Unix' simpler permission model, it can be a bit tricky to figure out why
a directory is considered unsafe. To help with this, Git will provide
more detailed information when the environment variable
`GIT_TEST_DEBUG_UNSAFE_DIRECTORIES` is set to `true`.
118 changes: 118 additions & 0 deletions compat/mingw.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "../git-compat-util.h"
#include "win32.h"
#include <aclapi.h>
#include <sddl.h>
#include <conio.h>
#include <wchar.h>
#include <winioctl.h>
Expand Down Expand Up @@ -3446,6 +3448,122 @@ static void setup_windows_environment(void)
has_symlinks = 0;
}

static PSID get_current_user_sid(void)
{
HANDLE token;
DWORD len = 0;
PSID result = NULL;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
return NULL;

if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
TOKEN_USER *info = xmalloc((size_t)len);
if (GetTokenInformation(token, TokenUser, info, len, &len)) {
len = GetLengthSid(info->User.Sid);
result = xmalloc(len);
if (!CopySid(len, result, info->User.Sid)) {
error(_("failed to copy SID (%ld)"),
GetLastError());
FREE_AND_NULL(result);
}
}
FREE_AND_NULL(info);
}
CloseHandle(token);

return result;
}

int is_path_owned_by_current_sid(const char *path)
{
WCHAR wpath[MAX_PATH];
PSID sid = NULL;
PSECURITY_DESCRIPTOR descriptor = NULL;
DWORD err;

static wchar_t home[MAX_PATH];

int result = 0;

if (xutftowcs_path(wpath, path) < 0)
return 0;

/*
* On Windows, the home directory is owned by the administrator, but for
* all practical purposes, it belongs to the user. Do pretend that it is
* owned by the user.
*/
if (!*home) {
DWORD size = ARRAY_SIZE(home);
DWORD len = GetEnvironmentVariableW(L"HOME", home, size);
if (!len || len > size)
wcscpy(home, L"::N/A::");
}
if (!wcsicmp(wpath, home))
return 1;

/* Get the owner SID */
err = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
&sid, NULL, NULL, NULL, &descriptor);

if (err != ERROR_SUCCESS)
error(_("failed to get owner for '%s' (%ld)"), path, err);
else if (sid && IsValidSid(sid)) {
/* Now, verify that the SID matches the current user's */
static PSID current_user_sid;
BOOL is_member;

if (!current_user_sid)
current_user_sid = get_current_user_sid();

if (current_user_sid &&
IsValidSid(current_user_sid) &&
EqualSid(sid, current_user_sid))
result = 1;
else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) &&
CheckTokenMembership(NULL, sid, &is_member) &&
is_member)
/*
* If owned by the Administrators group, and the
* current user is an administrator, we consider that
* okay, too.
*/
result = 1;
else if (git_env_bool("GIT_TEST_DEBUG_UNSAFE_DIRECTORIES", 0)) {
LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL;

if (ConvertSidToStringSidA(sid, &str1))
to_free1 = str1;
else
str1 = "(inconvertible)";

if (!current_user_sid)
str2 = "(none)";
else if (!IsValidSid(current_user_sid))
str2 = "(invalid)";
else if (ConvertSidToStringSidA(current_user_sid, &str2))
to_free2 = str2;
else
str2 = "(inconvertible)";
warning("'%s' is owned by:\n\t'%s'\nbut the current user is:\n\t'%s'", path, str1, str2);
LocalFree(to_free1);
LocalFree(to_free2);
}
}

/*
* We can release the security descriptor struct only now because `sid`
* actually points into this struct.
*/
if (descriptor)
LocalFree(descriptor);

return result;
}

int is_valid_win32_path(const char *path, int allow_literal_nul)
{
const char *p = path;
Expand Down
7 changes: 7 additions & 0 deletions compat/mingw.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,13 @@ char *mingw_strbuf_realpath(struct strbuf *resolved, const char *path);
#include <inttypes.h>
#endif

/**
* Verifies that the specified path is owned by the user running the
* current process.
*/
int is_path_owned_by_current_sid(const char *path);
#define is_path_owned_by_current_user is_path_owned_by_current_sid

/**
* Verifies that the given path is a valid one on Windows.
*
Expand Down
12 changes: 12 additions & 0 deletions git-compat-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,18 @@ static inline int git_offset_1st_component(const char *path)
#define is_valid_path(path) 1
#endif

#ifndef is_path_owned_by_current_user
static inline int is_path_owned_by_current_uid(const char *path)
{
struct stat st;
if (lstat(path, &st))
return 0;
return st.st_uid == geteuid();
}

#define is_path_owned_by_current_user is_path_owned_by_current_uid
#endif

#ifndef find_last_dir_sep
static inline char *git_find_last_dir_sep(const char *path)
{
Expand Down
14 changes: 9 additions & 5 deletions path.c
Original file line number Diff line number Diff line change
Expand Up @@ -1231,11 +1231,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes)
const char *ceil = prefixes->items[i].string;
int len = strlen(ceil);

if (len == 1 && ceil[0] == '/')
len = 0; /* root matches anything, with length 0 */
else if (!strncmp(path, ceil, len) && path[len] == '/')
; /* match of length len */
else
/*
* For root directories (`/`, `C:/`, `//server/share/`)
* adjust the length to exclude the trailing slash.
*/
if (len > 0 && ceil[len - 1] == '/')
len--;

if (strncmp(path, ceil, len) ||
path[len] != '/' || !path[len + 1])
continue; /* no match */

if (len > max_len)
Expand Down
57 changes: 56 additions & 1 deletion setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "string-list.h"
#include "chdir-notify.h"
#include "promisor-remote.h"
#include "quote.h"

static int inside_git_dir = -1;
static int inside_work_tree = -1;
Expand Down Expand Up @@ -1090,6 +1091,42 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
}
}

struct safe_directory_data {
const char *path;
int is_safe;
};

static int safe_directory_cb(const char *key, const char *value, void *d)
{
struct safe_directory_data *data = d;

if (!value || !*value)
data->is_safe = 0;
else {
const char *interpolated = NULL;

if (!git_config_pathname(&interpolated, key, value) &&
!fspathcmp(data->path, interpolated ? interpolated : value))
data->is_safe = 1;

free((char *)interpolated);
}

return 0;
}

static int ensure_valid_ownership(const char *path)
{
struct safe_directory_data data = { .path = path };

if (is_path_owned_by_current_user(path))
return 1;

read_very_early_config(safe_directory_cb, &data);

return data.is_safe;
}

enum discovery_result {
GIT_DIR_NONE = 0,
GIT_DIR_EXPLICIT,
Expand All @@ -1098,7 +1135,8 @@ enum discovery_result {
/* these are errors */
GIT_DIR_HIT_CEILING = -1,
GIT_DIR_HIT_MOUNT_POINT = -2,
GIT_DIR_INVALID_GITFILE = -3
GIT_DIR_INVALID_GITFILE = -3,
GIT_DIR_INVALID_OWNERSHIP = -4
};

/*
Expand Down Expand Up @@ -1188,11 +1226,15 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
}
strbuf_setlen(dir, offset);
if (gitdirenv) {
if (!ensure_valid_ownership(dir->buf))
return GIT_DIR_INVALID_OWNERSHIP;
strbuf_addstr(gitdir, gitdirenv);
return GIT_DIR_DISCOVERED;
}

if (is_git_directory(dir->buf)) {
if (!ensure_valid_ownership(dir->buf))
return GIT_DIR_INVALID_OWNERSHIP;
strbuf_addstr(gitdir, ".");
return GIT_DIR_BARE;
}
Expand Down Expand Up @@ -1324,6 +1366,19 @@ const char *setup_git_directory_gently(int *nongit_ok)
dir.buf);
*nongit_ok = 1;
break;
case GIT_DIR_INVALID_OWNERSHIP:
if (!nongit_ok) {
struct strbuf quoted = STRBUF_INIT;

sq_quote_buf_pretty(&quoted, dir.buf);
die(_("unsafe repository ('%s' is owned by someone else)\n"
"To add an exception for this directory, call:\n"
"\n"
"\tgit config --global --add safe.directory %s"),
dir.buf, quoted.buf);
}
*nongit_ok = 1;
break;
case GIT_DIR_NONE:
/*
* As a safeguard against setup_git_directory_gently_1 returning
Expand Down
Loading

0 comments on commit fa8edb8

Please sign in to comment.