Skip to content

Commit 2088a0c

Browse files
committed
Merge branch 'cb/path-owner-check-with-sudo'
With a recent update to refuse access to repositories of other people by default, "sudo make install" and "sudo git describe" stopped working. This series intends to loosen it while keeping the safety. * cb/path-owner-check-with-sudo: t0034: add negative tests and allow git init to mostly work under sudo git-compat-util: avoid failing dir ownership checks if running privileged t: regression git needs safe.directory when using sudo
2 parents 7ec4a9e + b9063af commit 2088a0c

File tree

4 files changed

+186
-1
lines changed

4 files changed

+186
-1
lines changed

Documentation/config/safe.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,16 @@ directory was listed in the `safe.directory` list. If `safe.directory=*`
2626
is set in system config and you want to re-enable this protection, then
2727
initialize your list with an empty value before listing the repositories
2828
that you deem safe.
29+
+
30+
As explained, Git only allows you to access repositories owned by
31+
yourself, i.e. the user who is running Git, by default. When Git
32+
is running as 'root' in a non Windows platform that provides sudo,
33+
however, git checks the SUDO_UID environment variable that sudo creates
34+
and will allow access to the uid recorded as its value instead.
35+
This is to make it easy to perform a common sequence during installation
36+
"make && sudo make install". A git process running under 'sudo' runs as
37+
'root' but the 'sudo' command exports the environment variable to record
38+
which id the original user has.
39+
If that is not what you would prefer and want git to only trust
40+
repositories that are owned by root instead, then you must remove
41+
the `SUDO_UID` variable from root's environment before invoking git.

git-compat-util.h

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,12 +437,63 @@ static inline int git_offset_1st_component(const char *path)
437437
#endif
438438

439439
#ifndef is_path_owned_by_current_user
440+
441+
#ifdef __TANDEM
442+
#define ROOT_UID 65535
443+
#else
444+
#define ROOT_UID 0
445+
#endif
446+
447+
/*
448+
* Do not use this function when
449+
* (1) geteuid() did not say we are running as 'root', or
450+
* (2) using this function will compromise the system.
451+
*
452+
* PORTABILITY WARNING:
453+
* This code assumes uid_t is unsigned because that is what sudo does.
454+
* If your uid_t type is signed and all your ids are positive then it
455+
* should all work fine.
456+
* If your version of sudo uses negative values for uid_t or it is
457+
* buggy and return an overflowed value in SUDO_UID, then git might
458+
* fail to grant access to your repository properly or even mistakenly
459+
* grant access to someone else.
460+
* In the unlikely scenario this happened to you, and that is how you
461+
* got to this message, we would like to know about it; so sent us an
462+
* email to [email protected] indicating which platform you are
463+
* using and which version of sudo, so we can improve this logic and
464+
* maybe provide you with a patch that would prevent this issue again
465+
* in the future.
466+
*/
467+
static inline void extract_id_from_env(const char *env, uid_t *id)
468+
{
469+
const char *real_uid = getenv(env);
470+
471+
/* discard anything empty to avoid a more complex check below */
472+
if (real_uid && *real_uid) {
473+
char *endptr = NULL;
474+
unsigned long env_id;
475+
476+
errno = 0;
477+
/* silent overflow errors could trigger a bug here */
478+
env_id = strtoul(real_uid, &endptr, 10);
479+
if (!*endptr && !errno)
480+
*id = env_id;
481+
}
482+
}
483+
440484
static inline int is_path_owned_by_current_uid(const char *path)
441485
{
442486
struct stat st;
487+
uid_t euid;
488+
443489
if (lstat(path, &st))
444490
return 0;
445-
return st.st_uid == geteuid();
491+
492+
euid = geteuid();
493+
if (euid == ROOT_UID)
494+
extract_id_from_env("SUDO_UID", &euid);
495+
496+
return st.st_uid == euid;
446497
}
447498

448499
#define is_path_owned_by_current_user is_path_owned_by_current_uid

t/lib-sudo.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Helpers for running git commands under sudo.
2+
3+
# Runs a scriplet passed through stdin under sudo.
4+
run_with_sudo () {
5+
local ret
6+
local RUN="$TEST_DIRECTORY/$$.sh"
7+
write_script "$RUN" "$TEST_SHELL_PATH"
8+
# avoid calling "$RUN" directly so sudo doesn't get a chance to
9+
# override the shell, add aditional restrictions or even reject
10+
# running the script because its security policy deem it unsafe
11+
sudo "$TEST_SHELL_PATH" -c "\"$RUN\""
12+
ret=$?
13+
rm -f "$RUN"
14+
return $ret
15+
}

t/t0034-root-safe-directory.sh

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/bin/sh
2+
3+
test_description='verify safe.directory checks while running as root'
4+
5+
. ./test-lib.sh
6+
. "$TEST_DIRECTORY"/lib-sudo.sh
7+
8+
if [ "$GIT_TEST_ALLOW_SUDO" != "YES" ]
9+
then
10+
skip_all="You must set env var GIT_TEST_ALLOW_SUDO=YES in order to run this test"
11+
test_done
12+
fi
13+
14+
if ! test_have_prereq NOT_ROOT
15+
then
16+
skip_all="These tests do not support running as root"
17+
test_done
18+
fi
19+
20+
test_lazy_prereq SUDO '
21+
sudo -n id -u >u &&
22+
id -u root >r &&
23+
test_cmp u r &&
24+
command -v git >u &&
25+
sudo command -v git >r &&
26+
test_cmp u r
27+
'
28+
29+
if ! test_have_prereq SUDO
30+
then
31+
skip_all="Your sudo/system configuration is either too strict or unsupported"
32+
test_done
33+
fi
34+
35+
test_expect_success SUDO 'setup' '
36+
sudo rm -rf root &&
37+
mkdir -p root/r &&
38+
(
39+
cd root/r &&
40+
git init
41+
)
42+
'
43+
44+
test_expect_success SUDO 'sudo git status as original owner' '
45+
(
46+
cd root/r &&
47+
git status &&
48+
sudo git status
49+
)
50+
'
51+
52+
test_expect_success SUDO 'setup root owned repository' '
53+
sudo mkdir -p root/p &&
54+
sudo git init root/p
55+
'
56+
57+
test_expect_success 'cannot access if owned by root' '
58+
(
59+
cd root/p &&
60+
test_must_fail git status
61+
)
62+
'
63+
64+
test_expect_success 'can access if addressed explicitly' '
65+
(
66+
cd root/p &&
67+
GIT_DIR=.git GIT_WORK_TREE=. git status
68+
)
69+
'
70+
71+
test_expect_failure SUDO 'can access with sudo if root' '
72+
(
73+
cd root/p &&
74+
sudo git status
75+
)
76+
'
77+
78+
test_expect_success SUDO 'can access with sudo if root by removing SUDO_UID' '
79+
(
80+
cd root/p &&
81+
run_with_sudo <<-END
82+
unset SUDO_UID &&
83+
git status
84+
END
85+
)
86+
'
87+
88+
test_lazy_prereq SUDO_SUDO '
89+
sudo sudo id -u >u &&
90+
id -u root >r &&
91+
test_cmp u r
92+
'
93+
94+
test_expect_success SUDO_SUDO 'can access with sudo abusing SUDO_UID' '
95+
(
96+
cd root/p &&
97+
sudo sudo git status
98+
)
99+
'
100+
101+
# this MUST be always the last test
102+
test_expect_success SUDO 'cleanup' '
103+
sudo rm -rf root
104+
'
105+
106+
test_done

0 commit comments

Comments
 (0)