summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config/safe.txt13
-rw-r--r--git-compat-util.h53
-rw-r--r--t/lib-sudo.sh15
-rwxr-xr-xt/t0034-root-safe-directory.sh106
4 files changed, 186 insertions, 1 deletions
diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index ae0e2e3bdb..1ee10fae14 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -26,3 +26,16 @@ 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 instead.
+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 must remove
+the `SUDO_UID` variable from root's environment before invoking git.
diff --git a/git-compat-util.h b/git-compat-util.h
index 58fd813bd0..96293b6c43 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -437,12 +437,63 @@ static inline int git_offset_1st_component(const char *path)
#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;
- return st.st_uid == geteuid();
+
+ euid = geteuid();
+ if (euid == ROOT_UID)
+ 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
diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
new file mode 100644
index 0000000000..b4d7788f4e
--- /dev/null
+++ b/t/lib-sudo.sh
@@ -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
+}
diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 0000000000..a621f1ea5e
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,106 @@
+#!/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_failure 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
+ )
+'
+
+test_lazy_prereq SUDO_SUDO '
+ sudo sudo id -u >u &&
+ id -u root >r &&
+ test_cmp u r
+'
+
+test_expect_success SUDO_SUDO 'can access with sudo abusing SUDO_UID' '
+ (
+ cd root/p &&
+ sudo sudo git status
+ )
+'
+
+# this MUST be always the last test
+test_expect_success SUDO 'cleanup' '
+ sudo rm -rf root
+'
+
+test_done