Compare commits
22 Commits
v2.34.0-rc
...
v2.31.4
Author | SHA1 | Date | |
---|---|---|---|
5b1c746c35 | |||
2f8809f9a1 | |||
88b7be68a4 | |||
3b0bf27049 | |||
b779214eaf | |||
6b11e3d52e | |||
b9063afda1 | |||
ae9abbb63e | |||
5f1a3fec8c | |||
09f66d65f8 | |||
17083c79ae | |||
0f85c4a30b | |||
bb50ec3cc3 | |||
e47363e5a8 | |||
44de39c45c | |||
6a2381a3e5 | |||
cb95038137 | |||
fdcad5a53e | |||
8959555cee | |||
bdc77d1d68 | |||
2a9a5862e5 | |||
6e7ad1e4c2 |
24
Documentation/RelNotes/2.30.3.txt
Normal file
24
Documentation/RelNotes/2.30.3.txt
Normal file
@ -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.
|
21
Documentation/RelNotes/2.30.4.txt
Normal file
21
Documentation/RelNotes/2.30.4.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
Git v2.30.4 Release Notes
|
||||||
|
=========================
|
||||||
|
|
||||||
|
This release contains minor fix-ups for the changes that went into
|
||||||
|
Git 2.30.3, which was made to address CVE-2022-24765.
|
||||||
|
|
||||||
|
* The code that was meant to parse the new `safe.directory`
|
||||||
|
configuration variable was not checking what configuration
|
||||||
|
variable was being fed to it, which has been corrected.
|
||||||
|
|
||||||
|
* '*' can be used as the value for the `safe.directory` variable to
|
||||||
|
signal that the user considers that any directory is safe.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Derrick Stolee (2):
|
||||||
|
t0033: add tests for safe.directory
|
||||||
|
setup: opt-out of check with safe.directory=*
|
||||||
|
|
||||||
|
Matheus Valadares (1):
|
||||||
|
setup: fix safe.directory key not being checked
|
12
Documentation/RelNotes/2.30.5.txt
Normal file
12
Documentation/RelNotes/2.30.5.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Git v2.30.5 Release Notes
|
||||||
|
=========================
|
||||||
|
|
||||||
|
This release contains minor fix-ups for the changes that went into
|
||||||
|
Git 2.30.3 and 2.30.4, addressing CVE-2022-29187.
|
||||||
|
|
||||||
|
* The safety check that verifies a safe ownership of the Git
|
||||||
|
worktree is now extended to also cover the ownership of the Git
|
||||||
|
directory (and the `.git` file, if there is any).
|
||||||
|
|
||||||
|
Carlo Marcelo Arenas Belón (1):
|
||||||
|
setup: tighten ownership checks post CVE-2022-24765
|
6
Documentation/RelNotes/2.31.2.txt
Normal file
6
Documentation/RelNotes/2.31.2.txt
Normal file
@ -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.
|
4
Documentation/RelNotes/2.31.3.txt
Normal file
4
Documentation/RelNotes/2.31.3.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Git Documentation/RelNotes/2.31.3.txt Release Notes
|
||||||
|
=========================
|
||||||
|
|
||||||
|
This release merges up the fixes that appear in v2.31.3.
|
6
Documentation/RelNotes/2.31.4.txt
Normal file
6
Documentation/RelNotes/2.31.4.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Git v2.31.4 Release Notes
|
||||||
|
=========================
|
||||||
|
|
||||||
|
This release merges up the fixes that appear in v2.30.5 to address
|
||||||
|
the security issue CVE-2022-29187; see the release notes for that
|
||||||
|
version for details.
|
@ -440,6 +440,8 @@ include::config/rerere.txt[]
|
|||||||
|
|
||||||
include::config/reset.txt[]
|
include::config/reset.txt[]
|
||||||
|
|
||||||
|
include::config/safe.txt[]
|
||||||
|
|
||||||
include::config/sendemail.txt[]
|
include::config/sendemail.txt[]
|
||||||
|
|
||||||
include::config/sequencer.txt[]
|
include::config/sequencer.txt[]
|
||||||
|
42
Documentation/config/safe.txt
Normal file
42
Documentation/config/safe.txt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
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.
|
||||||
|
+
|
||||||
|
As explained, Git only allows you to access repositories owned by
|
||||||
|
yourself, i.e. the user who is running Git, by default. When Git
|
||||||
|
is running as 'root' in a non Windows platform that provides sudo,
|
||||||
|
however, git checks the SUDO_UID environment variable that sudo creates
|
||||||
|
and will allow access to the uid recorded as its value in addition to
|
||||||
|
the id from 'root'.
|
||||||
|
This is to make it easy to perform a common sequence during installation
|
||||||
|
"make && sudo make install". A git process running under 'sudo' runs as
|
||||||
|
'root' but the 'sudo' command exports the environment variable to record
|
||||||
|
which id the original user has.
|
||||||
|
If that is not what you would prefer and want git to only trust
|
||||||
|
repositories that are owned by root instead, then you can remove
|
||||||
|
the `SUDO_UID` variable from root's environment before invoking git.
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
GVF=GIT-VERSION-FILE
|
GVF=GIT-VERSION-FILE
|
||||||
DEF_VER=v2.31.1
|
DEF_VER=v2.31.4
|
||||||
|
|
||||||
LF='
|
LF='
|
||||||
'
|
'
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "../git-compat-util.h"
|
#include "../git-compat-util.h"
|
||||||
#include "win32.h"
|
#include "win32.h"
|
||||||
|
#include <aclapi.h>
|
||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include "../strbuf.h"
|
#include "../strbuf.h"
|
||||||
@ -1060,6 +1061,7 @@ int pipe(int filedes[2])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef __MINGW64__
|
||||||
struct tm *gmtime_r(const time_t *timep, struct tm *result)
|
struct tm *gmtime_r(const time_t *timep, struct tm *result)
|
||||||
{
|
{
|
||||||
if (gmtime_s(result, timep) == 0)
|
if (gmtime_s(result, timep) == 0)
|
||||||
@ -1073,6 +1075,7 @@ struct tm *localtime_r(const time_t *timep, struct tm *result)
|
|||||||
return result;
|
return result;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
char *mingw_getcwd(char *pointer, int len)
|
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)
|
int is_valid_win32_path(const char *path, int allow_literal_nul)
|
||||||
{
|
{
|
||||||
const char *p = path;
|
const char *p = path;
|
||||||
|
@ -453,6 +453,13 @@ char *mingw_query_user_email(void);
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#endif
|
#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.
|
* Verifies that the given path is a valid one on Windows.
|
||||||
*
|
*
|
||||||
|
@ -127,7 +127,9 @@
|
|||||||
/* Approximation of the length of the decimal representation of this type. */
|
/* Approximation of the length of the decimal representation of this type. */
|
||||||
#define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1)
|
#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
|
* On Solaris, when _XOPEN_EXTENDED is set, its header file
|
||||||
* forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
|
* forces the programs to be XPG4v2, defeating any _XOPEN_SOURCE
|
||||||
@ -390,6 +392,74 @@ static inline int git_offset_1st_component(const char *path)
|
|||||||
#define is_valid_path(path) 1
|
#define is_valid_path(path) 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef is_path_owned_by_current_user
|
||||||
|
|
||||||
|
#ifdef __TANDEM
|
||||||
|
#define ROOT_UID 65535
|
||||||
|
#else
|
||||||
|
#define ROOT_UID 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do not use this function when
|
||||||
|
* (1) geteuid() did not say we are running as 'root', or
|
||||||
|
* (2) using this function will compromise the system.
|
||||||
|
*
|
||||||
|
* PORTABILITY WARNING:
|
||||||
|
* This code assumes uid_t is unsigned because that is what sudo does.
|
||||||
|
* If your uid_t type is signed and all your ids are positive then it
|
||||||
|
* should all work fine.
|
||||||
|
* If your version of sudo uses negative values for uid_t or it is
|
||||||
|
* buggy and return an overflowed value in SUDO_UID, then git might
|
||||||
|
* fail to grant access to your repository properly or even mistakenly
|
||||||
|
* grant access to someone else.
|
||||||
|
* In the unlikely scenario this happened to you, and that is how you
|
||||||
|
* got to this message, we would like to know about it; so sent us an
|
||||||
|
* email to git@vger.kernel.org indicating which platform you are
|
||||||
|
* using and which version of sudo, so we can improve this logic and
|
||||||
|
* maybe provide you with a patch that would prevent this issue again
|
||||||
|
* in the future.
|
||||||
|
*/
|
||||||
|
static inline void extract_id_from_env(const char *env, uid_t *id)
|
||||||
|
{
|
||||||
|
const char *real_uid = getenv(env);
|
||||||
|
|
||||||
|
/* discard anything empty to avoid a more complex check below */
|
||||||
|
if (real_uid && *real_uid) {
|
||||||
|
char *endptr = NULL;
|
||||||
|
unsigned long env_id;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
/* silent overflow errors could trigger a bug here */
|
||||||
|
env_id = strtoul(real_uid, &endptr, 10);
|
||||||
|
if (!*endptr && !errno)
|
||||||
|
*id = env_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_path_owned_by_current_uid(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
uid_t euid;
|
||||||
|
|
||||||
|
if (lstat(path, &st))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
euid = geteuid();
|
||||||
|
if (euid == ROOT_UID)
|
||||||
|
{
|
||||||
|
if (st.st_uid == ROOT_UID)
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
extract_id_from_env("SUDO_UID", &euid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return st.st_uid == euid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define is_path_owned_by_current_user is_path_owned_by_current_uid
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef find_last_dir_sep
|
#ifndef find_last_dir_sep
|
||||||
static inline char *git_find_last_dir_sep(const char *path)
|
static inline char *git_find_last_dir_sep(const char *path)
|
||||||
{
|
{
|
||||||
|
14
path.c
14
path.c
@ -1218,11 +1218,15 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes)
|
|||||||
const char *ceil = prefixes->items[i].string;
|
const char *ceil = prefixes->items[i].string;
|
||||||
int len = strlen(ceil);
|
int len = strlen(ceil);
|
||||||
|
|
||||||
if (len == 1 && ceil[0] == '/')
|
/*
|
||||||
len = 0; /* root matches anything, with length 0 */
|
* For root directories (`/`, `C:/`, `//server/share/`)
|
||||||
else if (!strncmp(path, ceil, len) && path[len] == '/')
|
* adjust the length to exclude the trailing slash.
|
||||||
; /* match of length len */
|
*/
|
||||||
else
|
if (len > 0 && ceil[len - 1] == '/')
|
||||||
|
len--;
|
||||||
|
|
||||||
|
if (strncmp(path, ceil, len) ||
|
||||||
|
path[len] != '/' || !path[len + 1])
|
||||||
continue; /* no match */
|
continue; /* no match */
|
||||||
|
|
||||||
if (len > max_len)
|
if (len > max_len)
|
||||||
|
120
setup.c
120
setup.c
@ -5,6 +5,7 @@
|
|||||||
#include "string-list.h"
|
#include "string-list.h"
|
||||||
#include "chdir-notify.h"
|
#include "chdir-notify.h"
|
||||||
#include "promisor-remote.h"
|
#include "promisor-remote.h"
|
||||||
|
#include "quote.h"
|
||||||
|
|
||||||
static int inside_git_dir = -1;
|
static int inside_git_dir = -1;
|
||||||
static int inside_work_tree = -1;
|
static int inside_work_tree = -1;
|
||||||
@ -1024,6 +1025,66 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if a repository is safe, by verifying the ownership of the
|
||||||
|
* worktree (if any), the git directory, and the gitfile (if any).
|
||||||
|
*
|
||||||
|
* Exemptions for known-safe repositories can be added via `safe.directory`
|
||||||
|
* config settings; for non-bare repositories, their worktree needs to be
|
||||||
|
* added, for bare ones their git directory.
|
||||||
|
*/
|
||||||
|
static int ensure_valid_ownership(const char *gitfile,
|
||||||
|
const char *worktree, const char *gitdir)
|
||||||
|
{
|
||||||
|
struct safe_directory_data data = {
|
||||||
|
.path = worktree ? worktree : gitdir
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) &&
|
||||||
|
(!gitfile || is_path_owned_by_current_user(gitfile)) &&
|
||||||
|
(!worktree || is_path_owned_by_current_user(worktree)) &&
|
||||||
|
(!gitdir || is_path_owned_by_current_user(gitdir)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* data.path is the "path" that identifies the repository and it is
|
||||||
|
* constant regardless of what failed above. data.is_safe should be
|
||||||
|
* initialized to false, and might be changed by the callback.
|
||||||
|
*/
|
||||||
|
read_very_early_config(safe_directory_cb, &data);
|
||||||
|
|
||||||
|
return data.is_safe;
|
||||||
|
}
|
||||||
|
|
||||||
enum discovery_result {
|
enum discovery_result {
|
||||||
GIT_DIR_NONE = 0,
|
GIT_DIR_NONE = 0,
|
||||||
GIT_DIR_EXPLICIT,
|
GIT_DIR_EXPLICIT,
|
||||||
@ -1032,7 +1093,8 @@ enum discovery_result {
|
|||||||
/* these are errors */
|
/* these are errors */
|
||||||
GIT_DIR_HIT_CEILING = -1,
|
GIT_DIR_HIT_CEILING = -1,
|
||||||
GIT_DIR_HIT_MOUNT_POINT = -2,
|
GIT_DIR_HIT_MOUNT_POINT = -2,
|
||||||
GIT_DIR_INVALID_GITFILE = -3
|
GIT_DIR_INVALID_GITFILE = -3,
|
||||||
|
GIT_DIR_INVALID_OWNERSHIP = -4
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1105,6 +1167,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
|
|||||||
current_device = get_device_or_die(dir->buf, NULL, 0);
|
current_device = get_device_or_die(dir->buf, NULL, 0);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int offset = dir->len, error_code = 0;
|
int offset = dir->len, error_code = 0;
|
||||||
|
char *gitdir_path = NULL;
|
||||||
|
char *gitfile = NULL;
|
||||||
|
|
||||||
if (offset > min_offset)
|
if (offset > min_offset)
|
||||||
strbuf_addch(dir, '/');
|
strbuf_addch(dir, '/');
|
||||||
@ -1115,18 +1179,51 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
|
|||||||
if (die_on_error ||
|
if (die_on_error ||
|
||||||
error_code == READ_GITFILE_ERR_NOT_A_FILE) {
|
error_code == READ_GITFILE_ERR_NOT_A_FILE) {
|
||||||
/* NEEDSWORK: fail if .git is not file nor dir */
|
/* NEEDSWORK: fail if .git is not file nor dir */
|
||||||
if (is_git_directory(dir->buf))
|
if (is_git_directory(dir->buf)) {
|
||||||
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
|
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
|
||||||
|
gitdir_path = xstrdup(dir->buf);
|
||||||
|
}
|
||||||
} else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
|
} else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
|
||||||
return GIT_DIR_INVALID_GITFILE;
|
return GIT_DIR_INVALID_GITFILE;
|
||||||
}
|
} else
|
||||||
|
gitfile = xstrdup(dir->buf);
|
||||||
|
/*
|
||||||
|
* Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
|
||||||
|
* to check that directory for a repository.
|
||||||
|
* Now trim that tentative addition away, because we want to
|
||||||
|
* focus on the real directory we are in.
|
||||||
|
*/
|
||||||
strbuf_setlen(dir, offset);
|
strbuf_setlen(dir, offset);
|
||||||
if (gitdirenv) {
|
if (gitdirenv) {
|
||||||
strbuf_addstr(gitdir, gitdirenv);
|
enum discovery_result ret;
|
||||||
return GIT_DIR_DISCOVERED;
|
|
||||||
|
if (ensure_valid_ownership(gitfile,
|
||||||
|
dir->buf,
|
||||||
|
(gitdir_path ? gitdir_path : gitdirenv))) {
|
||||||
|
strbuf_addstr(gitdir, gitdirenv);
|
||||||
|
ret = GIT_DIR_DISCOVERED;
|
||||||
|
} else
|
||||||
|
ret = GIT_DIR_INVALID_OWNERSHIP;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Earlier, during discovery, we might have allocated
|
||||||
|
* string copies for gitdir_path or gitfile so make
|
||||||
|
* sure we don't leak by freeing them now, before
|
||||||
|
* leaving the loop and function.
|
||||||
|
*
|
||||||
|
* Note: gitdirenv will be non-NULL whenever these are
|
||||||
|
* allocated, therefore we need not take care of releasing
|
||||||
|
* them outside of this conditional block.
|
||||||
|
*/
|
||||||
|
free(gitdir_path);
|
||||||
|
free(gitfile);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_git_directory(dir->buf)) {
|
if (is_git_directory(dir->buf)) {
|
||||||
|
if (!ensure_valid_ownership(NULL, NULL, dir->buf))
|
||||||
|
return GIT_DIR_INVALID_OWNERSHIP;
|
||||||
strbuf_addstr(gitdir, ".");
|
strbuf_addstr(gitdir, ".");
|
||||||
return GIT_DIR_BARE;
|
return GIT_DIR_BARE;
|
||||||
}
|
}
|
||||||
@ -1253,6 +1350,19 @@ const char *setup_git_directory_gently(int *nongit_ok)
|
|||||||
dir.buf);
|
dir.buf);
|
||||||
*nongit_ok = 1;
|
*nongit_ok = 1;
|
||||||
break;
|
break;
|
||||||
|
case GIT_DIR_INVALID_OWNERSHIP:
|
||||||
|
if (!nongit_ok) {
|
||||||
|
struct strbuf quoted = STRBUF_INIT;
|
||||||
|
|
||||||
|
sq_quote_buf_pretty("ed, dir.buf);
|
||||||
|
die(_("detected dubious ownership in repository at '%s'\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:
|
case GIT_DIR_NONE:
|
||||||
/*
|
/*
|
||||||
* As a safeguard against setup_git_directory_gently_1 returning
|
* As a safeguard against setup_git_directory_gently_1 returning
|
||||||
|
15
t/lib-sudo.sh
Normal file
15
t/lib-sudo.sh
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Helpers for running git commands under sudo.
|
||||||
|
|
||||||
|
# Runs a scriplet passed through stdin under sudo.
|
||||||
|
run_with_sudo () {
|
||||||
|
local ret
|
||||||
|
local RUN="$TEST_DIRECTORY/$$.sh"
|
||||||
|
write_script "$RUN" "$TEST_SHELL_PATH"
|
||||||
|
# avoid calling "$RUN" directly so sudo doesn't get a chance to
|
||||||
|
# override the shell, add aditional restrictions or even reject
|
||||||
|
# running the script because its security policy deem it unsafe
|
||||||
|
sudo "$TEST_SHELL_PATH" -c "\"$RUN\""
|
||||||
|
ret=$?
|
||||||
|
rm -f "$RUN"
|
||||||
|
return $ret
|
||||||
|
}
|
49
t/t0033-safe-directory.sh
Executable file
49
t/t0033-safe-directory.sh
Executable file
@ -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
|
93
t/t0034-root-safe-directory.sh
Executable file
93
t/t0034-root-safe-directory.sh
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='verify safe.directory checks while running as root'
|
||||||
|
|
||||||
|
. ./test-lib.sh
|
||||||
|
. "$TEST_DIRECTORY"/lib-sudo.sh
|
||||||
|
|
||||||
|
if [ "$GIT_TEST_ALLOW_SUDO" != "YES" ]
|
||||||
|
then
|
||||||
|
skip_all="You must set env var GIT_TEST_ALLOW_SUDO=YES in order to run this test"
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! test_have_prereq NOT_ROOT
|
||||||
|
then
|
||||||
|
skip_all="These tests do not support running as root"
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_lazy_prereq SUDO '
|
||||||
|
sudo -n id -u >u &&
|
||||||
|
id -u root >r &&
|
||||||
|
test_cmp u r &&
|
||||||
|
command -v git >u &&
|
||||||
|
sudo command -v git >r &&
|
||||||
|
test_cmp u r
|
||||||
|
'
|
||||||
|
|
||||||
|
if ! test_have_prereq SUDO
|
||||||
|
then
|
||||||
|
skip_all="Your sudo/system configuration is either too strict or unsupported"
|
||||||
|
test_done
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_expect_success SUDO 'setup' '
|
||||||
|
sudo rm -rf root &&
|
||||||
|
mkdir -p root/r &&
|
||||||
|
(
|
||||||
|
cd root/r &&
|
||||||
|
git init
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SUDO 'sudo git status as original owner' '
|
||||||
|
(
|
||||||
|
cd root/r &&
|
||||||
|
git status &&
|
||||||
|
sudo git status
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SUDO 'setup root owned repository' '
|
||||||
|
sudo mkdir -p root/p &&
|
||||||
|
sudo git init root/p
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'cannot access if owned by root' '
|
||||||
|
(
|
||||||
|
cd root/p &&
|
||||||
|
test_must_fail git status
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'can access if addressed explicitly' '
|
||||||
|
(
|
||||||
|
cd root/p &&
|
||||||
|
GIT_DIR=.git GIT_WORK_TREE=. git status
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SUDO 'can access with sudo if root' '
|
||||||
|
(
|
||||||
|
cd root/p &&
|
||||||
|
sudo git status
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success SUDO 'can access with sudo if root by removing SUDO_UID' '
|
||||||
|
(
|
||||||
|
cd root/p &&
|
||||||
|
run_with_sudo <<-END
|
||||||
|
unset SUDO_UID &&
|
||||||
|
git status
|
||||||
|
END
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
# this MUST be always the last test
|
||||||
|
test_expect_success SUDO 'cleanup' '
|
||||||
|
sudo rm -rf root
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
@ -55,12 +55,15 @@ fi
|
|||||||
ancestor() {
|
ancestor() {
|
||||||
# We do some math with the expected ancestor length.
|
# We do some math with the expected ancestor length.
|
||||||
expected=$3
|
expected=$3
|
||||||
if test -n "$rootoff" && test "x$expected" != x-1; then
|
case "$rootoff,$expected,$2" in
|
||||||
expected=$(($expected-$rootslash))
|
*,*,//*) ;; # leave UNC paths alone
|
||||||
test $expected -lt 0 ||
|
[0-9]*,[0-9]*,/*)
|
||||||
expected=$(($expected+$rootoff))
|
# On Windows, expect MSYS2 pseudo root translation for
|
||||||
fi
|
# Unix-style absolute paths
|
||||||
test_expect_success "longest ancestor: $1 $2 => $expected" \
|
expected=$(($expected-$rootslash+$rootoff))
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
test_expect_success $4 "longest ancestor: $1 $2 => $expected" \
|
||||||
"actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
|
"actual=\$(test-tool path-utils longest_ancestor_length '$1' '$2') &&
|
||||||
test \"\$actual\" = '$expected'"
|
test \"\$actual\" = '$expected'"
|
||||||
}
|
}
|
||||||
@ -156,6 +159,11 @@ ancestor /foo/bar /foo 4
|
|||||||
ancestor /foo/bar /foo:/bar 4
|
ancestor /foo/bar /foo:/bar 4
|
||||||
ancestor /foo/bar /bar -1
|
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_expect_success 'strip_path_suffix' '
|
||||||
test c:/msysgit = $(test-tool path-utils strip_path_suffix \
|
test c:/msysgit = $(test-tool path-utils strip_path_suffix \
|
||||||
c:/msysgit/libexec//git-core libexec/git-core)
|
c:/msysgit/libexec//git-core libexec/git-core)
|
||||||
|
Reference in New Issue
Block a user