summaryrefslogtreecommitdiffstats
path: root/debian/patches/CVE-2022-24765.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/CVE-2022-24765.patch')
-rw-r--r--debian/patches/CVE-2022-24765.patch499
1 files changed, 499 insertions, 0 deletions
diff --git a/debian/patches/CVE-2022-24765.patch b/debian/patches/CVE-2022-24765.patch
new file mode 100644
index 0000000..f62a327
--- /dev/null
+++ b/debian/patches/CVE-2022-24765.patch
@@ -0,0 +1,499 @@
+Origin: https://github.com/git/git/commit/6e7ad1e4c22e7038975ba37c7413374fe566b064
+Origin: https://github.com/git/git/commit/bdc77d1d685be9c10b88abb281a42bc620548595
+Origin: https://github.com/git/git/commit/8959555cee7ec045958f9b6dd62e541affb7e7d9
+Origin: https://github.com/git/git/commit/cb95038137e9e66fc6a6b4a0e8db62bcc521b709
+Origin: https://github.com/git/git/commit/e47363e5a8bdf5144059d664c45c0975243ef05b
+Origin: https://github.com/git/git/commit/bb50ec3cc300eeff3aba7a2bea145aabdb477d31
+Origin: https://github.com/git/git/commit/0f85c4a30b072a26d74af8bbf63cc8f6a5dfc1b8
+Reviewed-by: Aron Xu <aron@debian.org>
+Last-Updated: 2023-01-26
+Comment: With the above patches, we are effectively updating the package to v2.30.3
+ plus some more changes. The symlink change to RelNotes has been dropped.
+
+diff --git a/Documentation/RelNotes/2.30.3.txt b/Documentation/RelNotes/2.30.3.txt
+new file mode 100644
+index 0000000..31b2a4d
+--- /dev/null
++++ b/Documentation/RelNotes/2.30.3.txt
+@@ -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.
+diff --git a/Documentation/config.txt b/Documentation/config.txt
+index 6ba50b1..34e6d47 100644
+--- a/Documentation/config.txt
++++ b/Documentation/config.txt
+@@ -438,6 +438,8 @@ include::config/rerere.txt[]
+
+ include::config/reset.txt[]
+
++include::config/safe.txt[]
++
+ include::config/sendemail.txt[]
+
+ include::config/sequencer.txt[]
+diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
+new file mode 100644
+index 0000000..6d764fe
+--- /dev/null
++++ b/Documentation/config/safe.txt
+@@ -0,0 +1,28 @@
++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.
+++
++To completely opt-out of this security check, set `safe.directory` to the
++string `*`. This will allow all repositories to be treated as if their
++directory was listed in the `safe.directory` list. If `safe.directory=*`
++is set in system config and you want to re-enable this protection, then
++initialize your list with an empty value before listing the repositories
++that you deem safe.
+diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
+index 9d789e0..7cf68be 100755
+--- a/GIT-VERSION-GEN
++++ b/GIT-VERSION-GEN
+@@ -1,7 +1,7 @@
+ #!/bin/sh
+
+ GVF=GIT-VERSION-FILE
+-DEF_VER=v2.30.2
++DEF_VER=v2.30.3
+
+ LF='
+ '
+diff --git a/compat/mingw.c b/compat/mingw.c
+index a435998..38ac359 100644
+--- a/compat/mingw.c
++++ b/compat/mingw.c
+@@ -1,5 +1,6 @@
+ #include "../git-compat-util.h"
+ #include "win32.h"
++#include <aclapi.h>
+ #include <conio.h>
+ #include <wchar.h>
+ #include "../strbuf.h"
+@@ -1060,6 +1061,7 @@ int pipe(int filedes[2])
+ return 0;
+ }
+
++#ifndef __MINGW64__
+ struct tm *gmtime_r(const time_t *timep, struct tm *result)
+ {
+ if (gmtime_s(result, timep) == 0)
+@@ -1073,6 +1075,7 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
+ return result;
+ return NULL;
+ }
++#endif
+
+ char *mingw_getcwd(char *pointer, int len)
+ {
+@@ -2599,6 +2602,92 @@ static void setup_windows_environment(void)
+ }
+ }
+
++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;
++
++ 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;
++ }
++
++ /*
++ * 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;
+diff --git a/compat/mingw.h b/compat/mingw.h
+index af8eddd..f6bab54 100644
+--- a/compat/mingw.h
++++ b/compat/mingw.h
+@@ -452,6 +452,13 @@ char *mingw_query_user_email(void);
+ #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.
+ *
+diff --git a/git-compat-util.h b/git-compat-util.h
+index 7d3db43..63ba89d 100644
+--- a/git-compat-util.h
++++ b/git-compat-util.h
+@@ -127,7 +127,9 @@
+ /* Approximation of the length of the decimal representation of this type. */
+ #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
+
+-#if defined(__sun__)
++#ifdef __MINGW64__
++#define _POSIX_C_SOURCE 1
++#elif defined(__sun__)
+ /*
+ * On Solaris, when _XOPEN_EXTENDED is set, its header file
+ * forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
+@@ -390,6 +392,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)
+ {
+diff --git a/path.c b/path.c
+index 7b385e5..853e716 100644
+--- a/path.c
++++ b/path.c
+@@ -1218,11 +1218,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)
+diff --git a/setup.c b/setup.c
+index c04cd25..aad9ace 100644
+--- a/setup.c
++++ b/setup.c
+@@ -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;
+@@ -1024,6 +1025,48 @@ 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 (strcmp(key, "safe.directory"))
++ return 0;
++
++ if (!value || !*value) {
++ data->is_safe = 0;
++ } else if (!strcmp(value, "*")) {
++ data->is_safe = 1;
++ } 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 (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) &&
++ 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,
+@@ -1032,7 +1075,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
+ };
+
+ /*
+@@ -1122,11 +1166,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;
+ }
+@@ -1253,6 +1301,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
+diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
+new file mode 100755
+index 0000000..239d93f
+--- /dev/null
++++ b/t/t0033-safe-directory.sh
+@@ -0,0 +1,49 @@
++#!/bin/sh
++
++test_description='verify safe.directory checks'
++
++. ./test-lib.sh
++
++GIT_TEST_ASSUME_DIFFERENT_OWNER=1
++export GIT_TEST_ASSUME_DIFFERENT_OWNER
++
++expect_rejected_dir () {
++ test_must_fail git status 2>err &&
++ grep "safe.directory" err
++}
++
++test_expect_success 'safe.directory is not set' '
++ expect_rejected_dir
++'
++
++test_expect_success 'safe.directory does not match' '
++ git config --global safe.directory bogus &&
++ expect_rejected_dir
++'
++
++test_expect_success 'path exist as different key' '
++ git config --global foo.bar "$(pwd)" &&
++ expect_rejected_dir
++'
++
++test_expect_success 'safe.directory matches' '
++ git config --global --add safe.directory "$(pwd)" &&
++ git status
++'
++
++test_expect_success 'safe.directory matches, but is reset' '
++ git config --global --add safe.directory "" &&
++ expect_rejected_dir
++'
++
++test_expect_success 'safe.directory=*' '
++ git config --global --add safe.directory "*" &&
++ git status
++'
++
++test_expect_success 'safe.directory=*, but is reset' '
++ git config --global --add safe.directory "" &&
++ expect_rejected_dir
++'
++
++test_done
+diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
+index 56db5c8..f538264 100755
+--- a/t/t0060-path-utils.sh
++++ b/t/t0060-path-utils.sh
+@@ -55,12 +55,15 @@ fi
+ ancestor() {
+ # We do some math with the expected ancestor length.
+ expected=$3
+- if test -n "$rootoff" && test "x$expected" != x-1; then
+- expected=$(($expected-$rootslash))
+- test $expected -lt 0 ||
+- expected=$(($expected+$rootoff))
+- fi
+- test_expect_success "longest ancestor: $1 $2 => $expected" \
++ case "$rootoff,$expected,$2" in
++ *,*,//*) ;; # leave UNC paths alone
++ [0-9]*,[0-9]*,/*)
++ # On Windows, expect MSYS2 pseudo root translation for
++ # Unix-style absolute paths
++ expected=$(($expected-$rootslash+$rootoff))
++ ;;
++ esac
++ test_expect_success $4 "longest ancestor: $1 $2 => $expected" \
+ "actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
+ test \"\$actual\" = '$expected'"
+ }
+@@ -156,6 +159,11 @@ ancestor /foo/bar /foo 4
+ ancestor /foo/bar /foo:/bar 4
+ ancestor /foo/bar /bar -1
+
++# Windows-specific: DOS drives, network shares
++ancestor C:/Users/me C:/ 2 MINGW
++ancestor D:/Users/me C:/ -1 MINGW
++ancestor //server/share/my-directory //server/share/ 14 MINGW
++
+ test_expect_success 'strip_path_suffix' '
+ test c:/msysgit = $(test-tool path-utils strip_path_suffix \
+ c:/msysgit/libexec//git-core libexec/git-core)