diff -Nru git-2.25.1/debian/changelog git-2.25.1/debian/changelog --- git-2.25.1/debian/changelog 2021-09-09 11:42:33.000000000 +0000 +++ git-2.25.1/debian/changelog 2022-04-08 12:57:16.000000000 +0000 @@ -1,3 +1,15 @@ +git (1:2.25.1-1ubuntu3.3) focal-security; urgency=medium + + * SECURITY UPDATE: Run commands in diff users + - debian/patches/CVE-2022-24765-*.patch: fix GIT_CEILING_DIRECTORIES; add + an owner check for the top-level-directory; add a function to + determine whether a path is owned by the current user in patch.c, + t/t0060-path-utils.sh, setup.c, compat/mingw.c, compat/mingw.h, + git-compat-util.h. + - CVE-2022-24765 + + -- Leonidas Da Silva Barbosa Fri, 08 Apr 2022 09:57:16 -0300 + git (1:2.25.1-1ubuntu3.2) focal-security; urgency=medium * SECURITY UPDATE: cross-protocol request via newline character in repo path diff -Nru git-2.25.1/debian/patches/CVE-2022-24765-1.patch git-2.25.1/debian/patches/CVE-2022-24765-1.patch --- git-2.25.1/debian/patches/CVE-2022-24765-1.patch 1970-01-01 00:00:00.000000000 +0000 +++ git-2.25.1/debian/patches/CVE-2022-24765-1.patch 2022-04-08 12:56:40.000000000 +0000 @@ -0,0 +1,118 @@ +From fdcad5a53e14bd397e4fa323e7fd0c3bf16dd373 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 23 Mar 2022 23:00:41 +0100 +Subject: [PATCH 4/5] Fix `GIT_CEILING_DIRECTORIES` with `C:\` and the likes + +When determining the length of the longest ancestor of a given path with +respect to to e.g. `GIT_CEILING_DIRECTORIES`, we special-case the root +directory by returning 0 (i.e. we pretend that the path `/` does not end +in a slash by virtually stripping it). + +That is the correct behavior because when normalizing paths, the root +directory is special: all other directory paths have their trailing +slash stripped, but not the root directory's path (because it would +become the empty string, which is not a legal path). + +However, this special-casing of the root directory in +`longest_ancestor_length()` completely forgets about Windows-style root +directories, e.g. `C:\`. These _also_ get normalized with a trailing +slash (because `C:` would actually refer to the current directory on +that drive, not necessarily to its root directory). + +In fc56c7b34b (mingw: accomodate t0060-path-utils for MSYS2, +2016-01-27), we almost got it right. We noticed that +`longest_ancestor_length()` expects a slash _after_ the matched prefix, +and if the prefix already ends in a slash, the normalized path won't +ever match and -1 is returned. + +But then that commit went astray: The correct fix is not to adjust the +_tests_ to expect an incorrect -1 when that function is fed a prefix +that ends in a slash, but instead to treat such a prefix as if the +trailing slash had been removed. + +Likewise, that function needs to handle the case where it is fed a path +that ends in a slash (not only a prefix that ends in a slash): if it +matches the prefix (plus trailing slash), we still need to verify that +the path does not end there, otherwise the prefix is not actually an +ancestor of the path but identical to it (and we need to return -1 in +that case). + +With these two adjustments, we no longer need to play games in t0060 +where we only add `$rootoff` if the passed prefix is different from the +MSYS2 pseudo root, instead we also add it for the MSYS2 pseudo root +itself. We do have to be careful to skip that logic entirely for Windows +paths, though, because they do are not subject to that MSYS2 pseudo root +treatment. + +This patch fixes the scenario where a user has set +`GIT_CEILING_DIRECTORIES=C:\`, which would be ignored otherwise. + +Signed-off-by: Johannes Schindelin +--- + path.c | 14 +++++++++----- + t/t0060-path-utils.sh | 20 ++++++++++++++------ + 2 files changed, 23 insertions(+), 11 deletions(-) + +Index: git-2.25.1/path.c +=================================================================== +--- git-2.25.1.orig/path.c ++++ git-2.25.1/path.c +@@ -1216,11 +1216,15 @@ int longest_ancestor_length(const char * + 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) +Index: git-2.25.1/t/t0060-path-utils.sh +=================================================================== +--- git-2.25.1.orig/t/t0060-path-utils.sh ++++ git-2.25.1/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) diff -Nru git-2.25.1/debian/patches/CVE-2022-24765-2.patch git-2.25.1/debian/patches/CVE-2022-24765-2.patch --- git-2.25.1/debian/patches/CVE-2022-24765-2.patch 1970-01-01 00:00:00.000000000 +0000 +++ git-2.25.1/debian/patches/CVE-2022-24765-2.patch 2022-04-08 12:56:50.000000000 +0000 @@ -0,0 +1,186 @@ +From 8959555cee7ec045958f9b6dd62e541affb7e7d9 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 2 Mar 2022 12:23:04 +0100 +Subject: [PATCH 3/5] setup_git_directory(): add an owner check for the + top-level directory + +It poses a security risk to search for a git directory outside of the +directories owned by the current user. + +For example, it is common e.g. in computer pools of educational +institutes to have a "scratch" space: a mounted disk with plenty of +space that is regularly swiped where any authenticated user can create +a directory to do their work. Merely navigating to such a space with a +Git-enabled `PS1` when there is a maliciously-crafted `/scratch/.git/` +can lead to a compromised account. + +The same holds true in multi-user setups running Windows, as `C:\` is +writable to every authenticated user by default. + +To plug this vulnerability, we stop Git from accepting top-level +directories owned by someone other than the current user. We avoid +looking at the ownership of each and every directories between the +current and the top-level one (if there are any between) to avoid +introducing a performance bottleneck. + +This new default behavior is obviously incompatible with the concept of +shared repositories, where we expect the top-level directory to be owned +by only one of its legitimate users. To re-enable that use case, we add +support for adding exceptions from the new default behavior via the +config setting `safe.directory`. + +The `safe.directory` config setting is only respected in the system and +global configs, not from repository configs or via the command-line, and +can have multiple values to allow for multiple shared repositories. + +We are particularly careful to provide a helpful message to any user +trying to use a shared repository. + +Signed-off-by: Johannes Schindelin +--- + Documentation/config.txt | 2 ++ + Documentation/config/safe.txt | 21 +++++++++++++ + setup.c | 57 ++++++++++++++++++++++++++++++++++- + 3 files changed, 79 insertions(+), 1 deletion(-) + create mode 100644 Documentation/config/safe.txt + +Index: git-2.25.1/Documentation/config.txt +=================================================================== +--- git-2.25.1.orig/Documentation/config.txt ++++ git-2.25.1/Documentation/config.txt +@@ -427,6 +427,8 @@ include::config/rerere.txt[] + + include::config/reset.txt[] + ++include::config/safe.txt[] ++ + include::config/sendemail.txt[] + + include::config/sequencer.txt[] +Index: git-2.25.1/Documentation/config/safe.txt +=================================================================== +--- /dev/null ++++ git-2.25.1/Documentation/config/safe.txt +@@ -0,0 +1,21 @@ ++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=`. +++ ++The value of this setting is interpolated, i.e. `~/` expands to a ++path relative to the home directory and `%(prefix)/` expands to a ++path relative to Git's (runtime) prefix. +Index: git-2.25.1/setup.c +=================================================================== +--- git-2.25.1.orig/setup.c ++++ git-2.25.1/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; +@@ -889,6 +890,42 @@ static int canonicalize_ceiling_entry(st + } + } + ++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, +@@ -897,7 +934,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 + }; + + /* +@@ -987,11 +1025,15 @@ static enum discovery_result setup_git_d + } + 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; + } +@@ -1118,6 +1160,19 @@ const char *setup_git_directory_gently(i + dir.buf); + *nongit_ok = 1; + break; ++ case GIT_DIR_INVALID_OWNERSHIP: ++ if (!nongit_ok) { ++ struct strbuf quoted = STRBUF_INIT; ++ ++ sq_quote_buf_pretty("ed, 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 -Nru git-2.25.1/debian/patches/CVE-2022-24765-3.patch git-2.25.1/debian/patches/CVE-2022-24765-3.patch --- git-2.25.1/debian/patches/CVE-2022-24765-3.patch 1970-01-01 00:00:00.000000000 +0000 +++ git-2.25.1/debian/patches/CVE-2022-24765-3.patch 2022-04-08 12:57:00.000000000 +0000 @@ -0,0 +1,174 @@ +From bdc77d1d685be9c10b88abb281a42bc620548595 Mon Sep 17 00:00:00 2001 +From: Johannes Schindelin +Date: Wed, 2 Mar 2022 11:06:24 +0100 +Subject: [PATCH 2/5] Add a function to determine whether a path is owned by + the current user + +This function will be used in the next commit to prevent +`setup_git_directory()` from discovering a repository in a directory +that is owned by someone other than the current user. + +Note: We cannot simply use `st.st_uid` on Windows just like we do on +Linux and other Unix-like platforms: according to +https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions +this field is always zero on Windows (because Windows' idea of a user ID +does not fit into a single numerical value). Therefore, we have to do +something a little involved to replicate the same functionality there. + +Also note: On Windows, a user's home directory is not actually owned by +said user, but by the administrator. For all practical purposes, it is +under the user's control, though, therefore we pretend that it is owned +by the user. + +Signed-off-by: Johannes Schindelin +--- + compat/mingw.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ + compat/mingw.h | 7 ++++ + git-compat-util.h | 12 +++++++ + 3 files changed, 106 insertions(+) + +Index: git-2.25.1/compat/mingw.c +=================================================================== +--- git-2.25.1.orig/compat/mingw.c ++++ git-2.25.1/compat/mingw.c +@@ -1,5 +1,6 @@ + #include "../git-compat-util.h" + #include "win32.h" ++#include + #include + #include + #include "../strbuf.h" +@@ -2535,6 +2536,92 @@ static void setup_windows_environment(vo + } + } + ++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; +Index: git-2.25.1/compat/mingw.h +=================================================================== +--- git-2.25.1.orig/compat/mingw.h ++++ git-2.25.1/compat/mingw.h +@@ -453,6 +453,13 @@ char *mingw_query_user_email(void); + #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. + * + * In particular, path segments are disallowed which +Index: git-2.25.1/git-compat-util.h +=================================================================== +--- git-2.25.1.orig/git-compat-util.h ++++ git-2.25.1/git-compat-util.h +@@ -386,6 +386,18 @@ static inline int git_offset_1st_compone + #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 -Nru git-2.25.1/debian/patches/CVE-2022-24765-4.patch git-2.25.1/debian/patches/CVE-2022-24765-4.patch --- git-2.25.1/debian/patches/CVE-2022-24765-4.patch 1970-01-01 00:00:00.000000000 +0000 +++ git-2.25.1/debian/patches/CVE-2022-24765-4.patch 2022-04-08 12:57:10.000000000 +0000 @@ -0,0 +1,66 @@ +From 6e7ad1e4c22e7038975ba37c7413374fe566b064 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Carlo=20Marcelo=20Arenas=20Bel=C3=B3n?= +Date: Sat, 27 Nov 2021 10:15:32 +0000 +Subject: [PATCH 1/5] mingw: avoid fallback for {local,gm}time_r() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +mingw-w64's pthread_unistd.h had a bug that mistakenly (because there is +no support for the *lockfile() functions required[1]) defined +_POSIX_THREAD_SAFE_FUNCTIONS and that was being worked around since +3ecd153a3b (compat/mingw: support MSys2-based MinGW build, 2016-01-14). + +The bug was fixed in winphtreads, but as a side effect, leaves the +reentrant functions from time.h no longer visible and therefore breaks +the build. + +Since the intention all along was to avoid using the fallback functions, +formalize the use of POSIX by setting the corresponding feature flag and +compile out the implementation for the fallback functions. + +[1] https://unix.org/whitepapers/reentrant.html + +Signed-off-by: Carlo Marcelo Arenas Belón +Acked-by: Johannes Schindelin +Signed-off-by: Junio C Hamano +--- + compat/mingw.c | 2 ++ + git-compat-util.h | 4 +++- + 2 files changed, 5 insertions(+), 1 deletion(-) + +Index: git-2.25.1/compat/mingw.c +=================================================================== +--- git-2.25.1.orig/compat/mingw.c ++++ git-2.25.1/compat/mingw.c +@@ -1023,6 +1023,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) +@@ -1036,6 +1037,7 @@ struct tm *localtime_r(const time_t *tim + return result; + return NULL; + } ++#endif + + char *mingw_getcwd(char *pointer, int len) + { +Index: git-2.25.1/git-compat-util.h +=================================================================== +--- git-2.25.1.orig/git-compat-util.h ++++ git-2.25.1/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 diff -Nru git-2.25.1/debian/patches/series git-2.25.1/debian/patches/series --- git-2.25.1/debian/patches/series 2021-09-09 11:42:33.000000000 +0000 +++ git-2.25.1/debian/patches/series 2022-04-08 12:57:06.000000000 +0000 @@ -13,3 +13,7 @@ CVE-2020-11008-9.patch CVE-2021-21300.patch CVE-2021-40330.patch +CVE-2022-24765-1.patch +CVE-2022-24765-2.patch +CVE-2022-24765-3.patch +CVE-2022-24765-4.patch