git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
* [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
@ 2022-04-26 18:31 Carlo Marcelo Arenas Belón
  2022-04-26 19:48 ` Derrick Stolee
  2022-04-27  0:05 ` [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
  0 siblings, 2 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-26 18:31 UTC (permalink / raw)
  To: git
  Cc: philipoakley, me, Carlo Marcelo Arenas Belón, Guy Maurel,
	SZEDER Gábor, Randall Becker, Johannes Schindelin

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that uid was
root (because git was invoked through sudo or doas) and the effetive id
that repositiry trusted for its config was different, therefore failing
the following common call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using those instead.

This assumes the environment the user is running with after going
priviledged can't be tampered with, and also does the check only for
root to keep the most common case less complicated, but as a side effect
will miss cases where sudo (or an equivalent) was used to change to
another unpriviledged user.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
Sent as an RFC as it has been only lightly tested and because some of
the assumptions I have to make, made me unconfortable.

Ex, in order to make the atoi() calls safe, I was originally doing
is_digit(), but that would require this function to move further down
to work.

It is also now big enough that would make sense for it to move into
its own compat file and outside for git-compat-util.h, but if that is
done we might not keep the "root uid is not always 0" bits that seem
useful to have for the future.

getent() is not thread safe, so it might be worth to use an alternative
but that would require a bigger change.

IMHO it should have a test added, but not sure where it would fit.

Original discussion in :

  https://lore.kernel.org/git/4ef9287b-6260-9538-7c89-cffb611520ee@maurel.de/

 git-compat-util.h | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 58fd813bd01..2ed97b47979 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -437,12 +437,34 @@ 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
+
 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) {
+		/* we might have raised our priviledges with sudo or doas */
+		const char *real_uid = getenv("SUDO_UID");
+		if (real_uid && *real_uid)
+			euid = atoi(real_uid);
+		else {
+			real_uid = getenv("DOAS_UID");
+			if (real_uid && *real_uid)
+				euid = atoi(real_uid);
+		}
+	}
+	return st.st_uid == euid;
 }
 
 #define is_path_owned_by_current_user is_path_owned_by_current_uid
-- 
2.36.0.266.g59f845bde02


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 18:31 [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged Carlo Marcelo Arenas Belón
@ 2022-04-26 19:48 ` Derrick Stolee
  2022-04-26 19:56   ` Junio C Hamano
                     ` (2 more replies)
  2022-04-27  0:05 ` [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
  1 sibling, 3 replies; 161+ messages in thread
From: Derrick Stolee @ 2022-04-26 19:48 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git
  Cc: philipoakley, me, Guy Maurel, SZEDER Gábor, Randall Becker,
	Johannes Schindelin

On 4/26/2022 2:31 PM, Carlo Marcelo Arenas Belón wrote:
> bdc77d1d685 (Add a function to determine whether a path is owned by the
> current user, 2022-03-02) checks for the effective uid of the running
> process using geteuid() but didn't account for cases where that uid was
> root (because git was invoked through sudo or doas) and the effetive id

s/effetive/effective

> that repositiry trusted for its config was different, therefore failing

s/repositiry/repository

> the following common call:
> 
>   guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
>   [sudo] password for guy:
>   fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)
> 
> Attempt to detect those cases by using the environment variables that
> those tools create to keep track of the original user id, and do the
> ownership check using those instead.
> 
> This assumes the environment the user is running with after going
> priviledged can't be tampered with, and also does the check only for

s/priviledged/privileged 

> root to keep the most common case less complicated, but as a side effect
> will miss cases where sudo (or an equivalent) was used to change to
> another unpriviledged user.

s/unpriviledged/unpriviledged 

> Sent as an RFC as it has been only lightly tested and because some of
> the assumptions I have to make, made me unconfortable.

 
> Ex, in order to make the atoi() calls safe, I was originally doing
> is_digit(), but that would require this function to move further down
> to work.
>
> It is also now big enough that would make sense for it to move into
> its own compat file and outside for git-compat-util.h, but if that is
> done we might not keep the "root uid is not always 0" bits that seem
> useful to have for the future.
>
> getent() is not thread safe, so it might be worth to use an alternative
> but that would require a bigger change.

These points make sense. It would be worth taking our time and
doing some refactoring in advance of this functional change.
 
> IMHO it should have a test added, but not sure where it would fit.

I wonder how we could test a multi-user scenario. The tests I
added in e47363e5a (t0033: add tests for safe.directory, 2022-04-13)
purposefully avoid the user-id functionality.

> Original discussion in :
> 
>   https://lore.kernel.org/git/4ef9287b-6260-9538-7c89-cffb611520ee@maurel.de/

I agree that the idea behind this change is a good one. The escalation
of privilege isn't a huge concern when the "real" user is the same.
The only way to trick the root user here is to set an environment
variable, in which case they might as well modify PATH and be done with
it.

> +	euid = geteuid();
> +	if (euid == ROOT_UID) {
> +		/* we might have raised our priviledges with sudo or doas */

Similar spelling error here.

> +		const char *real_uid = getenv("SUDO_UID");
> +		if (real_uid && *real_uid)
> +			euid = atoi(real_uid);
> +		else {
> +			real_uid = getenv("DOAS_UID");
> +			if (real_uid && *real_uid)
> +				euid = atoi(real_uid);
> +		}

I imagine that something else could be added here to help Windows
users who have elevated to administrator privileges. It will use a
completely different mechanism, though, if needed at all. We can
delay that for now.

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 19:48 ` Derrick Stolee
@ 2022-04-26 19:56   ` Junio C Hamano
  2022-04-26 20:10     ` rsbecker
  2022-04-26 20:12     ` Carlo Arenas
  2022-04-26 20:26   ` Carlo Arenas
  2022-04-29 16:16   ` Derrick Stolee
  2 siblings, 2 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-26 19:56 UTC (permalink / raw)
  To: Derrick Stolee
  Cc: Carlo Marcelo Arenas Belón, git, philipoakley, me,
	Guy Maurel, SZEDER Gábor, Randall Becker,
	Johannes Schindelin

Derrick Stolee <derrickstolee@github.com> writes:

>> Original discussion in :
>> 
>>   https://lore.kernel.org/git/4ef9287b-6260-9538-7c89-cffb611520ee@maurel.de/
>
> I agree that the idea behind this change is a good one. The escalation
> of privilege isn't a huge concern when the "real" user is the same.
> The only way to trick the root user here is to set an environment
> variable, in which case they might as well modify PATH and be done with
> it.

How much do we really want to trust SUDO_UID or DOSA_UID are telling
the truth, though?

>> +	euid = geteuid();
>> +	if (euid == ROOT_UID) {
>> +		/* we might have raised our priviledges with sudo or doas */
>
> Similar spelling error here.
>
>> +		const char *real_uid = getenv("SUDO_UID");
>> +		if (real_uid && *real_uid)
>> +			euid = atoi(real_uid);
>> +		else {
>> +			real_uid = getenv("DOAS_UID");
>> +			if (real_uid && *real_uid)
>> +				euid = atoi(real_uid);
>> +		}
>
> I imagine that something else could be added here to help Windows
> users who have elevated to administrator privileges. It will use a
> completely different mechanism, though, if needed at all. We can
> delay that for now.
>
> Thanks,
> -Stolee

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 19:56   ` Junio C Hamano
@ 2022-04-26 20:10     ` rsbecker
  2022-04-26 20:45       ` Carlo Arenas
  2022-04-26 20:12     ` Carlo Arenas
  1 sibling, 1 reply; 161+ messages in thread
From: rsbecker @ 2022-04-26 20:10 UTC (permalink / raw)
  To: 'Junio C Hamano', 'Derrick Stolee'
  Cc: 'Carlo Marcelo Arenas Belón',
	git, philipoakley, me, 'Guy Maurel',
	'SZEDER Gábor', 'Johannes Schindelin'

On April 26, 2022 3:56 PM, Junio C Hamano wrote:
>Subject: Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if
>running priviledged
>
>Derrick Stolee <derrickstolee@github.com> writes:
>
>>> Original discussion in :
>>>
>>>
>>> https://lore.kernel.org/git/4ef9287b-6260-9538-7c89-cffb611520ee@maur
>>> el.de/
>>
>> I agree that the idea behind this change is a good one. The escalation
>> of privilege isn't a huge concern when the "real" user is the same.
>> The only way to trick the root user here is to set an environment
>> variable, in which case they might as well modify PATH and be done
>> with it.
>
>How much do we really want to trust SUDO_UID or DOSA_UID are telling the
>truth, though?
>
>>> +	euid = geteuid();
>>> +	if (euid == ROOT_UID) {
>>> +		/* we might have raised our priviledges with sudo or doas */
>>
>> Similar spelling error here.
>>
>>> +		const char *real_uid = getenv("SUDO_UID");
>>> +		if (real_uid && *real_uid)
>>> +			euid = atoi(real_uid);
>>> +		else {
>>> +			real_uid = getenv("DOAS_UID");
>>> +			if (real_uid && *real_uid)
>>> +				euid = atoi(real_uid);

This should be strtol() instead of atoi(). Putting garbage into DOAS_UID might end up causing some unwanted effects since atoi() could then return 0 or some partial value. The result should also be checked for sanity and the end pointer should point to a '\0'. My team has effectively banned the use of atoi() in new code and is migrating to strtol() or strtoll() as code is touched.

>>> +		}
>>
>> I imagine that something else could be added here to help Windows
>> users who have elevated to administrator privileges. It will use a
>> completely different mechanism, though, if needed at all. We can delay
>> that for now.
>>
>> Thanks,
>> -Stolee


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 19:56   ` Junio C Hamano
  2022-04-26 20:10     ` rsbecker
@ 2022-04-26 20:12     ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-26 20:12 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Derrick Stolee, git, philipoakley, me, Guy Maurel,
	SZEDER Gábor, Randall Becker, Johannes Schindelin

On Tue, Apr 26, 2022 at 12:56 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> How much do we really want to trust SUDO_UID or DOSA_UID are telling
> the truth, though?

IMHO since we are only trusting this if the EUID is root it would require that
the root account was compromised already or running in a tampered environment.

for the absolutely paranoid we could trace back the process tree to make sure
the current session was indeed created by that tool, but if we are going that
way I think that trusting the ownership of the pty as was proposed[1] originally
would be simpler and is indeed how other tools (like who) deal with
that problem.

The advantage of trusting these variables is that we can keep the more common
case simpler and avoid the reported regression.

Carlo

[1] https://lore.kernel.org/git/20220425084003.nf267feurpqyvmsd@carlos-mbp.lan/

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 19:48 ` Derrick Stolee
  2022-04-26 19:56   ` Junio C Hamano
@ 2022-04-26 20:26   ` Carlo Arenas
  2022-04-29 16:16   ` Derrick Stolee
  2 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-26 20:26 UTC (permalink / raw)
  To: Derrick Stolee
  Cc: git, philipoakley, me, Guy Maurel, SZEDER Gábor,
	Randall Becker, Johannes Schindelin

On Tue, Apr 26, 2022 at 12:48 PM Derrick Stolee
<derrickstolee@github.com> wrote:

> These points make sense. It would be worth taking our time and
> doing some refactoring in advance of this functional change.

Problem with taking our time or making it a bigger change is that
it is currently broken in the last maint versions so it would be ideal
to backport quickly there as a "regression".

> I wonder how we could test a multi-user scenario. The tests I
> added in e47363e5a (t0033: add tests for safe.directory, 2022-04-13)
> purposefully avoid the user-id functionality.

It would need to be a CI only test (where passwordless sudo is allowed)

> I imagine that something else could be added here to help Windows
> users who have elevated to administrator privileges. It will use a
> completely different mechanism, though, if needed at all. We can
> delay that for now.

Note this function is *NIX only, there is another one for Windows where
a similar fix (if needed) could be implemented

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 20:10     ` rsbecker
@ 2022-04-26 20:45       ` Carlo Arenas
  2022-04-26 21:10         ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-26 20:45 UTC (permalink / raw)
  To: rsbecker
  Cc: Junio C Hamano, Derrick Stolee, git, philipoakley, me,
	Guy Maurel, SZEDER Gábor, Johannes Schindelin

On Tue, Apr 26, 2022 at 1:10 PM <rsbecker@nexbridge.com> wrote:
>
> Putting garbage into DOAS_UID might end up causing some unwanted effects

Since it was the root user who put garbage there, we will have to
trust it was not
unwanted.  My proposal to use is_digit() was to make sure we didn't get garbage
from the getenv() call (ex: "") that would confuse the logic, but if
there is some sudo
version that is saving the uid as "ThisIsGarbage" then that is a bug
better handled
somewhere else.

Agree with you that using strtol is better, but the added checks and
logic make it
more complicated and go against the assumption made in the commit message
that the environment CAN'T be tampered with.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 20:45       ` Carlo Arenas
@ 2022-04-26 21:10         ` Junio C Hamano
  0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-26 21:10 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: rsbecker, Derrick Stolee, git, philipoakley, me, Guy Maurel,
	SZEDER Gábor, Johannes Schindelin

Carlo Arenas <carenas@gmail.com> writes:

> Agree with you that using strtol is better, but the added checks
> and logic make it more complicated and go against the assumption
> made in the commit message that the environment CAN'T be tampered
> with.

I think we require strto*l() here, to relieve us from worrying about
"is int large enough?" question.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-26 18:31 [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged Carlo Marcelo Arenas Belón
  2022-04-26 19:48 ` Derrick Stolee
@ 2022-04-27  0:05 ` Carlo Marcelo Arenas Belón
  2022-04-27  9:33   ` Phillip Wood
  2022-04-27 22:26   ` [RFC PATCH v2] " Carlo Marcelo Arenas Belón
  1 sibling, 2 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-27  0:05 UTC (permalink / raw)
  To: git
  Cc: philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Carlo Marcelo Arenas Belón,
	Randall Becker, Johannes Schindelin

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that uid was
root (because git was invoked through sudo or a compatible tool) and the
original user that repository trusted for its config was different,
therefore failing the following common use case:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
sudo or compatible tools create to keep track of the original user id,
and do the ownership check using that instead.

This assumes the environment the user is running with after going
privileged can't be tampered with, and also does the check only for
root to keep the most common case less complicated, but as a side effect
will miss cases where sudo (or an equivalent) was used to change to
another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
Changes since RFC
* Addresses all spelling errors, even the ones not reported and even if I
  am sure "priviledged" is a nicer sounding word even if obsoleted.
* Uses strtol instead of atoi as suggested by Randall and Junio, the extra
  error checking was too much to handle inline so a new helper function
  was added.
* Removes checks for DOAS_UID, in an attempt to make the change smaller
  and because that is part of an extention that might not be that common.
  This means `doas` is still broken, but that was punted for now.
* Has been tested a little more, but is still missing a test case, but
  as Derrick pointed out doing so is not trivial, so punted for now.

 git-compat-util.h | 38 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 37 insertions(+), 1 deletion(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 58fd813bd01..9bb0eb5087a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -437,12 +437,48 @@ 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
+
+/*
+ * this helper function overrides a ROOT_UID with the one provided by
+ * an environment variable, do not use unless the original uid is
+ * root
+ */
+static inline int extract_id_from_env(const char *env, uid_t *id)
+{
+	const char *real_uid = getenv(env);
+
+	if (real_uid && *real_uid) {
+		char *error;
+		long extracted_id = strtol(real_uid, &error, 10);
+		if (!*error && LONG_MIN < extracted_id &&
+				extracted_id < LONG_MAX) {
+			*id = (uid_t)extracted_id;
+			return 1;
+		}
+	}
+	return 0;
+}
+
 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) {
+		/* we might have raised our privileges with sudo */
+		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
-- 
2.36.0.266.g59f845bde02


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27  0:05 ` [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-04-27  9:33   ` Phillip Wood
  2022-04-27 12:30     ` Phillip Wood
  2022-04-27 15:38     ` Carlo Arenas
  2022-04-27 22:26   ` [RFC PATCH v2] " Carlo Marcelo Arenas Belón
  1 sibling, 2 replies; 161+ messages in thread
From: Phillip Wood @ 2022-04-27  9:33 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git
  Cc: philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

Hi Carlo

On 27/04/2022 01:05, Carlo Marcelo Arenas Belón wrote:
> bdc77d1d685 (Add a function to determine whether a path is owned by the
> current user, 2022-03-02) checks for the effective uid of the running
> process using geteuid() but didn't account for cases where that uid was
> root (because git was invoked through sudo or a compatible tool) and the
> original user that repository trusted for its config was different,
> therefore failing the following common use case:
> 
>    guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
>    [sudo] password for guy:
>    fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)
> 
> Attempt to detect those cases by using the environment variables that
> sudo or compatible tools create to keep track of the original user id,
> and do the ownership check using that instead.
> 
> This assumes the environment the user is running with after going
> privileged can't be tampered with, and also does the check only for
> root to keep the most common case less complicated, but as a side effect
> will miss cases where sudo (or an equivalent) was used to change to
> another unprivileged user or where the equivalent tool used to raise
> privileges didn't track the original id in a sudo compatible way.
> 
> Reported-by: Guy Maurel <guy.j@maurel.de>
> Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
> Helped-by: Randall Becker <rsbecker@nexbridge.com>
> Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
> Changes since RFC
> * Addresses all spelling errors, even the ones not reported and even if I
>    am sure "priviledged" is a nicer sounding word even if obsoleted.
> * Uses strtol instead of atoi as suggested by Randall and Junio, the extra
>    error checking was too much to handle inline so a new helper function
>    was added.
> * Removes checks for DOAS_UID, in an attempt to make the change smaller
>    and because that is part of an extention that might not be that common.
>    This means `doas` is still broken, but that was punted for now.
> * Has been tested a little more, but is still missing a test case, but
>    as Derrick pointed out doing so is not trivial, so punted for now.
> 
>   git-compat-util.h | 38 +++++++++++++++++++++++++++++++++++++-
>   1 file changed, 37 insertions(+), 1 deletion(-)
> 
> diff --git a/git-compat-util.h b/git-compat-util.h
> index 58fd813bd01..9bb0eb5087a 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -437,12 +437,48 @@ 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
> +
> +/*
> + * this helper function overrides a ROOT_UID with the one provided by
> + * an environment variable, do not use unless the original uid is
> + * root
> + */
> +static inline int extract_id_from_env(const char *env, uid_t *id)

Do we really want this living in git-compat-util.h?

> +{
> +	const char *real_uid = getenv(env);
> +
> +	if (real_uid && *real_uid) {
> +		char *error;
> +		long extracted_id = strtol(real_uid, &error, 10);
> +		if (!*error && LONG_MIN < extracted_id &&
> +				extracted_id < LONG_MAX) {

strtol() returns a long so the last two checks are redundant. The 
standard is silent on what happens to error when the value is out of 
range. The way to check that all the string was consumed without 
underflow/overflow is

	errno = 0;
	val = strtol(str, &endp, 10);
	if (errno || !*endp)
		error(...)

In this case I think negative values make no sense as I believe uids are 
always positive integers so we should add a check for "extracted_id < 0"

> +			*id = (uid_t)extracted_id;

There is a potential integer truncation here which could lead to false 
positives. I'm not sure that there is a way to query the maximum valid 
uid but we could do

	#if sizeof(uid_t) == sizeof(int)
		if (extracted_id > INT_MAX)
			error(...)
	#endif

Unfortunately I think we need to use #if rather than if to prevent 
compilers complaining that the condition is always true or always false.

Checking the real user id from an environment variable makes me feel a 
bit queasy but as others have pointed out if an attacker can mess with 
the users environment then they can already override $PATH.

Best Wishes

Phillip

> +			return 1;
> +		}
> +	}
> +	return 0;
> +}
> +
>   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) {
> +		/* we might have raised our privileges with sudo */
> +		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


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27  9:33   ` Phillip Wood
@ 2022-04-27 12:30     ` Phillip Wood
  2022-04-27 14:15       ` rsbecker
  2022-04-27 15:58       ` Carlo Arenas
  2022-04-27 15:38     ` Carlo Arenas
  1 sibling, 2 replies; 161+ messages in thread
From: Phillip Wood @ 2022-04-27 12:30 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git
  Cc: philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On 27/04/2022 10:33, Phillip Wood wrote:
> Hi Carlo
> 
> On 27/04/2022 01:05, Carlo Marcelo Arenas Belón wrote:

> [...]
>> +{
>> +    const char *real_uid = getenv(env);
>> +
>> +    if (real_uid && *real_uid) {
>> +        char *error;
>> +        long extracted_id = strtol(real_uid, &error, 10);
>> +        if (!*error && LONG_MIN < extracted_id &&
>> +                extracted_id < LONG_MAX) {
> 
> strtol() returns a long so the last two checks are redundant. The 
> standard is silent on what happens to error when the value is out of 
> range. The way to check that all the string was consumed without 
> underflow/overflow is
> 
>      errno = 0;
>      val = strtol(str, &endp, 10);
>      if (errno || !*endp)

Sorry that should be "if (errno || *endp)" to check for an error

 > [...]
 >     #if sizeof(uid_t) == sizeof(int)
 >         if (extracted_id > INT_MAX)
 >             error(...)
 >     #endif

I think we should probably check if uid_t is a short integer as well as

 > +#ifdef __TANDEM
 > +#define ROOT_UID 65535

suggests it may be an unsigned short on NonStop.

Not knowing the size of uid_t or if it is signed or not (as far as I can 
tell posix just says it's an integer) makes the limit checks tricky - 
maybe we make euid a long (or unsigned long) here and it the function 
below rather than casting it to uid_t and possibly truncating it.

 > [...]
>> +
>>   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) {
>> +        /* we might have raised our privileges with sudo */
>> +        extract_id_from_env("SUDO_UID", &euid);

You are ignoring any errors when parsing the environment variable - that 
is not a good idea in a security check.

Best Wishes

Phillip

>> +    }
>> +    return st.st_uid == euid;
>>   }
>>   #define is_path_owned_by_current_user is_path_owned_by_current_uid
> 


^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 12:30     ` Phillip Wood
@ 2022-04-27 14:15       ` rsbecker
  2022-04-27 15:58       ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-04-27 14:15 UTC (permalink / raw)
  To: phillip.wood, 'Carlo Marcelo Arenas Belón', git
  Cc: philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee

On April 27, 2022 8:31 AM, Phillip Wood wrote:
>On 27/04/2022 10:33, Phillip Wood wrote:
>> Hi Carlo
>>
>> On 27/04/2022 01:05, Carlo Marcelo Arenas Belón wrote:
>
>> [...]
>>> +{
>>> +    const char *real_uid = getenv(env);
>>> +
>>> +    if (real_uid && *real_uid) {
>>> +        char *error;
>>> +        long extracted_id = strtol(real_uid, &error, 10);
>>> +        if (!*error && LONG_MIN < extracted_id &&
>>> +                extracted_id < LONG_MAX) {
>>
>> strtol() returns a long so the last two checks are redundant. The
>> standard is silent on what happens to error when the value is out of
>> range. The way to check that all the string was consumed without
>> underflow/overflow is
>>
>>      errno = 0;
>>      val = strtol(str, &endp, 10);
>>      if (errno || !*endp)
>
>Sorry that should be "if (errno || *endp)" to check for an error
>
> > [...]
> >     #if sizeof(uid_t) == sizeof(int)
> >         if (extracted_id > INT_MAX)
> >             error(...)
> >     #endif
>
>I think we should probably check if uid_t is a short integer as well as
>
> > +#ifdef __TANDEM
> > +#define ROOT_UID 65535
>
>suggests it may be an unsigned short on NonStop.
>
>Not knowing the size of uid_t or if it is signed or not (as far as I can tell posix just
>says it's an integer) makes the limit checks tricky - maybe we make euid a long (or
>unsigned long) here and it the function below rather than casting it to uid_t and
>possibly truncating it.

It depends on the compile. If LP64 is used - long file pointers, then it is a long (32-bit) or an int (also 32-bit). Not unsigned in either case, but specifying unsigned would be helpful. Nice catch.

> > [...]
>>> +
>>>   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) {
>>> +        /* we might have raised our privileges with sudo */
>>> +        extract_id_from_env("SUDO_UID", &euid);
>
>You are ignoring any errors when parsing the environment variable - that is not a
>good idea in a security check.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27  9:33   ` Phillip Wood
  2022-04-27 12:30     ` Phillip Wood
@ 2022-04-27 15:38     ` Carlo Arenas
  2022-04-27 15:50       ` rsbecker
                         ` (2 more replies)
  1 sibling, 3 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 15:38 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On Wed, Apr 27, 2022 at 2:33 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 27/04/2022 01:05, Carlo Marcelo Arenas Belón wrote:
> > diff --git a/git-compat-util.h b/git-compat-util.h
> > index 58fd813bd01..9bb0eb5087a 100644
> > --- a/git-compat-util.h
> > +++ b/git-compat-util.h
> > @@ -437,12 +437,48 @@ 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
> > +
> > +/*
> > + * this helper function overrides a ROOT_UID with the one provided by
> > + * an environment variable, do not use unless the original uid is
> > + * root
> > + */
> > +static inline int extract_id_from_env(const char *env, uid_t *id)
>
> Do we really want this living in git-compat-util.h?

No; but IMHO the same applies to is_path_owned_by_current_uid(), and
as I mentioned in my original proposal refactoring this code to do so
has been punted until later since the objective here was to keep the
change as small as possible for clean backporting.

My intention with that comment was not only to warn people that might
want to reuse that helper but to indicate it was just a hack that
should be refactored ASAP.

FWIW, I still think that using atoi with a check to skip "" is
probably as safe as doing all this extra checking as no one has shown
yet a system where sizeof(uid_t) > sizeof(uint32_t), but agree with
Junio that using long instead avoids issues with the systems where
sizeof(uid_t) > sizeof(int) and unless sizeof(int) == sizeof(long)
(ex: 32-bit Linux) which is then covered by the cast.

> > +{
> > +     const char *real_uid = getenv(env);
> > +
> > +     if (real_uid && *real_uid) {
> > +             char *error;
> > +             long extracted_id = strtol(real_uid, &error, 10);
> > +             if (!*error && LONG_MIN < extracted_id &&
> > +                             extracted_id < LONG_MAX) {
>
> strtol() returns a long so the last two checks are redundant.

The last two checks were to check for underflow or overflow and to
make sure that the bogus values this function returns in case of those
errors are not taken into consideration.

>The standard is silent on what happens to error when the value is out of
> range.

Which is why instead I was avoiding LONG_{MIN,MAX} which are
described[1] as the bogus values that will be used in that case
Agree with you that we could also add a check for >=0 as uid_t is
usually unsigned, but it is not warranted to be so, and that is I was
aiming for the wider possible range so we don't have to worry that
much and let it be settled to a valid value through the cast.

Carlo

[1] https://pubs.opengroup.org/onlinepubs/007904875/functions/strtol.html

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 15:38     ` Carlo Arenas
@ 2022-04-27 15:50       ` rsbecker
  2022-04-27 16:19       ` Junio C Hamano
  2022-04-27 16:31       ` Phillip Wood
  2 siblings, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-04-27 15:50 UTC (permalink / raw)
  To: 'Carlo Arenas', phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee

On April 27, 2022 11:39 AM, Carlo Arenas wrote:
>On Wed, Apr 27, 2022 at 2:33 AM Phillip Wood <phillip.wood123@gmail.com>
>wrote:
>> On 27/04/2022 01:05, Carlo Marcelo Arenas Belón wrote:
>> > diff --git a/git-compat-util.h b/git-compat-util.h index
>> > 58fd813bd01..9bb0eb5087a 100644
>> > --- a/git-compat-util.h
>> > +++ b/git-compat-util.h
>> > @@ -437,12 +437,48 @@ 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
>> > +
>> > +/*
>> > + * this helper function overrides a ROOT_UID with the one provided
>> > +by
>> > + * an environment variable, do not use unless the original uid is
>> > + * root
>> > + */
>> > +static inline int extract_id_from_env(const char *env, uid_t *id)
>>
>> Do we really want this living in git-compat-util.h?
>
>No; but IMHO the same applies to is_path_owned_by_current_uid(), and as I
>mentioned in my original proposal refactoring this code to do so has been punted
>until later since the objective here was to keep the change as small as possible for
>clean backporting.
>
>My intention with that comment was not only to warn people that might want to
>reuse that helper but to indicate it was just a hack that should be refactored ASAP.
>
>FWIW, I still think that using atoi with a check to skip "" is probably as safe as doing
>all this extra checking as no one has shown yet a system where sizeof(uid_t) >
>sizeof(uint32_t), but agree with Junio that using long instead avoids issues with
>the systems where
>sizeof(uid_t) > sizeof(int) and unless sizeof(int) == sizeof(long)
>(ex: 32-bit Linux) which is then covered by the cast.
>
>> > +{
>> > +     const char *real_uid = getenv(env);
>> > +
>> > +     if (real_uid && *real_uid) {
>> > +             char *error;
>> > +             long extracted_id = strtol(real_uid, &error, 10);
>> > +             if (!*error && LONG_MIN < extracted_id &&
>> > +                             extracted_id < LONG_MAX) {
>>
>> strtol() returns a long so the last two checks are redundant.
>
>The last two checks were to check for underflow or overflow and to make sure
>that the bogus values this function returns in case of those errors are not taken
>into consideration.
>
>>The standard is silent on what happens to error when the value is out
>>of  range.
>
>Which is why instead I was avoiding LONG_{MIN,MAX} which are described[1] as
>the bogus values that will be used in that case Agree with you that we could also
>add a check for >=0 as uid_t is usually unsigned, but it is not warranted to be so,
>and that is I was aiming for the wider possible range so we don't have to worry
>that much and let it be settled to a valid value through the cast.

Somehow, the root user id is a compat issue. Perhaps we need a uid_t getRootUid() in git-compat-util.h, and hide the details of the proc from the interface.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 12:30     ` Phillip Wood
  2022-04-27 14:15       ` rsbecker
@ 2022-04-27 15:58       ` Carlo Arenas
  2022-04-27 16:14         ` Phillip Wood
  1 sibling, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 15:58 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On Wed, Apr 27, 2022 at 5:30 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> You are ignoring any errors when parsing the environment variable - that
> is not a good idea in a security check.

which errors are you concerned about?, if anything in this code
worries me from a security point of view is the fact that we are
relying in getenv not being racy (as mentioned in the original RFC),
but there are no errors set there AFAIK.

not ignoring errno in strtol is an option, but as mentioned before I
decided instead to reject bogus values and therefore not the clobber a
previous errno, since I was using strtol as a wider version of atoi.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 15:58       ` Carlo Arenas
@ 2022-04-27 16:14         ` Phillip Wood
  2022-04-27 18:54           ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-04-27 16:14 UTC (permalink / raw)
  To: Carlo Arenas, phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On 27/04/2022 16:58, Carlo Arenas wrote:
> On Wed, Apr 27, 2022 at 5:30 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> You are ignoring any errors when parsing the environment variable - that
>> is not a good idea in a security check.
> 
> which errors are you concerned about?, if anything in this code

I was confused by the fact that the helper function returns a 
success/failure value which we ignore. However euid is not overwritten 
if strtol fails so it is safe I think.

> worries me from a security point of view is the fact that we are
> relying in getenv not being racy (as mentioned in the original RFC),
> but there are no errors set there AFAIK.
> 
> not ignoring errno in strtol is an option, but as mentioned before I
> decided instead to reject bogus values and therefore not the clobber a
> previous errno,

strtol() will set errno if there is a range error ignoring it does not 
change that. In any case is_path_owned_by_current_uid() already clobbers 
errno if stat() fails.

Best Wishes

Phillip

> since I was using strtol as a wider version of atoi.


> Carlo


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 15:38     ` Carlo Arenas
  2022-04-27 15:50       ` rsbecker
@ 2022-04-27 16:19       ` Junio C Hamano
  2022-04-27 16:45         ` Carlo Arenas
  2022-04-27 17:22         ` Phillip Wood
  2022-04-27 16:31       ` Phillip Wood
  2 siblings, 2 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-27 16:19 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: phillip.wood, git, philipoakley, me, guy.j, szeder.dev,
	johannes.Schindelin, derrickstolee, Randall Becker

Carlo Arenas <carenas@gmail.com> writes:

>> > +     if (real_uid && *real_uid) {
>> > +             char *error;
>> > +             long extracted_id = strtol(real_uid, &error, 10);
>> > +             if (!*error && LONG_MIN < extracted_id &&
>> > +                             extracted_id < LONG_MAX) {
>>
>> strtol() returns a long so the last two checks are redundant.
>
> The last two checks were to check for underflow or overflow and to
> make sure that the bogus values this function returns in case of those
> errors are not taken into consideration.
>
>>The standard is silent on what happens to error when the value is out of
>> range.

Actually the standard is is very clear what happens to endptr (no,
don't call it "error", that is not the point of the parameter).

    A pointer to the final string shall be stored in the object
    pointed to by endptr, provided that endptr is not a null
    pointer.

where "final string" has a precise definition much earlier:

    First, they decompose the input string into three parts:

    1. An initial, possibly empty, sequence of white-space
       characters (as specified by isspace())

    2. A subject sequence interpreted as an integer represented in
       some radix determined by the value of base

    3. A final string of one or more unrecognized characters,
       including the terminating null byte of the input string.

    Then they shall attempt to convert the subject sequence to an
    integer, and return the result.

So, leading whitespace is stripped, then "subject sequence" that is
the sequence of digits (with optional +/- sign) to be turned into a
long is recognised, and what remains is the "final string".  endptr
is made to point at that "final string", and it does not matter what
kind of value the interpretation of "subject sequence" yields.

And that is why it is wrong to call it "error".  It is not about
errors, but is about the shape of the input string, i.e. where the
run of digits ends.

> Which is why instead I was avoiding LONG_{MIN,MAX} which are
> described[1] as the bogus values that will be used in that case

strtol() is designed to return LONG_MIN and LONG_MAX when values
overflow or underflow, but that does not mean it returns these two
values only when the input overflows or underflows.  If it were
strtouint8(), giving it "255" will give 255, while you would get
UCHAR_MAX when the input makes it overflow, e.g. "1000", and from
the returned value you cannot tell which is which because UCHAR_MAX
is 255 ;-)

IOW, you are reading the standard wrong.  It does not say that
LONG_MIN and LONG_MAX are bogus values at all.

And theree is this note:

    Since 0, {LONG_MIN} or {LLONG_MIN}, and {LONG_MAX} or {LLONG_MAX}
    are returned on error and are also valid returns on success, an
    application wishing to check for error situations should set errno
    to 0, then call strtol() or strtoll(), then check errno.

So, no, I do not think the range check gives us anything
meaningful.

At this point, it is tempting to say that it is not worth trying to
do it right by avoiding atoi(), as we are bound to make more of this
kind of mistakes X-<.  But at the same time, I hope we can learn
together and get this right ;-).

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 15:38     ` Carlo Arenas
  2022-04-27 15:50       ` rsbecker
  2022-04-27 16:19       ` Junio C Hamano
@ 2022-04-27 16:31       ` Phillip Wood
  2022-04-27 16:54         ` Carlo Arenas
  2 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-04-27 16:31 UTC (permalink / raw)
  To: Carlo Arenas, phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On 27/04/2022 16:38, Carlo Arenas wrote:
> On Wed, Apr 27, 2022 at 2:33 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 27/04/2022 01:05, Carlo Marcelo Arenas Belón wrote:
> [...]
> FWIW, I still think that using atoi with a check to skip "" is
> probably as safe as doing all this extra checking as no one has shown
> yet a system where sizeof(uid_t) > sizeof(uint32_t), but agree with
> Junio that using long instead avoids issues with the systems where
> sizeof(uid_t) > sizeof(int) and unless sizeof(int) == sizeof(long)
> (ex: 32-bit Linux) which is then covered by the cast.

if sizeof(uid_t) < sizeof(long) then the cast will truncate the value 
returned by strtol() which means we are trusting that SUDO_UID is a 
valid uid otherwise it will be truncated.

>>> +{
>>> +     const char *real_uid = getenv(env);
>>> +
>>> +     if (real_uid && *real_uid) {
>>> +             char *error;
>>> +             long extracted_id = strtol(real_uid, &error, 10);
>>> +             if (!*error && LONG_MIN < extracted_id &&
>>> +                             extracted_id < LONG_MAX) {
>>
>> strtol() returns a long so the last two checks are redundant.
> 
> The last two checks were to check for underflow or overflow and to
> make sure that the bogus values this function returns in case of those
> errors are not taken into consideration.

Sorry I misread the code - this is checking if the value is valid, not 
if there is an error. On platforms where uid_t and long are the same 
size (e.g. where int and long are both 32 bits) the "< LONG_MAX" check 
is rejecting a valid uid. The fact that we are doing these checks makes 
me think we should care about the possible truncation above.

Best Wishes

Phillip

>> The standard is silent on what happens to error when the value is out of
>> range.
> 
> Which is why instead I was avoiding LONG_{MIN,MAX} which are
> described[1] as the bogus values that will be used in that case
> Agree with you that we could also add a check for >=0 as uid_t is
> usually unsigned, but it is not warranted to be so, and that is I was
> aiming for the wider possible range so we don't have to worry that
> much and let it be settled to a valid value through the cast.
> 
> Carlo
> 
> [1] https://pubs.opengroup.org/onlinepubs/007904875/functions/strtol.html


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 16:19       ` Junio C Hamano
@ 2022-04-27 16:45         ` Carlo Arenas
  2022-04-27 17:22         ` Phillip Wood
  1 sibling, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 16:45 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: phillip.wood, git, philipoakley, me, guy.j, szeder.dev,
	johannes.Schindelin, derrickstolee, Randall Becker

On Wed, Apr 27, 2022 at 9:19 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> And that is why it is wrong to call it "error".  It is not about
> errors, but is about the shape of the input string, i.e. where the
> run of digits ends.

I called it error, because I only cared about it to signal that the
string it converted was an erroneous one (ex: "1a" or "a")

> And there is this note:
>
>     Since 0, {LONG_MIN} or {LLONG_MIN}, and {LONG_MAX} or {LLONG_MAX}
>     are returned on error and are also valid returns on success, an
>     application wishing to check for error situations should set errno
>     to 0, then call strtol() or strtoll(), then check errno.
>
> So, no, I do not think the range check gives us anything
> meaningful.

the meaningful part of it was meant to be:

this function will try to get the value of an UID from SUDO_UID, if it
gets anything insane like LONG_MIN or LONG_MAX or anything above or
below that, it would consider you are just trying to play a bad joke
with it and ignore you.

> At this point, it is tempting to say that it is not worth trying to
> do it right by avoiding atoi(), as we are bound to make more of this
> kind of mistakes X-<.  But at the same time, I hope we can learn
> together and get this right ;-).

except it wasn't a mistake, but maybe poorly documented since I was
already going over my quota of added lines for what should have been a
3 line patch.

apologies for making the code so cryptic and thanks for spending so
much time reviewing it and commenting on it, has been definitely very
instructive so far.

any specific suggestions you would like to have in a reroll?

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 16:31       ` Phillip Wood
@ 2022-04-27 16:54         ` Carlo Arenas
  2022-04-27 17:28           ` Phillip Wood
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 16:54 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On Wed, Apr 27, 2022 at 9:31 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 27/04/2022 16:38, Carlo Arenas wrote:
> > FWIW, I still think that using atoi with a check to skip "" is
> > probably as safe as doing all this extra checking as no one has shown
> > yet a system where sizeof(uid_t) > sizeof(uint32_t), but agree with
> > Junio that using long instead avoids issues with the systems where
> > sizeof(uid_t) > sizeof(int) and unless sizeof(int) == sizeof(long)
> > (ex: 32-bit Linux) which is then covered by the cast.
>
> if sizeof(uid_t) < sizeof(long) then the cast will truncate the value
> returned by strtol() which means we are trusting that SUDO_UID is a
> valid uid otherwise it will be truncated.

correct, this whole procedure relies on the fact that SUDO_UID is not
a bogus value (ex: it was produced by a non buggy sudo and hasn't been
tampered with)

in systems where sizeof(uid_t) < sizeof(long), it is expected that the
id we got should be able to fit in an uid_t so no truncation will ever
happen.

the only thing that worries me is sign extension but that is why I put
a specific cast.  for all practical reasons I expect uid_t to be
uint32_t and therefore using long should be better than using int
(through atoi)

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 16:19       ` Junio C Hamano
  2022-04-27 16:45         ` Carlo Arenas
@ 2022-04-27 17:22         ` Phillip Wood
  2022-04-27 17:49           ` rsbecker
  1 sibling, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-04-27 17:22 UTC (permalink / raw)
  To: Junio C Hamano, Carlo Arenas
  Cc: phillip.wood, git, philipoakley, me, guy.j, szeder.dev,
	johannes.Schindelin, derrickstolee, Randall Becker

On 27/04/2022 17:19, Junio C Hamano wrote:
> Carlo Arenas <carenas@gmail.com> writes:
>>> The standard is silent on what happens to error when the value is out of
>>> range.
> 
> Actually the standard is is very clear what happens to endptr (no,
> don't call it "error", that is not the point of the parameter).
 >
>      A pointer to the final string shall be stored in the object
>      pointed to by endptr, provided that endptr is not a null
>      pointer.
> 
> where "final string" has a precise definition much earlier:
> 
>      First, they decompose the input string into three parts:
> 
>      1. An initial, possibly empty, sequence of white-space
>         characters (as specified by isspace())
> 
>      2. A subject sequence interpreted as an integer represented in
>         some radix determined by the value of base
> 
>      3. A final string of one or more unrecognized characters,
>         including the terminating null byte of the input string.
> 
>      Then they shall attempt to convert the subject sequence to an
>      integer, and return the result.
> 
> So, leading whitespace is stripped, then "subject sequence" that is
> the sequence of digits (with optional +/- sign) to be turned into a
> long is recognised, and what remains is the "final string".  endptr
> is made to point at that "final string", and it does not matter what
> kind of value the interpretation of "subject sequence" yields.

Oh I think I misunderstood when I read the standard this morning. Just 
to check I do understand now, in the case of overflow entptr points to 
the final string (i.e. to the character following the last digit) even 
though the "subject sequence" cannot be successfully converted?

Thanks

Phillip

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 16:54         ` Carlo Arenas
@ 2022-04-27 17:28           ` Phillip Wood
  2022-04-27 17:49             ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-04-27 17:28 UTC (permalink / raw)
  To: Carlo Arenas, phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On 27/04/2022 17:54, Carlo Arenas wrote:
> On Wed, Apr 27, 2022 at 9:31 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 27/04/2022 16:38, Carlo Arenas wrote:
>>> FWIW, I still think that using atoi with a check to skip "" is
>>> probably as safe as doing all this extra checking as no one has shown
>>> yet a system where sizeof(uid_t) > sizeof(uint32_t), but agree with
>>> Junio that using long instead avoids issues with the systems where
>>> sizeof(uid_t) > sizeof(int) and unless sizeof(int) == sizeof(long)
>>> (ex: 32-bit Linux) which is then covered by the cast.
>>
>> if sizeof(uid_t) < sizeof(long) then the cast will truncate the value
>> returned by strtol() which means we are trusting that SUDO_UID is a
>> valid uid otherwise it will be truncated.
> 
> correct, this whole procedure relies on the fact that SUDO_UID is not
> a bogus value (ex: it was produced by a non buggy sudo and hasn't been
> tampered with)
> 
> in systems where sizeof(uid_t) < sizeof(long), it is expected that the
> id we got should be able to fit in an uid_t so no truncation will ever
> happen.
> 
> the only thing that worries me is sign extension but that is why I put
> a specific cast.  for all practical reasons I expect uid_t to be
> uint32_t and therefore using long should be better than using int
> (through atoi)


If we think uid_t is a uint32_t then should we be using strtoul() to 
make sure we cover the whole uid range where sizeof(long) == 
sizeof(uint32_t)?

Best Wishes

Phillip

> Carlo


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 17:28           ` Phillip Wood
@ 2022-04-27 17:49             ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 17:49 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	gitster, derrickstolee, Randall Becker

On Wed, Apr 27, 2022 at 10:28 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 27/04/2022 17:54, Carlo Arenas wrote:
> >
> > the only thing that worries me is sign extension but that is why I put
> > a specific cast.  for all practical reasons I expect uid_t to be
> > uint32_t and therefore using long should be better than using int
> > (through atoi)

Well, just because I think that is the most likely option, doesn't
mean it will be so since it is not defined as such in the standard.
I should have documented though that I was (probably incorrectly)
prioritizing the possibility of supporting negative uids instead of
positive uids > INT_MAX.

This of course only matters in 32bit, but looking at sudo's sources
they use "%u" to set the UID in the environment and therefore we
should change our approach to match.

> If we think uid_t is a uint32_t then should we be using strtoul() to
> make sure we cover the whole uid range where sizeof(long) ==
> sizeof(uint32_t)?

strtoul is sadly not very portable, but I think you are correct that
it should be used instead
originally I thought it would be better to do strtoimax but that will
also require moving this function around.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 17:22         ` Phillip Wood
@ 2022-04-27 17:49           ` rsbecker
  2022-04-27 17:54             ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: rsbecker @ 2022-04-27 17:49 UTC (permalink / raw)
  To: phillip.wood, 'Junio C Hamano', 'Carlo Arenas'
  Cc: git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	derrickstolee

On April 27, 2022 1:22 PM, Phillip Wood wrote:
>On 27/04/2022 17:19, Junio C Hamano wrote:
>> Carlo Arenas <carenas@gmail.com> writes:
>>>> The standard is silent on what happens to error when the value is
>>>> out of range.
>>
>> Actually the standard is is very clear what happens to endptr (no,
>> don't call it "error", that is not the point of the parameter).
> >
>>      A pointer to the final string shall be stored in the object
>>      pointed to by endptr, provided that endptr is not a null
>>      pointer.
>>
>> where "final string" has a precise definition much earlier:
>>
>>      First, they decompose the input string into three parts:
>>
>>      1. An initial, possibly empty, sequence of white-space
>>         characters (as specified by isspace())
>>
>>      2. A subject sequence interpreted as an integer represented in
>>         some radix determined by the value of base
>>
>>      3. A final string of one or more unrecognized characters,
>>         including the terminating null byte of the input string.
>>
>>      Then they shall attempt to convert the subject sequence to an
>>      integer, and return the result.
>>
>> So, leading whitespace is stripped, then "subject sequence" that is
>> the sequence of digits (with optional +/- sign) to be turned into a
>> long is recognised, and what remains is the "final string".  endptr is
>> made to point at that "final string", and it does not matter what kind
>> of value the interpretation of "subject sequence" yields.
>
>Oh I think I misunderstood when I read the standard this morning. Just to check I
>do understand now, in the case of overflow entptr points to the final string (i.e. to
>the character following the last digit) even though the "subject sequence" cannot
>be successfully converted?

I can confirm this on multiple 32-bit platforms. With strtol("123456789012345678", &endptr, 10) returns 2147483647 and *endptr == '\0' just beyond the last 8.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 17:49           ` rsbecker
@ 2022-04-27 17:54             ` Carlo Arenas
  2022-04-27 18:05               ` rsbecker
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 17:54 UTC (permalink / raw)
  To: rsbecker
  Cc: phillip.wood, Junio C Hamano, git, philipoakley, me, guy.j,
	szeder.dev, johannes.Schindelin, derrickstolee

On Wed, Apr 27, 2022 at 10:49 AM <rsbecker@nexbridge.com> wrote:
>
> I can confirm this on multiple 32-bit platforms. With strtol("123456789012345678", &endptr, 10) returns 2147483647 and *endptr == '\0' just beyond the last 8.

can you also confirm that you have strtoul and works the same, before
I break NON-STOP in my next reroll?

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 17:54             ` Carlo Arenas
@ 2022-04-27 18:05               ` rsbecker
  2022-04-27 18:11                 ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: rsbecker @ 2022-04-27 18:05 UTC (permalink / raw)
  To: 'Carlo Arenas'
  Cc: phillip.wood, 'Junio C Hamano',
	git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	derrickstolee

On April 27, 2022 1:54 PM, Carlo Arenas wrote:
>To: rsbecker@nexbridge.com
>Cc: phillip.wood@dunelm.org.uk; Junio C Hamano <gitster@pobox.com>;
>git@vger.kernel.org; philipoakley@iee.email; me@ttaylorr.com;
>guy.j@maurel.de; szeder.dev@gmail.com; johannes.Schindelin@gmx.de;
>derrickstolee@github.com
>Subject: Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running
>privileged
>
>On Wed, Apr 27, 2022 at 10:49 AM <rsbecker@nexbridge.com> wrote:
>>
>> I can confirm this on multiple 32-bit platforms. With
>strtol("123456789012345678", &endptr, 10) returns 2147483647 and *endptr == '\0'
>just beyond the last 8.
>
>can you also confirm that you have strtoul and works the same, before I break
>NON-STOP in my next reroll?

Results on NonStop: strtol("123456789012345678", &endptr, 10) returns 4294967295 and *endptr == '\0' just beyond the last 8.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 18:05               ` rsbecker
@ 2022-04-27 18:11                 ` Carlo Arenas
  2022-04-27 18:16                   ` rsbecker
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 18:11 UTC (permalink / raw)
  To: rsbecker
  Cc: phillip.wood, Junio C Hamano, git, philipoakley, me, guy.j,
	szeder.dev, johannes.Schindelin, derrickstolee

On Wed, Apr 27, 2022 at 11:06 AM <rsbecker@nexbridge.com> wrote:
> Results on NonStop: strtol("123456789012345678", &endptr, 10) returns 4294967295 and *endptr == '\0' just beyond the last 8.

Thanks and errno == ERANGE as POSIX requires, right?, the code
(untested) I was planning to use instead looks like :

static inline void extract_id_from_env(const char *env, uid_t *id)
{
        const char *real_uid = getenv(env);

        /* discard any empty values */
        if (real_uid && *real_uid) {
                unsigned long extracted_id;
                int saved_errno = errno;

                errno = 0;
                extracted_id = strtoul(real_uid, NULL, 10);
                if (!errno)
                        *id = (uid_t)extracted_id;
                errno = saved_errno;
        }
        return 0;
}

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 18:11                 ` Carlo Arenas
@ 2022-04-27 18:16                   ` rsbecker
  0 siblings, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-04-27 18:16 UTC (permalink / raw)
  To: 'Carlo Arenas'
  Cc: phillip.wood, 'Junio C Hamano',
	git, philipoakley, me, guy.j, szeder.dev, johannes.Schindelin,
	derrickstolee

On April 27, 2022 2:12 PM, Carlo Arenas wrote:
>On Wed, Apr 27, 2022 at 11:06 AM <rsbecker@nexbridge.com> wrote:
>> Results on NonStop: strtol("123456789012345678", &endptr, 10) returns
>4294967295 and *endptr == '\0' just beyond the last 8.
>
>Thanks and errno == ERANGE as POSIX requires, right?, the code
>(untested) I was planning to use instead looks like :
>
>static inline void extract_id_from_env(const char *env, uid_t *id) {
>        const char *real_uid = getenv(env);
>
>        /* discard any empty values */
>        if (real_uid && *real_uid) {
>                unsigned long extracted_id;
>                int saved_errno = errno;
>
>                errno = 0;
>                extracted_id = strtoul(real_uid, NULL, 10);
>                if (!errno)
>                        *id = (uid_t)extracted_id;
>                errno = saved_errno;
>        }
>        return 0;
>}

errno = 4034 (Value out of range), which is the ERANGE #define.
--Randall


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 16:14         ` Phillip Wood
@ 2022-04-27 18:54           ` Junio C Hamano
  2022-04-27 20:59             ` Carlo Arenas
  2022-04-28 17:56             ` Phillip Wood
  0 siblings, 2 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-27 18:54 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Carlo Arenas, phillip.wood, git, philipoakley, me, guy.j,
	szeder.dev, johannes.Schindelin, derrickstolee, Randall Becker

Phillip Wood <phillip.wood123@gmail.com> writes:

> On 27/04/2022 16:58, Carlo Arenas wrote:
>> On Wed, Apr 27, 2022 at 5:30 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>>> You are ignoring any errors when parsing the environment variable - that
>>> is not a good idea in a security check.
>> which errors are you concerned about?, if anything in this code
>
> I was confused by the fact that the helper function returns a
> success/failure value which we ignore. However euid is not overwritten 
> if strtol fails so it is safe I think.
>
>> worries me from a security point of view is the fact that we are
>> relying in getenv not being racy (as mentioned in the original RFC),
>> but there are no errors set there AFAIK.
>> not ignoring errno in strtol is an option, but as mentioned before I
>> decided instead to reject bogus values and therefore not the clobber a
>> previous errno,
>
> strtol() will set errno if there is a range error ignoring it does not
> change that. In any case is_path_owned_by_current_uid() already
> clobbers errno if stat() fails.

IOW we are not preserving errno anyway, so ignoring errno (or
resetting before calling strtol) does not by us anything.

We probably should step back a bit, take a deep breath and think
what we are trying to protect our users against.  When I said early
in the discussion how much we trust the value in SUDO_UID, I hoped
people would do so, and I was especially hoping such a discussion to
happen when they realized that the width of uid_t is unknown and
there can be truncations in either direction.

So let's start one now.

If we didn't trust the value in SUDO_UID, what valid use case do we
help, and what avenue of attack do we open ourselves to?  This is
not a suggestion of an alternative, but is a discussion starter to
illustrate the line along which we want to think about the issues.

The original "sudo make install" (which runs "git describe" inside)
use case does not really care.  The process running with SUDO_UID is
not spelunking into random directories uncontrollably without being
aware that there may be hostile repositories.  The directories they
visit are part of legitimate journey "make install" takes.

The "sudo sh to get a shell, chdir to /var/tmp/foo to do something"
use case does care---it needs to make sure that whereever it goes is
not part of a hostile repository.  So "if SUDO_UID is there, anything
goes" is not a good protection for such a use case.

If we read SUDO_UID into an int16_t and uid_t happens to be much
wider than that type, then what happens?  Again, I am not advocating
to deliberately use shorter type to cause truncation.  This is
merely to illustrate how much truncation matters.

The "sudo make install" use case may be harmed unless the comparison
is done carefully.  If the repository owner's UID is beyond 32k then
asking if (st.st_uid == SUDO_UID) would say "no" due to truncation
and refuse access to the legitimate user.  If we compare both after
casting them to int16_t then the repository owner will be allowed in
again.  A friendly stranger who happens to share the low 16-bit of
UID will also be allowed to install from the repository, but that is
not an intended consequence.

The "sudo sh and go spelunking" use case is weakend by truncation.
It is protected only if the victim's UID does not share the lower
16-bit with the owner of hostile repository.

So what attack does this allow?  An attacker needs to learn their
own UID, find another user whose UID is the same modulo 16-bit as a
potential victim, and the victim has to be among those who can run
"sudo".  Then the victim somehow has to be talked into running stuff
in a repository the attacker prepares.

Possible?  Yes.  Practical?  I dunno.  Especially if we do not
deliberately truncate by reading into int16_t but use "int" (32-bit
almost everywhere we care about) or "long".  Now how likely is uid_t
narrower than long?  If it is not likely, then we probably are OK to
use long and not worry about the truncation at all.  Or use strtoll()
to make it even less likely.

So, in short, I think it is reasonable if we did:

 - do the extra "if SUDO_UID exists, then..." only when we are root.

 - use strtol to ensure SUDO_UID is in good shape (all digits) and
   read into long; use errno=0 trick to notice overflows.  err on
   the safe side and do not allow if the value cannot be read
   correctly.

 - do the comparison between st.st_uid and strtol() result by
   casting both to (long).  Without this, st.st_uid has a value that
   needs wider than long, we cannot allow the owner of such a
   repository in (and I somehow feel that is unlikely to happen).

 - or omit the third one --- tough luck for those whose UID is
   beyond what a long can represent (and I somehow feel that this is
   probably OK).

Another thing I'd suggest doing is to have a helper function to do
the "we do a bit more than geteuid() to come up with the allowed
owner of repositories we encounter", and call that function instead
of geteuid().

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 18:54           ` Junio C Hamano
@ 2022-04-27 20:59             ` Carlo Arenas
  2022-04-27 21:09               ` rsbecker
  2022-04-27 21:25               ` Junio C Hamano
  2022-04-28 17:56             ` Phillip Wood
  1 sibling, 2 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-27 20:59 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, phillip.wood, git, philipoakley, me, guy.j,
	szeder.dev, johannes.Schindelin, derrickstolee, Randall Becker

On Wed, Apr 27, 2022 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:

> The "sudo sh to get a shell, chdir to /var/tmp/foo to do something"
> use case does care---it needs to make sure that whereever it goes is
> not part of a hostile repository.  So "if SUDO_UID is there, anything
> goes" is not a good protection for such a use case.

FWIW that was never part of the proposal, indeed making git aware of
SUDO_ID when it is running as only as root was part of the design to
avoid other users probably allowing repositories they don't control by
having an evil SUDO_ID.

as per the root user, I was expecting that he could be trusted more
and that wouldn't accidentally get an evil SUDO_ID on their session
because it is something that gets generated from their original
account and they should be aware of it and even might need to tweak it
(ex: by un setting it if it gets in the way).

To make the `sudo make install` use case possible I think it make
sense to trust SUDO_ID by default, but I would be open to add a
configuration setting that would be required to enable it (it indeed
might become useful for root users that might want to disable it as an
alternative to unset SUDO_ID).

> Possible?  Yes.  Practical?  I dunno.  Especially if we do not
> deliberately truncate by reading into int16_t but use "int" (32-bit
> almost everywhere we care about) or "long".  Now how likely is uid_t
> narrower than long?  If it is not likely, then we probably are OK to
> use long and not worry about the truncation at all.  Or use strtoll()
> to make it even less likely.

strtoll (or my suggested alternative of strtoimax) suffer from
portability issues which are handled later in the git-compat-util.h
file, so while I think it is worth doing, I would prefer doing so
later as part of moving this code into a proper compat module.

for this iteration, the suggestion by Phillip to use strtoul makes
more sense (specially after confirmation by Randall that it works in
NON-STOP), it is more portable and doesn't require later in the file
git-compat magic to work, it allows for the full range of 32bit that
might be needed in 32bit *NIX, it is should fit find even in 32bit
environments with a 32bit unsigned uid_t (which is fairly common), it
doesn't sign extend if applied into a wider uid_t and would be likely
able to be assigned even without a cast and without the risk of
truncation for most uid_t types.  Lastly, since now this code is
focused only in sudo compatibility, and sudo casts their uid to an
unsigned int and prints it into that variable with "%u" it is the
right "API".

For comparison, I would also think it is better to let the compiler
warn if needed so at least the users would know their uid_t type is
narrower than what we expect and sudo sets, and might work as a
weather balloon to find those systems.

>
> So, in short, I think it is reasonable if we did:
>
>  - do the extra "if SUDO_UID exists, then..." only when we are root.

already in since the RFC, see discussion above for an option to
tighten it further if really needed, but even in that case I would
rather do it in the next iteration.

>  - use strtol to ensure SUDO_UID is in good shape (all digits) and
>    read into long; use errno=0 trick to notice overflows.  err on
>    the safe side and do not allow if the value cannot be read
>    correctly.

already implemented as part of v2, except it is using strtoul/unsigned long.
also in the line of "err on the safe side" I was also discarding any values
that would overflow an uid_t (which is assumed to be unsigned), which
affect the next bullet point.

>  - do the comparison between st.st_uid and strtol() result by
>    casting both to (long).  Without this, st.st_uid has a value that
>    needs wider than long, we cannot allow the owner of such a
>    repository in (and I somehow feel that is unlikely to happen).

do you still want to cast both sides even if discarding values that would
overflow an uid_t?

FWIW, as Phillip reported, using long (or another signed type) would
restrict the valid ids in 32-bit *NIX to half of what the expected uint32_t
range most of them should have.

>  - or omit the third one --- tough luck for those whose UID is
>    beyond what a long can represent (and I somehow feel that this is
>    probably OK).

That was what I proposed in the current RFC (at least in 32bit *NIX)
but I have to admit that i'd seen a lot more issues with uid larger than
INT_MAX that I expected when doing google for it, so that is what I
thought it would be safer to use the full 32-bit and an unsigned type
instead.

> Another thing I'd suggest doing is to have a helper function to do
> the "we do a bit more than geteuid() to come up with the allowed
> owner of repositories we encounter", and call that function instead
> of geteuid().

I did this originally, but discarded it quickly because it was a
little odd and easy to reuse, for something that I was hoping wouldn't
stay in git-compat for too long.

The use of the current helper function covers that use case but is by
design harder to reuse while keeping the overall logic of the 2
combined functions more visible.

Once we move this code into a proper compat module, I would be happy
to add a geteuid_or_sudo() function but that would be probably after
we also fix doas.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 20:59             ` Carlo Arenas
@ 2022-04-27 21:09               ` rsbecker
  2022-04-27 21:25               ` Junio C Hamano
  1 sibling, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-04-27 21:09 UTC (permalink / raw)
  To: 'Carlo Arenas', 'Junio C Hamano'
  Cc: 'Phillip Wood',
	phillip.wood, git, philipoakley, me, guy.j, szeder.dev,
	johannes.Schindelin, derrickstolee

On April 27, 2022 5:00 PM, Carlo Arenas wrote:
>On Wed, Apr 27, 2022 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:
>
>> The "sudo sh to get a shell, chdir to /var/tmp/foo to do something"
>> use case does care---it needs to make sure that whereever it goes is
>> not part of a hostile repository.  So "if SUDO_UID is there, anything
>> goes" is not a good protection for such a use case.
>
>FWIW that was never part of the proposal, indeed making git aware of SUDO_ID
>when it is running as only as root was part of the design to avoid other users
>probably allowing repositories they don't control by having an evil SUDO_ID.
>
>as per the root user, I was expecting that he could be trusted more and that
>wouldn't accidentally get an evil SUDO_ID on their session because it is something
>that gets generated from their original account and they should be aware of it and
>even might need to tweak it
>(ex: by un setting it if it gets in the way).
<snip>

For perspective, the root user is specifically only trusted so far my community. Mucking about with repositories is frowned upon except for special system configuration repositories (ones for /etc, ssl certs, for example, and they have to sign those). Commit signing is being deployed to detect and as much practical prevent root (or any other user, elevated or not) from inappropriate repo history operations.

Just sharing a different POV.
--Randall


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 20:59             ` Carlo Arenas
  2022-04-27 21:09               ` rsbecker
@ 2022-04-27 21:25               ` Junio C Hamano
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-27 21:25 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: Phillip Wood, phillip.wood, git, philipoakley, me, guy.j,
	szeder.dev, johannes.Schindelin, derrickstolee, Randall Becker

Carlo Arenas <carenas@gmail.com> writes:

> On Wed, Apr 27, 2022 at 11:54 AM Junio C Hamano <gitster@pobox.com> wrote:
>
>> The "sudo sh to get a shell, chdir to /var/tmp/foo to do something"
>> use case does care---it needs to make sure that whereever it goes is
>> not part of a hostile repository.  So "if SUDO_UID is there, anything
>> goes" is not a good protection for such a use case.
>
> FWIW that was never part of the proposal, indeed making git aware of
> SUDO_ID when it is running as only as root was part of the design to
> avoid other users probably allowing repositories they don't control by
> having an evil SUDO_ID.

You forgot to quote:

    This is not a suggestion of an alternative, but is a discussion
    starter to illustrate the line along which we want to think
    about the issues.

that came before it.

>>  - do the comparison between st.st_uid and strtol() result by
>>    casting both to (long).  Without this, st.st_uid has a value that
>>    needs wider than long, we cannot allow the owner of such a
>>    repository in (and I somehow feel that is unlikely to happen).
>
> do you still want to cast both sides even if discarding values that would
> overflow an uid_t?

The variable used with strtol (or strtoull or whatever) does not
have to be casted.  The "both sides" was primarily for the case
where st.st_uid is wider than whatever integral type we used to
parse and hold the value we took from the environment, to force
the same truncation on st.st_uid as the truncation the environment
parsing may have caused.  But as I keep saying I do not think it is
worth it, which means ...

>>  - or omit the third one --- tough luck for those whose UID is
>>    beyond what a long can represent (and I somehow feel that this is
>>    probably OK).

... this one.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v2] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27  0:05 ` [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
  2022-04-27  9:33   ` Phillip Wood
@ 2022-04-27 22:26   ` Carlo Marcelo Arenas Belón
  2022-04-27 22:33     ` Junio C Hamano
  2022-04-28  3:35     ` [PATCH 0/2] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  1 sibling, 2 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-27 22:26 UTC (permalink / raw)
  To: git
  Cc: philipoakley, me, guy.j, szeder.dev, Johannes.Schindelin,
	gitster, derrickstolee, rsbecker,
	Carlo Marcelo Arenas Belón, Phillip Wood

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that user was
root (because git was invoked through sudo or a compatible tool) and the
original uid that repository trusted for its config was no longer known,
therefore failing the following common call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using that instead.

This assumes the environment the user is running with after going
privileged can't be tampered with, and also does the check only for
root to keep the most common case less complicated, but as a side effect
will miss cases where sudo (or an equivalent) was used to change to
another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
Changes since v1
* The helper function was completely rewritten to include all feedback,
  specially in areas that were too confusing and that include:
  - removing the return type that was only useful when doas was also supported
    and that is therefore no longer needed since v1.
  - using strtoul instead of strtol and assumed uid_t is unsigned.  This is
    a likely more popular configuration and allows up to 2^32 uids in 32bit
    systems.
  - using errno to check for errors in strtoul, this also includes saving and
    restoring the previous errno even if that is not yet needed.
  - avoiding truncation issues in systems where sizeof(long) > sizeof(uid_t)
    by discarding any values that wouldn't fit in an uid_t.  sudo uses
    unsigned int to represent the uids so no valid id should be affected.
    This assumes an unsigned uid_t which is not guaranteed by the standard
    and therefore might need adjusting later if some platform we support does
    not provide that (it is expected to trigger a warning at build time)
  - renaming variables that had confusing names
* Improved comments and commit message, and spell checked twice.

Sent as an RFC to make sure it fits everyone expectations and since it doesn't
fully implement all suggestions that were proposed about the same time it was
ready.

 git-compat-util.h | 40 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 58fd813bd01..3c9883934f6 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -437,12 +437,50 @@ 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
+
+/*
+ * this helper function overrides a ROOT_UID with the one provided by
+ * an environment variable, do not use unless the original user is
+ * root
+ */
+static inline void extract_id_from_env(const char *env, uid_t *id)
+{
+	const char *real_uid = getenv(env);
+
+	/* discard any empty values */
+	if (real_uid && *real_uid) {
+		char *endptr;
+		unsigned long env_id;
+		int saved_errno = errno;
+
+		errno = 0;
+		env_id = strtoul(real_uid, &endptr, 10);
+		if (!errno && !*endptr && env_id <= (uid_t)-1)
+			*id = env_id;
+
+		errno = saved_errno;
+	}
+}
+
 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
-- 
2.36.0.266.g59f845bde02


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v2] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 22:26   ` [RFC PATCH v2] " Carlo Marcelo Arenas Belón
@ 2022-04-27 22:33     ` Junio C Hamano
  2022-04-28  3:35     ` [PATCH 0/2] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-27 22:33 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, philipoakley, me, guy.j, szeder.dev, Johannes.Schindelin,
	derrickstolee, rsbecker, Phillip Wood

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> diff --git a/git-compat-util.h b/git-compat-util.h
> index 58fd813bd01..3c9883934f6 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -437,12 +437,50 @@ 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
> +
> +/*
> + * this helper function overrides a ROOT_UID with the one provided by
> + * an environment variable, do not use unless the original user is
> + * root
> + */
> +static inline void extract_id_from_env(const char *env, uid_t *id)
> +{
> +	const char *real_uid = getenv(env);
> +
> +	/* discard any empty values */
> +	if (real_uid && *real_uid) {
> +		char *endptr;
> +		unsigned long env_id;
> +		int saved_errno = errno;
> +
> +		errno = 0;
> +		env_id = strtoul(real_uid, &endptr, 10);
> +		if (!errno && !*endptr && env_id <= (uid_t)-1)
> +			*id = env_id;

So we refrain from touching *id when we cannot read from SUDO_UID;
let's make sure that the caller is prepared for that ...

> +		errno = saved_errno;
> +	}
> +}
> +
>  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);

... and it is.  euid is set to the real thing, and the tweak done by
the helper function may overwrite it only when the helper computed
a value without an error.

> +	return st.st_uid == euid;

OK.  Looking good.

Will queue.

>  }
>  
>  #define is_path_owned_by_current_user is_path_owned_by_current_uid

^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH 0/2] fix `sudo make install` regression in maint
  2022-04-27 22:26   ` [RFC PATCH v2] " Carlo Marcelo Arenas Belón
  2022-04-27 22:33     ` Junio C Hamano
@ 2022-04-28  3:35     ` Carlo Marcelo Arenas Belón
  2022-04-28  3:35       ` [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
                         ` (3 more replies)
  1 sibling, 4 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-28  3:35 UTC (permalink / raw)
  To: git; +Cc: szeder.dev, gitster, Carlo Marcelo Arenas Belón

The following series complement the recently queued[0] RFC that adds
the missing functionality to allow git to validate a git repository
while running under sudo.

They could be squashed together if preferred, as they seem to be
smaller that I had originally assumed and also unlikely to conflict.

The test uses a function I stole from rs/t7812-pcre2-ws-bug-test that
could be alternatively moved to a common location and reused if that
is preferred, but it is small enough that might be as well be done
as part of some cleanup later.

Tried to change the documentation in a way that wouldn't conflict with
other on the fly changes, but there is at least one pending change
not in seen AFAIK in the same file from Gábor which might[1], but the
resolution should be to take both sides.

Carlo Marcelo Arenas Belón (2):
  Documentation: explain how safe.directory works when running under
    sudo
  t: add tests for safe.directory when running with sudo

 Documentation/config/safe.txt  |  8 ++++++
 t/t0034-root-safe-directory.sh | 51 ++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

[0] https://lore.kernel.org/git/xmqqilqujgvs.fsf@gitster.g/
[1] https://lore.kernel.org/git/xmqqee1il09v.fsf@gitster.g/
-- 
2.36.0.266.g59f845bde02


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo
  2022-04-28  3:35     ` [PATCH 0/2] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
@ 2022-04-28  3:35       ` Carlo Marcelo Arenas Belón
  2022-04-28  5:17         ` Junio C Hamano
  2022-04-28  3:35       ` [PATCH 2/2] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
                         ` (2 subsequent siblings)
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-28  3:35 UTC (permalink / raw)
  To: git; +Cc: szeder.dev, gitster, Carlo Marcelo Arenas Belón

In a previous patch, the behaviour of git was changed so it will be able
to find the "effective uid" that is required when git was invoked with
sudo to root, for example:

  $ sudo make install

Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 Documentation/config/safe.txt | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 6d764fe0ccf..67f8ef5d766 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -26,3 +26,11 @@ 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.
++
+When git tries to check for ownership of git repositories it will obviously
+use the user that is being used to run git itself, but if git is running
+as root, it will first check if it might had been started through `sudo`,
+and if that is the case, will use the user id that invoked sudo instead.
+If that is not what you would prefer and want git to instead only trust
+repositories that are owned by root, then you should remove the `SUDO_UID`
+variable from root's environment.
-- 
2.36.0.266.g59f845bde02


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH 2/2] t: add tests for safe.directory when running with sudo
  2022-04-28  3:35     ` [PATCH 0/2] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-04-28  3:35       ` [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
@ 2022-04-28  3:35       ` Carlo Marcelo Arenas Belón
  2022-04-28  5:34         ` Junio C Hamano
  2022-04-28  4:57       ` [PATCH 0/2] fix `sudo make install` regression in maint Junio C Hamano
  2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-28  3:35 UTC (permalink / raw)
  To: git; +Cc: szeder.dev, gitster, Carlo Marcelo Arenas Belón

In a previous commit the functionality for fixing this regression was
implemented, so add a test for it and the relevant infrastructure.

This new test file is meant to be run in CI and checks for a SUDO
prerequisite which requires a passwordless configuration if run
locally, but that is common there.

It could be run locally by first running sudo, so the credentials are
temporarily cached and then running it as :

  $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh

It is slightly ackward as it needs to clean up after itself since the
test framework would fail to do its own cleanup otherwise, and for
simplicity creates its own subtree while ignoring the one provided by
the framework, but doing some better integration has been punted.

Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/t0034-root-safe-directory.sh | 51 ++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 00000000000..c62bf3777c0
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.sh
+
+if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
+	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run thi
+s test"
+	test_done
+fi
+
+if ! test_have_prereq NOT_ROOT
+then
+	skip_all="this test uses sudo to run as root"
+	test_done
+fi
+
+doalarm () {
+	perl -e 'alarm shift; exec @ARGV' -- "$@"
+}
+
+test_lazy_prereq SUDO '
+	doalarm 1 sudo id -u >u &&
+	id -u root >r &&
+	test_cmp u r
+'
+
+test_expect_success SUDO 'setup' '
+	sudo rm -rf root &&
+	mkdir -p root/r &&
+	sudo chown root root &&
+	(
+		cd root/r &&
+		git init
+	)
+'
+
+test_expect_success SUDO 'sudo git status works' '
+	(
+		cd root/r &&
+		git status &&
+		sudo git status
+	)
+'
+
+test_expect_success SUDO 'cleanup' '
+	sudo rm -rf root
+'
+
+test_done
-- 
2.36.0.266.g59f845bde02


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH 0/2] fix `sudo make install` regression in maint
  2022-04-28  3:35     ` [PATCH 0/2] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-04-28  3:35       ` [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
  2022-04-28  3:35       ` [PATCH 2/2] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
@ 2022-04-28  4:57       ` Junio C Hamano
  2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
  3 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28  4:57 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón; +Cc: git, szeder.dev

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> The following series complement the recently queued[0] RFC that adds
> the missing functionality to allow git to validate a git repository
> while running under sudo.
>
> They could be squashed together if preferred, as they seem to be
> smaller that I had originally assumed and also unlikely to conflict.
>
> The test uses a function I stole from rs/t7812-pcre2-ws-bug-test that
> could be alternatively moved to a common location and reused if that
> is preferred, but it is small enough that might be as well be done
> as part of some cleanup later.
>
> Tried to change the documentation in a way that wouldn't conflict with
> other on the fly changes, but there is at least one pending change
> not in seen AFAIK in the same file from Gábor which might[1], but the
> resolution should be to take both sides.

We shouldn't have to worry too much about anything that is not in
'master', as we can kick out things that conflict whose resolution
is too cumbersome and ask contributors to rebase and come back if
needed.  Regression fixes are more urgent than anything not in
'master' at this point, although of course we do not have to make
conflicts deliberately ;)

It seems that these two apply cleanly on top of the other one that
in turn applies cleanly on top of maint-2.30, which is very much
suitable base to build these fixes on.

Thanks, will queue.

I've already read through them but additional set of eyes are
appreciated as always and even more than usual.  Hint, hint...




^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo
  2022-04-28  3:35       ` [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
@ 2022-04-28  5:17         ` Junio C Hamano
  2022-04-28  5:58           ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28  5:17 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón; +Cc: git, szeder.dev

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> In a previous patch, the behaviour of git was changed so it will be able
> to find the "effective uid" that is required when git was invoked with
> sudo to root, for example:
>
>   $ sudo make install
>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
>  Documentation/config/safe.txt | 8 ++++++++
>  1 file changed, 8 insertions(+)
>
> diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
> index 6d764fe0ccf..67f8ef5d766 100644
> --- a/Documentation/config/safe.txt
> +++ b/Documentation/config/safe.txt
> @@ -26,3 +26,11 @@ 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.
> ++
> +When git tries to check for ownership of git repositories it will obviously

Comma before "it will obviously".

> +use the user that is being used to run git itself, but if git is running
> +as root, it will first check if it might had been started through `sudo`,
> +and if that is the case, will use the user id that invoked sudo instead.

This raises a design question.  In a repository is owned by root,
shouldn't "sudo git describe" work?  IOW, I am wondering if the
"instead" at the end of the description is what we want, or if we
want to check both the original user and "root".

There is not much point in protecting against a malicious repository
a repository that is owned by "root"---an attacker that can create
such a repository and futz with its config or hooks can attack you
more directly, without social engineering you into using git on such
a repository.  So from that point of view, it may be reasonable to
say that we can trust repositories owned by 

 - euid (both when euid is root and euid is not root)
 - SUDO_UID (when euid is root)

I think.  And even if we adopt such a tweak, ...

> +If that is not what you would prefer and want git to instead only trust
> +repositories that are owned by root, then you should remove the `SUDO_UID`
> +variable from root's environment.

... this is still true.

I guess the necessary tweak to the code, if we were to take that
suggestion, would be quite small.  We are happy when euid owns the
path (regardless of who euid is), and in addition, only when euid
is root, we check with SUDO_UID, too.

 git-compat-util.h | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git c/git-compat-util.h w/git-compat-util.h
index dfdd3e4f81..18660553b3 100644
--- c/git-compat-util.h
+++ w/git-compat-util.h
@@ -401,13 +401,13 @@ static inline int git_offset_1st_component(const char *path)
 #endif
 
 /*
- * this helper function overrides a ROOT_UID with the one provided by
- * an environment variable, do not use unless the original user is
- * root
+ * The environment variable (e.g. SUDO_UID) gives an integer;
+ * is it the same as the given uid_t id?
  */
-static inline void extract_id_from_env(const char *env, uid_t *id)
+static inline int id_from_env_matches(const char *env, uid_t id)
 {
 	const char *real_uid = getenv(env);
+	int matches = 0;
 
 	/* discard any empty values */
 	if (real_uid && *real_uid) {
@@ -417,11 +417,12 @@ static inline void extract_id_from_env(const char *env, uid_t *id)
 
 		errno = 0;
 		env_id = strtoul(real_uid, &endptr, 10);
-		if (!errno && !*endptr && env_id <= (uid_t)-1)
-			*id = env_id;
-
+		if (!errno && !*endptr && env_id <= (uid_t)-1 &&
+		    (uid_t)env_id == id)
+			matches = 1;
 		errno = saved_errno;
 	}
+	return matches;
 }
 
 static inline int is_path_owned_by_current_uid(const char *path)
@@ -433,10 +434,11 @@ static inline int is_path_owned_by_current_uid(const char *path)
 		return 0;
 
 	euid = geteuid();
+	if (st.st_uid == euid)
+		return 1;
 	if (euid == ROOT_UID)
-		extract_id_from_env("SUDO_UID", &euid);
-
-	return st.st_uid == euid;
+		return id_from_env_matches("SUDO_UID", st.st_uid);
+	return 0;
 }
 
 #define is_path_owned_by_current_user is_path_owned_by_current_uid

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH 2/2] t: add tests for safe.directory when running with sudo
  2022-04-28  3:35       ` [PATCH 2/2] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
@ 2022-04-28  5:34         ` Junio C Hamano
  0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28  5:34 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón; +Cc: git, szeder.dev

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> In a previous commit the functionality for fixing this regression was
> implemented, so add a test for it and the relevant infrastructure.
>
> This new test file is meant to be run in CI and checks for a SUDO
> prerequisite which requires a passwordless configuration if run
> locally, but that is common there.
>
> It could be run locally by first running sudo, so the credentials are
> temporarily cached and then running it as :
>
>   $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh
>
> It is slightly ackward as it needs to clean up after itself since the

"awkward", I think.

> test framework would fail to do its own cleanup otherwise, and for
> simplicity creates its own subtree while ignoring the one provided by
> the framework, but doing some better integration has been punted.
>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
>  t/t0034-root-safe-directory.sh | 51 ++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
>  create mode 100755 t/t0034-root-safe-directory.sh
>
> diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
> new file mode 100755
> index 00000000000..c62bf3777c0
> --- /dev/null
> +++ b/t/t0034-root-safe-directory.sh
> @@ -0,0 +1,51 @@
> +#!/bin/sh
> +
> +test_description='verify safe.directory checks while running as root'
> +
> +. ./test-lib.sh
> +
> +if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
> +	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run thi
> +s test"
> +	test_done
> +fi
> +
> +if ! test_have_prereq NOT_ROOT
> +then
> +	skip_all="this test uses sudo to run as root"

As a statement of fact, there is nothing wrong per-se in this
message.  It may be an explanation why you should not run this as
root, but as an explanation of the reason why all tests in this
script are skipped, and more importantly, hint to testers what they
need to do to correct the situation, this is too cryptic to readers.

Perhaps telling them

	skip_all="do not run this test as root"

is more important and useful.

> +	test_done
> +fi
> +
> +doalarm () {
> +	perl -e 'alarm shift; exec @ARGV' -- "$@"
> +}
> +
> +test_lazy_prereq SUDO '
> +	doalarm 1 sudo id -u >u &&
> +	id -u root >r &&
> +	test_cmp u r
> +'
> +
> +test_expect_success SUDO 'setup' '
> +	sudo rm -rf root &&
> +	mkdir -p root/r &&
> +	sudo chown root root &&
> +	(
> +		cd root/r &&
> +		git init
> +	)
> +'
> +
> +test_expect_success SUDO 'sudo git status works' '
> +	(
> +		cd root/r &&
> +		git status &&
> +		sudo git status
> +	)
> +'

All of the above are positive tests.  One possible negative test is
to prepare a root-owned repository and see what "git status" as
yourself in there does.  And also see what "git status" as root
there does.

test_expect_success SUDO 'in root owned repository' '
	mkdir root/p
        sudo chown root root/p &&
	sudo git init root/p &&

	# owned by other person (root), do I see it as a repository?
	(
		cd root/p &&
		test_must_fail git status
	) &&

	# owned by root, can I access it under sudo?
	(
		cd root/p &&
		sudo git status
	)
'

> +test_expect_success SUDO 'cleanup' '
> +	sudo rm -rf root
> +'
> +
> +test_done

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo
  2022-04-28  5:17         ` Junio C Hamano
@ 2022-04-28  5:58           ` Carlo Arenas
  2022-04-28  6:41             ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-28  5:58 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, szeder.dev

On Wed, Apr 27, 2022 at 10:17 PM Junio C Hamano <gitster@pobox.com> wrote:
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
> > diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
> > index 6d764fe0ccf..67f8ef5d766 100644
> > --- a/Documentation/config/safe.txt
> > +++ b/Documentation/config/safe.txt
> > @@ -26,3 +26,11 @@ 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.
> > ++
> > +When git tries to check for ownership of git repositories it will obviously
>
> Comma before "it will obviously".

Obviously my whole paragraph could be improved further, do you want
a reroll with this fix, or would instead fixup locally?

> > +use the user that is being used to run git itself, but if git is running

"use the user that is being used", is something my high school grammar
teacher would label as a "cacophony", so feel free to provide an
alternative as well there.

> > +as root, it will first check if it might had been started through `sudo`,
> > +and if that is the case, will use the user id that invoked sudo instead.
>
> This raises a design question.  In a repository is owned by root,
> shouldn't "sudo git describe" work?  IOW, I am wondering if the
> "instead" at the end of the description is what we want, or if we
> want to check both the original user and "root".

I think it makes sense to have both, and your implementation below
seems like a good way to do it but it might not be as safe as it
seems, since sometimes directories owned by root might be also world
writable and therefore not necessarily safe.

This is obviously not directly related to this code, but the original
issue as reported in Windows for the original CVE could be traced back
to the default policy to allow any authenticated user to write in the
root directory.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo
  2022-04-28  5:58           ` Carlo Arenas
@ 2022-04-28  6:41             ` Junio C Hamano
  0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28  6:41 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: git, szeder.dev

Carlo Arenas <carenas@gmail.com> writes:

> On Wed, Apr 27, 2022 at 10:17 PM Junio C Hamano <gitster@pobox.com> wrote:
>> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
>> > diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
>> > index 6d764fe0ccf..67f8ef5d766 100644
>> > --- a/Documentation/config/safe.txt
>> > +++ b/Documentation/config/safe.txt
>> > @@ -26,3 +26,11 @@ 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.
>> > ++
>> > +When git tries to check for ownership of git repositories it will obviously
>>
>> Comma before "it will obviously".
>
> Obviously my whole paragraph could be improved further, do you want
> a reroll with this fix, or would instead fixup locally?

I think the patches (including the previous one) are still fluid and
expect them to be reworked; local fix-ups would be a bit premature
and leads to waste.  At least not yet.

>> This raises a design question.  In a repository is owned by root,
>> shouldn't "sudo git describe" work?  IOW, I am wondering if the
>> "instead" at the end of the description is what we want, or if we
>> want to check both the original user and "root".
>
> I think it makes sense to have both, and your implementation below
> seems like a good way to do it but it might not be as safe as it
> seems, since sometimes directories owned by root might be also world
> writable and therefore not necessarily safe.

I am not quite following you; that logic applies to directories
owned by euid (not necessarily root).  As we are loosening to make
"sudo" usable again, the use case to visit root-owned repository as
root via "sudo" is worth discussing, if not worth immediately
supporting, I would think.  I do not think it is absolutely needed
as there is an easy workaround (see below).

Assuming we will go without "same euid, whether it is root or not,
plus SUDO_UID when run as root", here a test addition, updated from
the one I gave you in the review of [2/2]

test_expect_success SUDO 'in root owned repository' '
	mkdir root/p
        sudo chown root root/p &&
	sudo git init root/p &&

	# owned by other person (root), do I see it as a repository?
	(
		cd root/p &&
		test_must_fail git status
	) &&

	# owned by root, can I access it under sudo?
	(
		cd root/p &&
		test_must_fail sudo git status
	) &&

	# workaround #1, giving GIT_DIR explicitly
	(
		cd root/p &&
		sudo "
			GIT_DIR=.git GIT_WORK_TREE=. git status
		"
	) &&

	# workaround #2, discarding SUDO_UID
	(
		cd root/p &&
		sudo "
			unset SUDO_UID;
			git status
		"
	)
'



^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v2 0/3] fix `sudo make install` regression in maint
  2022-04-28  3:35     ` [PATCH 0/2] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
                         ` (2 preceding siblings ...)
  2022-04-28  4:57       ` [PATCH 0/2] fix `sudo make install` regression in maint Junio C Hamano
@ 2022-04-28 10:58       ` Carlo Marcelo Arenas Belón
  2022-04-28 10:58         ` [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
                           ` (4 more replies)
  3 siblings, 5 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-28 10:58 UTC (permalink / raw)
  To: git; +Cc: gitster, Carlo Marcelo Arenas Belón

Mainly documentation (both in the manual and commit messages) and
extended/cleaner tests with the following considerations :

 * able to run as root, and therefore removed reported issue with message
 * remove perl function for a sudo native alternative and a more reliable
   execution by making sure to avoid its PATH interceptions.
 * would likely need more changes to the test framework to avoid odities
   but the deficiencies are clearly visible until then, and so safe for
   backporting.

Carlo Marcelo Arenas Belón (3):
  git-compat-util: avoid failing dir ownership checks if running privileged
  Documentation: explain how safe.directory works when running under sudo
  t: add tests for safe.directory when running with sudo

 Documentation/config/safe.txt  |  9 ++++
 git-compat-util.h              | 40 +++++++++++++++-
 t/t0034-root-safe-directory.sh | 87 ++++++++++++++++++++++++++++++++++
 3 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100755 t/t0034-root-safe-directory.sh

-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
@ 2022-04-28 10:58         ` Carlo Marcelo Arenas Belón
  2022-04-28 18:02           ` Phillip Wood
  2022-04-28 10:58         ` [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
                           ` (3 subsequent siblings)
  4 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-28 10:58 UTC (permalink / raw)
  To: git
  Cc: gitster, Carlo Marcelo Arenas Belón, Guy Maurel,
	SZEDER Gábor, Randall Becker, Phillip Wood,
	Johannes Schindelin

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that user was
root (because git was invoked through sudo or a compatible tool) and the
original uid that repository trusted for its config was no longer known,
therefore failing the following otherwise safe call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using that instead.

This assumes the environment the user is running with after going
privileged can't be tampered with, and also does the check only for
root to keep the most common case less complicated, but as a side effect
will miss cases where sudo (or an equivalent) was used to change to
another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 git-compat-util.h | 40 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 63ba89dd31d..dfdd3e4f81a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -393,12 +393,50 @@ 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
+
+/*
+ * this helper function overrides a ROOT_UID with the one provided by
+ * an environment variable, do not use unless the original user is
+ * root
+ */
+static inline void extract_id_from_env(const char *env, uid_t *id)
+{
+	const char *real_uid = getenv(env);
+
+	/* discard any empty values */
+	if (real_uid && *real_uid) {
+		char *endptr;
+		unsigned long env_id;
+		int saved_errno = errno;
+
+		errno = 0;
+		env_id = strtoul(real_uid, &endptr, 10);
+		if (!errno && !*endptr && env_id <= (uid_t)-1)
+			*id = env_id;
+
+		errno = saved_errno;
+	}
+}
+
 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
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo
  2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
  2022-04-28 10:58         ` [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-04-28 10:58         ` Carlo Marcelo Arenas Belón
  2022-04-30  6:17           ` Bagas Sanjaya
  2022-04-28 10:58         ` [PATCH v2 3/3] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
                           ` (2 subsequent siblings)
  4 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-28 10:58 UTC (permalink / raw)
  To: git; +Cc: gitster, Carlo Marcelo Arenas Belón

In a previous patch, the behavior of git was changed so it will be able
to find the "effective uid" that is required when git was invoked with
sudo to root, for example the internal calls made to git when calling
the following in git's own repository:

  $ sudo make install

Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config/safe.txt | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 6d764fe0ccf..ee558ced8c7 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -26,3 +26,12 @@ 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.
++
+When git tries to check for ownership of git repositories, it will
+obviously do so with the uid of the user that is running git itself,
+but if git is running as root, it will check first if it might have
+been started through `sudo`, and if that is the case, will instead
+use the uid of the user that did so.
+If that is not what you would prefer and want git to only trust
+repositories that are owned by root instead, then you should remove
+the `SUDO_UID` variable from root's environment.
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
  2022-04-28 10:58         ` [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
  2022-04-28 10:58         ` [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
@ 2022-04-28 10:58         ` Carlo Marcelo Arenas Belón
  2022-04-28 16:55           ` Junio C Hamano
  2022-05-02 18:39         ` [RFC PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-03  6:54         ` [PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  4 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-28 10:58 UTC (permalink / raw)
  To: git; +Cc: gitster, Carlo Marcelo Arenas Belón

In a previous commit the functionality for fixing this regression was
implemented, so add the basic infrastructure needed to run sudo and
implement some tests with it.

This new test is meant to be mainly run in CI and therefore assumes
that the system where it runs provides passwordless sudo to root and
doesn't sanitize the path.

All tests should depend on the new SUDO prerequisite which validates
that setup is available but it could also run locally, with the right
configuration and maybe making use of the sudo credential cache by
first invoking sudo, entering your password if needed, and then
invoking the test by doing:

  $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh

It is slightly awkward as it needs to run its own clean up task at
the end to remove the root owned directories and that the test
framework can't yet manage, can't use the library inside sudo and
it creates its own subtree and repositories while ignoring the one
provided by the framework, but improving that has been punted for now.

Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t0034-root-safe-directory.sh | 87 ++++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 00000000000..fb54a2fb851
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.sh
+
+if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
+	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
+	test_done
+fi
+
+is_root() {
+	test -n "$1" && CMD="sudo -n"
+	test $($CMD id -u) = $(id -u root)
+}
+
+test_lazy_prereq SUDO '
+	is_root sudo &&
+	! sudo grep -E '^[^#].*secure_path' /etc/sudoers
+'
+
+test_lazy_prereq ROOT '
+	is_root
+'
+
+test_expect_success SUDO 'setup' '
+	sudo rm -rf root &&
+	mkdir -p root/r &&
+	sudo chown root root &&
+	(
+		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 SUDO,!ROOT 'can access if owned by root' '
+	(
+		cd root/p &&
+		test_must_fail git status
+	)
+'
+
+test_expect_success SUDO,!ROOT 'can access with sudo' '
+	# fail to access using sudo
+	(
+		# TODO: test_must_fail missing functionality
+		cd root/p &&
+		! sudo git status
+	)
+'
+
+test_expect_success SUDO 'can access with workaround' '
+	# provide explicit GIT_DIR
+	(
+		cd root/p &&
+		sudo sh -c "
+			GIT_DIR=.git GIT_WORK_TREE=. git status
+		"
+	) &&
+	# discard SUDO_UID
+	(
+		cd root/p &&
+		sudo sh -c "
+			unset SUDO_UID &&
+			git status
+		"
+	)
+'
+
+test_expect_success SUDO 'cleanup' '
+	sudo rm -rf root
+'
+
+test_done
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 10:58         ` [PATCH v2 3/3] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
@ 2022-04-28 16:55           ` Junio C Hamano
  2022-04-28 18:08             ` Phillip Wood
                               ` (2 more replies)
  0 siblings, 3 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28 16:55 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón; +Cc: git

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> +test_description='verify safe.directory checks while running as root'
> +
> +. ./test-lib.sh
> +
> +if [ "$IKNOWWHATIAMDOING" != "YES" ]; then

Style.

	if test "$IKNOWWHATIAMDOING" != "YES"
	then

> +is_root() {
> +	test -n "$1" && CMD="sudo -n"
> +	test $($CMD id -u) = $(id -u root)
> +}

Style.

	is_root () {
		... body ..

But more importantly, why do we need this in the first place?
SANITY prerequisite checks if the user is running as root or
non-root---can't we use it here?

Or perhaps my reading is wrong?  I assumed from its name that it was
just "see if we are running as user 'root' and return 0 or non-zero
to answer", but if it does more than that, like priming "sudo", then
probably it is misnamed.

> +test_lazy_prereq SUDO '
> +	is_root sudo &&
> +	! sudo grep -E '^[^#].*secure_path' /etc/sudoers
> +'

OK.

> +test_lazy_prereq ROOT '
> +	is_root
> +'
> +
> +test_expect_success SUDO 'setup' '
> +	sudo rm -rf root &&
> +	mkdir -p root/r &&
> +	sudo chown root root &&
> +	(
> +		cd root/r &&
> +		git init
> +	)
> +'

We have a root-owned directory "root" with a subdirectory "r" owned
by us.  We want to be able to use our "root/r" directory as a
repository.  OK.

The prerequisite allows this test to be started as root, but I do
not quite see the point.  It may pass when started as root, but it
is not testing what this test is designed to check (i.e. an ordinary
user who has repository at root/r can do things there).

> +test_expect_success SUDO 'sudo git status as original owner' '
> +	(
> +		cd root/r &&
> +		git status &&
> +		sudo git status
> +	)
> +'

And the directory can be used by the user under "sudo", too.  Good.

The same "this is allowed to run as root, but why?" question
applies.  If this was started by 'root', root, root/r and
root/r/.git all are owned by 'root' and we are checking if 'root'
can run 'git status' as 'root' (or 'root' via sudo) there.  Such a
test may well pass, but it is not catching a future regression on
the code you wrote for this series.

> +test_expect_success SUDO 'setup root owned repository' '
> +	sudo mkdir -p root/p &&
> +	sudo git init root/p
> +'

Now we go on to create root owned repository at root/p

> +test_expect_success SUDO,!ROOT 'can access if owned by root' '
> +	(
> +		cd root/p &&
> +		test_must_fail git status
> +	)
> +'

And as an ordinary user, we fail to access a repository that is
owned by a wrong person (i.e. root).  !ROOT (or SANITY) prereq
should be there NOT because the test written here would fail if run
by root, but because running it as root, even if passed, is totally
pointless, because we are *not* testing "person A has a repository,
person B cannot access it" combination.

The other side of the same coin is that the lack of !ROOT (or
SANITY) prereq in earlier tests I pointed out above misses the point
of why we have prerequisite mechanism in the first place.  It is not
to mark a test that fails when the precondition is not met.  It is
to avoid running code that would NOT test what we want to test.

The difference is that a test that passes for a wrong reason
(e.g. we wanted to see of person A can access a repository of their
own even when the user identity is tentatively switched to 'root'
via 'sudo'---if person A is 'root', the access will be granted even
if the special code to handle 'sudo' situation we have is broken)
should also be excluded with prerequisite.

> +test_expect_success SUDO,!ROOT 'can access with sudo' '
> +	# fail to access using sudo
> +	(
> +		# TODO: test_must_fail missing functionality

Care to explain a bit in the log message or in this comment the
reason why we do not use test_must_fail but use ! here?  Are we
over-eager to reject anything non "git" be fed, or something?

> +		cd root/p &&
> +		! sudo git status
> +	)
> +'

The repository is owned by 'root', but because of the 'sudo'
"support", you cannot access the repository with "sudo git".

The test title needs updating.  We expect that the repository cannot
be accessed under sudo.

> +test_expect_success SUDO 'can access with workaround' '

"workarounds", I think.

> +	# provide explicit GIT_DIR
> +	(
> +		cd root/p &&
> +		sudo sh -c "
> +			GIT_DIR=.git GIT_WORK_TREE=. git status
> +		"
> +	) &&
> +	# discard SUDO_UID
> +	(
> +		cd root/p &&
> +		sudo sh -c "
> +			unset SUDO_UID &&
> +			git status
> +		"
> +	)
> +'

Again, this lack !ROOT (or SANITY) because tests pass for a wrong
reason.

Overall, I like the simplicity and clarity of "do not start this
test as 'root'" in the previous round much better.

But other than that, the test coverage given by this patch looks
quite sensible.

Thanks.

> +
> +test_expect_success SUDO 'cleanup' '
> +	sudo rm -rf root
> +'
> +
> +test_done

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-27 18:54           ` Junio C Hamano
  2022-04-27 20:59             ` Carlo Arenas
@ 2022-04-28 17:56             ` Phillip Wood
  1 sibling, 0 replies; 161+ messages in thread
From: Phillip Wood @ 2022-04-28 17:56 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Carlo Arenas, phillip.wood, git, philipoakley, me, guy.j,
	szeder.dev, johannes.Schindelin, derrickstolee, Randall Becker

On 27/04/2022 19:54, Junio C Hamano wrote:

[ I've trimmed your discussion for brevity but thanks for taking the 
time to write it out, it was helpful in clarifying what we're trying to 
protect ourselves against]

> So, in short, I think it is reasonable if we did:
> 
>   - do the extra "if SUDO_UID exists, then..." only when we are root.
> 
>   - use strtol to ensure SUDO_UID is in good shape (all digits) and
>     read into long; use errno=0 trick to notice overflows.  err on
>     the safe side and do not allow if the value cannot be read
>     correctly.
> 
>   - do the comparison between st.st_uid and strtol() result by
>     casting both to (long).  Without this, st.st_uid has a value that
>     needs wider than long, we cannot allow the owner of such a
>     repository in (and I somehow feel that is unlikely to happen).
> 
>   - or omit the third one --- tough luck for those whose UID is
>     beyond what a long can represent (and I somehow feel that this is
>     probably OK).

I think that unsigned long should be wide enough. On x86 linux uid_t is 
uint32_t for both 32 & 64 bit processors. That means that we could 
truncate the value we read from SUDO_UID when long is 64 bits if it is 
cast to uid_t but we're supposed to be able to trust that SUDO_UID is a 
valid id so that shouldn't be a problem. If an attacker can change 
what's in SUDO_UID we're doomed anyway.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-28 10:58         ` [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-04-28 18:02           ` Phillip Wood
  2022-04-28 18:57             ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-04-28 18:02 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git
  Cc: gitster, Guy Maurel, SZEDER Gábor, Randall Becker,
	Johannes Schindelin

Hi Carlo

On 28/04/2022 11:58, Carlo Marcelo Arenas Belón wrote:
> bdc77d1d685 (Add a function to determine whether a path is owned by the
> current user, 2022-03-02) checks for the effective uid of the running
> process using geteuid() but didn't account for cases where that user was
> root (because git was invoked through sudo or a compatible tool) and the
> original uid that repository trusted for its config was no longer known,
> therefore failing the following otherwise safe call:
> 
>    guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
>    [sudo] password for guy:
>    fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)
> 
> Attempt to detect those cases by using the environment variables that
> those tools create to keep track of the original user id, and do the
> ownership check using that instead.
> 
> This assumes the environment the user is running with after going
> privileged can't be tampered with, and also does the check only for
> root to keep the most common case less complicated, but as a side effect
> will miss cases where sudo (or an equivalent) was used to change to
> another unprivileged user or where the equivalent tool used to raise
> privileges didn't track the original id in a sudo compatible way.
> 
> Reported-by: Guy Maurel <guy.j@maurel.de>
> Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
> Helped-by: Randall Becker <rsbecker@nexbridge.com>
> Helped-by: Phillip Wood <phillip.wood123@gmail.com>
> Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>   git-compat-util.h | 40 +++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 39 insertions(+), 1 deletion(-)
> 
> diff --git a/git-compat-util.h b/git-compat-util.h
> index 63ba89dd31d..dfdd3e4f81a 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -393,12 +393,50 @@ 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
> +
> +/*
> + * this helper function overrides a ROOT_UID with the one provided by
> + * an environment variable, do not use unless the original user is
> + * root
> + */
> +static inline void extract_id_from_env(const char *env, uid_t *id)
> +{
> +	const char *real_uid = getenv(env);
> +
> +	/* discard any empty values */
> +	if (real_uid && *real_uid) {
> +		char *endptr;
> +		unsigned long env_id;
> +		int saved_errno = errno;

I still don't understand why you're worried about preserving errno - am 
I missing something? It's not wrong to save it but I'm not sure why we 
need to. is_path_owned_by_current_uid() does not save it around the stat 
call so why do we need to do this here?

> +
> +		errno = 0;
> +		env_id = strtoul(real_uid, &endptr, 10);
> +		if (!errno && !*endptr && env_id <= (uid_t)-1)

Thinking out loud:
"!errno && !*endptr" ensures we have read a valid integer from SUDO_UID. 
If uid_t is unsigned then "env_id <= (uid_t)-1" ensures the value we 
read fits into a uid_t. If uid_t is signed then this test is always true 
and we could truncate the value we read. However if we don't trust 
SUDO_UID then we shouldn't be doing any of this so we are trusting that 
the truncation never happens which means we probably don't need this 
test in the first place.

Best Wishes

Phillip

> +			*id = env_id;
> +
> +		errno = saved_errno;
> +	}
> +}
> +
>   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

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 16:55           ` Junio C Hamano
@ 2022-04-28 18:08             ` Phillip Wood
  2022-04-28 18:12               ` Junio C Hamano
  2022-04-28 19:53             ` rsbecker
  2022-04-28 21:02             ` Carlo Arenas
  2 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-04-28 18:08 UTC (permalink / raw)
  To: Junio C Hamano, Carlo Marcelo Arenas Belón; +Cc: git

On 28/04/2022 17:55, Junio C Hamano wrote:
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
> 
>> +test_description='verify safe.directory checks while running as root'
>> +
>> +. ./test-lib.sh
>> +
>> +if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
> 
> Style.
> 
> 	if test "$IKNOWWHATIAMDOING" != "YES"
> 	then

Also naming - we normally prefix test environment variables with 
GIT_TEST_. IKNOWWHATIAMDOING does not tell us what we are allowing by 
setting the variable. Something like GIT_TEST_ALLOW_SUDO would tell us 
what we're letting ourselves in for by setting it.

>> +is_root() {
>> +	test -n "$1" && CMD="sudo -n"
>> +	test $($CMD id -u) = $(id -u root)
>> +}
> 
> Style.
> 
> 	is_root () {
> 		... body ..
> 
> But more importantly, why do we need this in the first place?
> SANITY prerequisite checks if the user is running as root or
> non-root---can't we use it here?
> 
> Or perhaps my reading is wrong?  I assumed from its name that it was
> just "see if we are running as user 'root' and return 0 or non-zero
> to answer", but if it does more than that, like priming "sudo", then
> probably it is misnamed.

I'm confused by this as well. Also if $1 is empty we run whatever 
happens to be in $CMD.

Best Wishes

Phillip

>> +test_lazy_prereq SUDO '
>> +	is_root sudo &&
>> +	! sudo grep -E '^[^#].*secure_path' /etc/sudoers
>> +'
> 
> OK.
> 
>> +test_lazy_prereq ROOT '
>> +	is_root
>> +'
>> +
>> +test_expect_success SUDO 'setup' '
>> +	sudo rm -rf root &&
>> +	mkdir -p root/r &&
>> +	sudo chown root root &&
>> +	(
>> +		cd root/r &&
>> +		git init
>> +	)
>> +'
> 
> We have a root-owned directory "root" with a subdirectory "r" owned
> by us.  We want to be able to use our "root/r" directory as a
> repository.  OK.
> 
> The prerequisite allows this test to be started as root, but I do
> not quite see the point.  It may pass when started as root, but it
> is not testing what this test is designed to check (i.e. an ordinary
> user who has repository at root/r can do things there).
> 
>> +test_expect_success SUDO 'sudo git status as original owner' '
>> +	(
>> +		cd root/r &&
>> +		git status &&
>> +		sudo git status
>> +	)
>> +'
> 
> And the directory can be used by the user under "sudo", too.  Good.
> 
> The same "this is allowed to run as root, but why?" question
> applies.  If this was started by 'root', root, root/r and
> root/r/.git all are owned by 'root' and we are checking if 'root'
> can run 'git status' as 'root' (or 'root' via sudo) there.  Such a
> test may well pass, but it is not catching a future regression on
> the code you wrote for this series.
> 
>> +test_expect_success SUDO 'setup root owned repository' '
>> +	sudo mkdir -p root/p &&
>> +	sudo git init root/p
>> +'
> 
> Now we go on to create root owned repository at root/p
> 
>> +test_expect_success SUDO,!ROOT 'can access if owned by root' '
>> +	(
>> +		cd root/p &&
>> +		test_must_fail git status
>> +	)
>> +'
> 
> And as an ordinary user, we fail to access a repository that is
> owned by a wrong person (i.e. root).  !ROOT (or SANITY) prereq
> should be there NOT because the test written here would fail if run
> by root, but because running it as root, even if passed, is totally
> pointless, because we are *not* testing "person A has a repository,
> person B cannot access it" combination.
> 
> The other side of the same coin is that the lack of !ROOT (or
> SANITY) prereq in earlier tests I pointed out above misses the point
> of why we have prerequisite mechanism in the first place.  It is not
> to mark a test that fails when the precondition is not met.  It is
> to avoid running code that would NOT test what we want to test.
> 
> The difference is that a test that passes for a wrong reason
> (e.g. we wanted to see of person A can access a repository of their
> own even when the user identity is tentatively switched to 'root'
> via 'sudo'---if person A is 'root', the access will be granted even
> if the special code to handle 'sudo' situation we have is broken)
> should also be excluded with prerequisite.
> 
>> +test_expect_success SUDO,!ROOT 'can access with sudo' '
>> +	# fail to access using sudo
>> +	(
>> +		# TODO: test_must_fail missing functionality
> 
> Care to explain a bit in the log message or in this comment the
> reason why we do not use test_must_fail but use ! here?  Are we
> over-eager to reject anything non "git" be fed, or something?
> 
>> +		cd root/p &&
>> +		! sudo git status
>> +	)
>> +'
> 
> The repository is owned by 'root', but because of the 'sudo'
> "support", you cannot access the repository with "sudo git".
> 
> The test title needs updating.  We expect that the repository cannot
> be accessed under sudo.
> 
>> +test_expect_success SUDO 'can access with workaround' '
> 
> "workarounds", I think.
> 
>> +	# provide explicit GIT_DIR
>> +	(
>> +		cd root/p &&
>> +		sudo sh -c "
>> +			GIT_DIR=.git GIT_WORK_TREE=. git status
>> +		"
>> +	) &&
>> +	# discard SUDO_UID
>> +	(
>> +		cd root/p &&
>> +		sudo sh -c "
>> +			unset SUDO_UID &&
>> +			git status
>> +		"
>> +	)
>> +'
> 
> Again, this lack !ROOT (or SANITY) because tests pass for a wrong
> reason.
> 
> Overall, I like the simplicity and clarity of "do not start this
> test as 'root'" in the previous round much better.
> 
> But other than that, the test coverage given by this patch looks
> quite sensible.
> 
> Thanks.
> 
>> +
>> +test_expect_success SUDO 'cleanup' '
>> +	sudo rm -rf root
>> +'
>> +
>> +test_done


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 18:08             ` Phillip Wood
@ 2022-04-28 18:12               ` Junio C Hamano
  2022-05-06 17:50                 ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28 18:12 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Carlo Marcelo Arenas Belón, git

Phillip Wood <phillip.wood123@gmail.com> writes:

> On 28/04/2022 17:55, Junio C Hamano wrote:
>> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
>> 
>>> +test_description='verify safe.directory checks while running as root'
>>> +
>>> +. ./test-lib.sh
>>> +
>>> +if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
>> Style.
>> 	if test "$IKNOWWHATIAMDOING" != "YES"
>> 	then
>
> Also naming - we normally prefix test environment variables with
> GIT_TEST_. IKNOWWHATIAMDOING does not tell us what we are allowing by 
> setting the variable. Something like GIT_TEST_ALLOW_SUDO would tell us
> what we're letting ourselves in for by setting it.

If this weren't "let's reuse the same mechanism as already used in
1509", I would have had the same reaction.  Renaming would be better
done outside the topic, I would think.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-04-28 18:02           ` Phillip Wood
@ 2022-04-28 18:57             ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-28 18:57 UTC (permalink / raw)
  To: phillip.wood
  Cc: git, gitster, Guy Maurel, SZEDER Gábor, Randall Becker,
	Johannes Schindelin

On Thu, Apr 28, 2022 at 11:02 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>
> On 28/04/2022 11:58, Carlo Marcelo Arenas Belón wrote:
>
> I still don't understand why you're worried about preserving errno - am
> I missing something?

I don't think so, you are correct that doing so is useless now and
could be dropped, I even said so in my message.

> It's not wrong to save it but I'm not sure why we
> need to. is_path_owned_by_current_uid() does not save it around the stat
> call so why do we need to do this here?

because the errno from is_path_owned_by_current_uid() is useful and
unlike this one is not being handled, so a caller of that function
might later use it to distinguish between a "dunno" and "no" answer to
the "is path owned by current uid" question and more importantly
understand better why "dunno" was the answer (ex: path has a loop, or
not accessible or whatever).

it also documents (in the code) that this function won't report any
issues it might have getting that information from the environment
(which extra checks on top to make sure anything suspicious is
ignored), so you can trust that if you get an answer it was a valid
one.

> > +             errno = 0;
> > +             env_id = strtoul(real_uid, &endptr, 10);
> > +             if (!errno && !*endptr && env_id <= (uid_t)-1)
>
> Thinking out loud:
> "!errno && !*endptr" ensures we have read a valid integer from SUDO_UID.
> If uid_t is unsigned then "env_id <= (uid_t)-1" ensures the value we
> read fits into a uid_t. If uid_t is signed then this test is always true

and you hopefully got a warning at build time.

> and we could truncate the value we read. However if we don't trust
> SUDO_UID then we shouldn't be doing any of this so we are trusting that
> the truncation never happens which means we probably don't need this
> test in the first place.

it is there to protect us against a system where uid_t is unsigned but
still narrower than long, and so we don't accidentally assume uid = 0
if we somehow get (the obviously tampered with) SUDO_UID = UINT_MAX +
1

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 16:55           ` Junio C Hamano
  2022-04-28 18:08             ` Phillip Wood
@ 2022-04-28 19:53             ` rsbecker
  2022-04-28 20:22               ` Carlo Arenas
  2022-04-28 20:32               ` Junio C Hamano
  2022-04-28 21:02             ` Carlo Arenas
  2 siblings, 2 replies; 161+ messages in thread
From: rsbecker @ 2022-04-28 19:53 UTC (permalink / raw)
  To: 'Junio C Hamano', 'Carlo Marcelo Arenas Belón'; +Cc: git

On April 28, 2022 12:55 PM, Junio C Hamano wrote:
>Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
>
>> +test_description='verify safe.directory checks while running as root'
>> +
>> +. ./test-lib.sh
>> +
>> +if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
>
>Style.
>
>	if test "$IKNOWWHATIAMDOING" != "YES"
>	then
>
>> +is_root() {
>> +	test -n "$1" && CMD="sudo -n"
>> +	test $($CMD id -u) = $(id -u root)
>> +}
>
>Style.
>
>	is_root () {
>		... body ..
>
>But more importantly, why do we need this in the first place?
>SANITY prerequisite checks if the user is running as root or non-root---can't we
>use it here?
>
>Or perhaps my reading is wrong?  I assumed from its name that it was just "see if
>we are running as user 'root' and return 0 or non-zero to answer", but if it does
>more than that, like priming "sudo", then probably it is misnamed.
>
>> +test_lazy_prereq SUDO '
>> +	is_root sudo &&
>> +	! sudo grep -E '^[^#].*secure_path' /etc/sudoers '

/etc/sudoers is not standard although usual. This path should come from a knob somewhere. We can't run this test on our x86 system anyway - no access to root or sudo even though it is installed. Also, /etc/sudoers is typically secured 0600 so the grep will fail even if is_root passes - and I'm worried about the portability of is_root, which is mostly Linux.

>OK.
>
>> +test_lazy_prereq ROOT '
>> +	is_root
>> +'
>> +
>> +test_expect_success SUDO 'setup' '
>> +	sudo rm -rf root &&
>> +	mkdir -p root/r &&
>> +	sudo chown root root &&
>> +	(
>> +		cd root/r &&
>> +		git init
>> +	)
>> +'
>
>We have a root-owned directory "root" with a subdirectory "r" owned by us.  We
>want to be able to use our "root/r" directory as a repository.  OK.
>
>The prerequisite allows this test to be started as root, but I do not quite see the
>point.  It may pass when started as root, but it is not testing what this test is
>designed to check (i.e. an ordinary user who has repository at root/r can do things
>there).
>
>> +test_expect_success SUDO 'sudo git status as original owner' '
>> +	(
>> +		cd root/r &&
>> +		git status &&
>> +		sudo git status
>> +	)
>> +'
>
>And the directory can be used by the user under "sudo", too.  Good.
>
>The same "this is allowed to run as root, but why?" question applies.  If this was
>started by 'root', root, root/r and root/r/.git all are owned by 'root' and we are
>checking if 'root'
>can run 'git status' as 'root' (or 'root' via sudo) there.  Such a test may well pass, but
>it is not catching a future regression on the code you wrote for this series.
>
>> +test_expect_success SUDO 'setup root owned repository' '
>> +	sudo mkdir -p root/p &&
>> +	sudo git init root/p
>> +'
>
>Now we go on to create root owned repository at root/p
>
>> +test_expect_success SUDO,!ROOT 'can access if owned by root' '
>> +	(
>> +		cd root/p &&
>> +		test_must_fail git status
>> +	)
>> +'
>
>And as an ordinary user, we fail to access a repository that is owned by a wrong
>person (i.e. root).  !ROOT (or SANITY) prereq should be there NOT because the
>test written here would fail if run by root, but because running it as root, even if
>passed, is totally pointless, because we are *not* testing "person A has a
>repository, person B cannot access it" combination.
>
>The other side of the same coin is that the lack of !ROOT (or
>SANITY) prereq in earlier tests I pointed out above misses the point of why we
>have prerequisite mechanism in the first place.  It is not to mark a test that fails
>when the precondition is not met.  It is to avoid running code that would NOT test
>what we want to test.
>
>The difference is that a test that passes for a wrong reason (e.g. we wanted to see
>of person A can access a repository of their own even when the user identity is
>tentatively switched to 'root'
>via 'sudo'---if person A is 'root', the access will be granted even if the special code
>to handle 'sudo' situation we have is broken) should also be excluded with
>prerequisite.
>
>> +test_expect_success SUDO,!ROOT 'can access with sudo' '
>> +	# fail to access using sudo
>> +	(
>> +		# TODO: test_must_fail missing functionality
>
>Care to explain a bit in the log message or in this comment the reason why we do
>not use test_must_fail but use ! here?  Are we over-eager to reject anything non
>"git" be fed, or something?
>
>> +		cd root/p &&
>> +		! sudo git status
>> +	)
>> +'
>
>The repository is owned by 'root', but because of the 'sudo'
>"support", you cannot access the repository with "sudo git".
>
>The test title needs updating.  We expect that the repository cannot be accessed
>under sudo.
>
>> +test_expect_success SUDO 'can access with workaround' '
>
>"workarounds", I think.
>
>> +	# provide explicit GIT_DIR
>> +	(
>> +		cd root/p &&
>> +		sudo sh -c "
>> +			GIT_DIR=.git GIT_WORK_TREE=. git status
>> +		"
>> +	) &&
>> +	# discard SUDO_UID
>> +	(
>> +		cd root/p &&
>> +		sudo sh -c "
>> +			unset SUDO_UID &&
>> +			git status
>> +		"
>> +	)
>> +'
>
>Again, this lack !ROOT (or SANITY) because tests pass for a wrong reason.
>
>Overall, I like the simplicity and clarity of "do not start this test as 'root'" in the
>previous round much better.
>
>But other than that, the test coverage given by this patch looks quite sensible.
>
>Thanks.
>
>> +
>> +test_expect_success SUDO 'cleanup' '
>> +	sudo rm -rf root
>> +'
>> +
>> +test_done


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 19:53             ` rsbecker
@ 2022-04-28 20:22               ` Carlo Arenas
  2022-04-28 20:43                 ` rsbecker
  2022-04-28 20:46                 ` Junio C Hamano
  2022-04-28 20:32               ` Junio C Hamano
  1 sibling, 2 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-28 20:22 UTC (permalink / raw)
  To: rsbecker; +Cc: Junio C Hamano, git

On Thu, Apr 28, 2022 at 12:53 PM <rsbecker@nexbridge.com> wrote:
> /etc/sudoers is not standard although usual. This path should come from a knob somewhere. We can't run this test on our x86 system anyway - no access to root or sudo even though it is installed.

correct and note that the test would succeed if the file doesn't exist
because what we are really interested on, is to make sure that sudo
won't mess with our path when invoking git, and if there is a chance
it would (because that setting is enabled in a different file for
example) then we will just skip these tests.

Obviously the target I had in mind when I built this test was my own
workstation and our public CI, but feel free to provide a fixup that
would also make it work for your private runs if you are interested in
also running this test.

> Also, /etc/sudoers is typically secured 0600 so the grep will fail even if is_root passes

It won't, because it is running with sudo ;).  note that as stated in
the commit message, this requires a fairly open sudo setup (like the
one github provides with their actions).

> - and I'm worried about the portability of is_root, which is mostly Linux.

I actually made sure that is_root was posix shell compatible, but got
a little carried away when refactoring it to accomodate for reuse;
eitherway it is gone in v3.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 19:53             ` rsbecker
  2022-04-28 20:22               ` Carlo Arenas
@ 2022-04-28 20:32               ` Junio C Hamano
  2022-04-28 20:40                 ` rsbecker
  2022-04-28 20:48                 ` Carlo Arenas
  1 sibling, 2 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28 20:32 UTC (permalink / raw)
  To: rsbecker; +Cc: 'Carlo Marcelo Arenas Belón', git

<rsbecker@nexbridge.com> writes:

>>> +test_lazy_prereq SUDO '
>>> +	is_root sudo &&
>>> +	! sudo grep -E '^[^#].*secure_path' /etc/sudoers '
>
> /etc/sudoers is not standard although usual. This path should come
> from a knob somewhere. We can't run this test on our x86 system
> anyway - no access to root or sudo even though it is
> installed. Also, /etc/sudoers is typically secured 0600 so the
> grep will fail even if is_root passes - and I'm worried about the
> portability of is_root, which is mostly Linux.

True.

On a box I happen to be using, the grep gives me

    Defaults secure_path=/usr/sbin:/usr/bin:/sbin:/bin

and makes the prereq fail.  Which is sort-of expected, and I
understand why this check is here, but I suspect that any sane and
sensible installations would reinitialize the path to a fairly
restricted one with the mechanism, so we wouldn't be testing this on
majority of places, I am afraid.

What we really care is that we use the same PATH as the test
environment uses, including "we want to test the binary we just
built" (or "at the specified path", when GIT_TEST_INSTALLED is in
effect).  I wonder what the right way to carry $PATH and other
environment variables down to whatever "sudo" in the test runs.

    $ foo=foobla; export foo
    $ sudo sh -c set | grep foo; echo $?
    1

so resetting PATH from an environment we export, e.g.

    USE_THIS_PATH=$PATH sudo sh -c '
	PATH=$USE_THIS_PATH
	... invoke our git normally here  ...
	git status
    '

would not work X-<.  Worse yet, other environment variables such as
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME that we set in our tests may
probably be cleared before "sudo" runs any test command, so rejecting
an installaion whose "sudo" resets PATH with the above check is probably
insufficient to give our tests a reasonable envionment to run in.

>>Overall, I like the simplicity and clarity of "do not start this test as 'root'" in the
>>previous round much better.
>>
>>But other than that, the test coverage given by this patch looks quite sensible.

So, I unfortunately have to strike the last sentence from my earlier
response.  The tests do mean well, but I doubt they would work in
the way the good intentions planned them to work.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 20:32               ` Junio C Hamano
@ 2022-04-28 20:40                 ` rsbecker
  2022-04-28 20:48                 ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-04-28 20:40 UTC (permalink / raw)
  To: 'Junio C Hamano'; +Cc: 'Carlo Marcelo Arenas Belón', git

On April 28, 2022 4:32 PM, Junio C Hamano wrote:
>To: rsbecker@nexbridge.com
>Cc: 'Carlo Marcelo Arenas Belón' <carenas@gmail.com>; git@vger.kernel.org
>Subject: Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
>
><rsbecker@nexbridge.com> writes:
>
>>>> +test_lazy_prereq SUDO '
>>>> +	is_root sudo &&
>>>> +	! sudo grep -E '^[^#].*secure_path' /etc/sudoers '
>>
>> /etc/sudoers is not standard although usual. This path should come
>> from a knob somewhere. We can't run this test on our x86 system anyway
>> - no access to root or sudo even though it is installed. Also,
>> /etc/sudoers is typically secured 0600 so the grep will fail even if
>> is_root passes - and I'm worried about the portability of is_root,
>> which is mostly Linux.
>
>True.
>
>On a box I happen to be using, the grep gives me
>
>    Defaults secure_path=/usr/sbin:/usr/bin:/sbin:/bin
>
>and makes the prereq fail.  Which is sort-of expected, and I understand why this
>check is here, but I suspect that any sane and sensible installations would
>reinitialize the path to a fairly restricted one with the mechanism, so we wouldn't
>be testing this on majority of places, I am afraid.
>
>What we really care is that we use the same PATH as the test environment uses,
>including "we want to test the binary we just built" (or "at the specified path",
>when GIT_TEST_INSTALLED is in effect).  I wonder what the right way to carry
>$PATH and other environment variables down to whatever "sudo" in the test
>runs.
>
>    $ foo=foobla; export foo
>    $ sudo sh -c set | grep foo; echo $?
>    1
>
>so resetting PATH from an environment we export, e.g.
>
>    USE_THIS_PATH=$PATH sudo sh -c '
>	PATH=$USE_THIS_PATH
>	... invoke our git normally here  ...
>	git status
>    '
>
>would not work X-<.  Worse yet, other environment variables such as
>GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME that we set in our tests may
>probably be cleared before "sudo" runs any test command, so rejecting an
>installaion whose "sudo" resets PATH with the above check is probably insufficient
>to give our tests a reasonable envionment to run in.
>
>>>Overall, I like the simplicity and clarity of "do not start this test
>>>as 'root'" in the previous round much better.
>>>
>>>But other than that, the test coverage given by this patch looks quite sensible.
>
>So, I unfortunately have to strike the last sentence from my earlier response.  The
>tests do mean well, but I doubt they would work in the way the good intentions
>planned them to work.

sudo on my machines is very specific that it does not preserve much of the environment. This causes all kinds of DLL load issues too. We rarely can build regression test suites that include sudo testing. More than that, it is general policy that password-less sudo is frowned upon, so I'm pretty sure we cannot run this test. When we run the git test suite, it is done by a user who is, by design, excluded from the sudoers file because regression tests happen in a sandbox.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 20:22               ` Carlo Arenas
@ 2022-04-28 20:43                 ` rsbecker
  2022-04-28 20:51                   ` Junio C Hamano
  2022-04-28 20:56                   ` Carlo Arenas
  2022-04-28 20:46                 ` Junio C Hamano
  1 sibling, 2 replies; 161+ messages in thread
From: rsbecker @ 2022-04-28 20:43 UTC (permalink / raw)
  To: 'Carlo Arenas'; +Cc: 'Junio C Hamano', git

On April 28, 2022 4:23 PM, Carlo Arenas wrote:
>On Thu, Apr 28, 2022 at 12:53 PM <rsbecker@nexbridge.com> wrote:
>> /etc/sudoers is not standard although usual. This path should come from a knob
>somewhere. We can't run this test on our x86 system anyway - no access to root
>or sudo even though it is installed.
>
>correct and note that the test would succeed if the file doesn't exist because what
>we are really interested on, is to make sure that sudo won't mess with our path
>when invoking git, and if there is a chance it would (because that setting is enabled
>in a different file for
>example) then we will just skip these tests.
>
>Obviously the target I had in mind when I built this test was my own workstation
>and our public CI, but feel free to provide a fixup that would also make it work for
>your private runs if you are interested in also running this test.
>
>> Also, /etc/sudoers is typically secured 0600 so the grep will fail
>> even if is_root passes
>
>It won't, because it is running with sudo ;).  note that as stated in the commit
>message, this requires a fairly open sudo setup (like the one github provides with
>their actions).
>
>> - and I'm worried about the portability of is_root, which is mostly Linux.
>
>I actually made sure that is_root was posix shell compatible, but got a little carried
>away when refactoring it to accomodate for reuse; eitherway it is gone in v3.

I tried to find is_root in POSIX but could not. Do you have a reference? It is not in bash 4.3.48, which is on our older system.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 20:22               ` Carlo Arenas
  2022-04-28 20:43                 ` rsbecker
@ 2022-04-28 20:46                 ` Junio C Hamano
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28 20:46 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: rsbecker, git

Carlo Arenas <carenas@gmail.com> writes:

> It won't, because it is running with sudo ;).  note that as stated in
> the commit message, this requires a fairly open sudo setup (like the
> one github provides with their actions).

Ahh, OK.  So this is pretty much only for CI environment and such,
not on a typical developer and end-user box.

OK, but the potential issues that I raised about cleansed
environment, not limited to $PATH, still exists.  Perhaps
the prereq check can be tightened to something like:

    GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=no-no-name \
    PATH=no-such-path:$PATH \
    sudo sh -c "echo '\$PATH \$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME'" |
    grep "^no-such-path.* no-no-name$"

i.e. we export an envionment with a known value that is unlikely
value for the variable in tester's environment, prepend a known
string that unlikely begins the tester's $PATH, and ask sudo what
values, if any, the process sudo spawned sees in these two
environment variables.  An environment that does not molest PATH and
passes environment variables we set in the tests to affect execution
of "git" being tested will pass the above test.  Otherwise the
environment would silently be breaking our expectation.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 20:32               ` Junio C Hamano
  2022-04-28 20:40                 ` rsbecker
@ 2022-04-28 20:48                 ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-04-28 20:48 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: rsbecker, git

On Thu, Apr 28, 2022 at 1:32 PM Junio C Hamano <gitster@pobox.com> wrote:
> On a box I happen to be using, the grep gives me
>
>     Defaults secure_path=/usr/sbin:/usr/bin:/sbin:/bin
>
> and makes the prereq fail.  Which is sort-of expected, and I
> understand why this check is here, but I suspect that any sane and
> sensible installations would reinitialize the path to a fairly
> restricted one with the mechanism, so we wouldn't be testing this on
> majority of places, I am afraid.

our CI runs with macOS should still work, and any place that does is
better that none IMHO

> What we really care is that we use the same PATH as the test
> environment uses, including "we want to test the binary we just
> built" (or "at the specified path", when GIT_TEST_INSTALLED is in
> effect).  I wonder what the right way to carry $PATH and other
> environment variables down to whatever "sudo" in the test runs.

sadly the issue is even more insidious because the PATH is not
changed, but sudo ignores it when going to look for a binary to run

>     $ foo=foobla; export foo
>     $ sudo sh -c set | grep foo; echo $?
>     1
>
> so resetting PATH from an environment we export, e.g.
>
>     USE_THIS_PATH=$PATH sudo sh -c '
>         PATH=$USE_THIS_PATH
>         ... invoke our git normally here  ...
>         git status
>     '
>
> would not work X-<.  Worse yet, other environment variables such as
> GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME that we set in our tests may
> probably be cleared before "sudo" runs any test command, so rejecting
> an installaion whose "sudo" resets PATH with the above check is probably
> insufficient to give our tests a reasonable envionment to run in.

I think we are confusing, running the whole suite with sudo vs running
a specific command inside a test with it.  We obviously will need to
adapt our suite a lot more before it will natively support sudo and
that might never happen as well, since there are conflicting
objectives there.  IMHO that doesn't mean we shouldn't try.

Maybe I should document better what I meant with "awkward", but yes I
was serious when I said "minimal support" for running things through
sudo, but I should mention the ones you provided almost worked AS-IS
so it is not that bad IMHO.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 20:43                 ` rsbecker
@ 2022-04-28 20:51                   ` Junio C Hamano
  2022-04-28 20:56                   ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28 20:51 UTC (permalink / raw)
  To: rsbecker; +Cc: 'Carlo Arenas', git

<rsbecker@nexbridge.com> writes:

>>I actually made sure that is_root was posix shell compatible, but got a little carried
>>away when refactoring it to accomodate for reuse; eitherway it is gone in v3.
>
> I tried to find is_root in POSIX but could not. Do you have a reference? It is not in bash 4.3.48, which is on our older system.

What he meant was the implementation of is_root shell function he
wrote in the patch uses construct from POSIX.

        is_root() {
                test -n "$1" && CMD="sudo -n"
                test $($CMD id -u) = $(id -u root)
        }

Besides, as somebody else already pointed out, this will run random
command that is in $CMD (perhaps from tester's environment) when it
is run without $1 or an empty string in $1.  But other than that,
"id" being in POSIX.1, that looks fairly safe.  Of course, sudo and
sudo -n would not be in POSIX, but that is what this one is testing
availablity for, so it is to be expected ;-)

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 20:43                 ` rsbecker
  2022-04-28 20:51                   ` Junio C Hamano
@ 2022-04-28 20:56                   ` Carlo Arenas
  2022-04-28 21:55                     ` rsbecker
  1 sibling, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-28 20:56 UTC (permalink / raw)
  To: rsbecker; +Cc: Junio C Hamano, git

On Thu, Apr 28, 2022 at 1:43 PM <rsbecker@nexbridge.com> wrote:
> I tried to find is_root in POSIX but could not. Do you have a reference? It is not in bash 4.3.48, which is on our older system.

my bad; is_root is a helper function i provided as part of this file;
the latest version which should work in your posix system AND was
specifically written to hopefully not break with NON-STOP based on
what you told us about it looks like (hand edited and not tested) :

is_root() {
  id -u >u
  id -u root >r
  cmp u r
}

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 16:55           ` Junio C Hamano
  2022-04-28 18:08             ` Phillip Wood
  2022-04-28 19:53             ` rsbecker
@ 2022-04-28 21:02             ` Carlo Arenas
  2022-04-28 21:07               ` Junio C Hamano
  2 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-04-28 21:02 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Thu, Apr 28, 2022 at 9:55 AM Junio C Hamano <gitster@pobox.com> wrote:
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
> > +is_root() {
> > +     test -n "$1" && CMD="sudo -n"
> > +     test $($CMD id -u) = $(id -u root)
> > +}
>
> Style.
>
>         is_root () {
>                 ... body ..
>
> But more importantly, why do we need this in the first place?
> SANITY prerequisite checks if the user is running as root or
> non-root---can't we use it here?

my bad; tried first to use NON_ROOT but it didn't work and SANITY
seems way too complicated for what is really needed, plus this can be
shared by both prerequisites, and more importantly allows me to
introduce an exploit with that CMD trick, but Phillip's eagle eyes
already blocked me so it is gone and replaced with SANITY for v3

> Or perhaps my reading is wrong?  I assumed from its name that it was
> just "see if we are running as user 'root' and return 0 or non-zero
> to answer", but if it does more than that, like priming "sudo", then
> probably it is misnamed.

it does both indeed, and is also why I am pulling the SUDO
prerequisite on each test instead of checking once at the beginning of
the file and being done with it.

I would rather have some tests skipped if sudo can't get root without
password than a failed test, and want sudo to always work and not
"possibly hang, waiting for a password" during each run, not to block
CI either.

> We have a root-owned directory "root" with a subdirectory "r" owned
> by us.  We want to be able to use our "root/r" directory as a
> repository.  OK.
>
> The prerequisite allows this test to be started as root, but I do
> not quite see the point.

I have to agree that I was looking at it the other way, my concern
with allowing root to call this was to make sure that none of my
changes (or any future ones) prevent them to do what they should
normally do, hence why I only disabled for root the tests that
couldn't work because make no sense (just like is done for tests that
rely on a case insensitive filesystem, for example)

More on that in the next test.

> It may pass when started as root, but it
> is not testing what this test is designed to check (i.e. an ordinary
> user who has a repository at root/r can do things there).

IMHO these tests should validate that ANY user can do what is
expected, and that includes root (the two versions of it), not only
"regular users"

> > +test_expect_success SUDO 'sudo git status as original owner' '
> > +     (
> > +             cd root/r &&
> > +             git status &&
> > +             sudo git status
> > +     )
> > +'
> If this was started by 'root', root, root/r and
> root/r/.git all are owned by 'root' and we are checking if 'root'
> can run 'git status' as 'root' (or 'root' via sudo) there.  Such a
> test may well pass, but it is not catching a future regression on
> the code you wrote for this series.

It is subtle but it does, when run as a real root it will pass, but if
we run it through sudo it fails because of the change that was
introduced in this series.

> > +test_expect_success SUDO,!ROOT 'can access if owned by root' '
> > +     (
> > +             cd root/p &&
> > +             test_must_fail git status
> > +     )
> > +'
>
> And as an ordinary user, we fail to access a repository that is
> owned by a wrong person (i.e. root).  !ROOT (or SANITY) prereq
> should be there NOT because the test written here would fail if run
> by root, but because running it as root, even if passed, is totally
> pointless, because we are *not* testing "person A has a repository,
> person B cannot access it" combination.
>
> The other side of the same coin is that the lack of !ROOT (or
> SANITY) prereq in earlier tests I pointed out above misses the point
> of why we have prerequisite mechanism in the first place.  It is not
> to mark a test that fails when the precondition is not met.  It is
> to avoid running code that would NOT test what we want to test.
>
> The difference is that a test that passes for a wrong reason
> (e.g. we wanted to see of person A can access a repository of their
> own even when the user identity is tentatively switched to 'root'
> via 'sudo'---if person A is 'root', the access will be granted even
> if the special code to handle 'sudo' situation we have is broken)
> should also be excluded with prerequisite.

Agree I am abusing the prerequisites, I am instead removing the tests
since they are pointless when run as root in v3, which would have been
part of the first proposal, even if slightly more complicated.

> > +test_expect_success SUDO,!ROOT 'can access with sudo' '
> > +     # fail to access using sudo
> > +     (
> > +             # TODO: test_must_fail missing functionality
>
> Care to explain a bit in the log message or in this comment the
> reason why we do not use test_must_fail but use ! here?  Are we
> over-eager to reject anything non "git" be fed, or something?

correct since 6a67c759489 (test-lib-functions: restrict test_must_fail
usage, 2020-07-07) using anything but git fails, and improving that
now is IMHO not worth it.

The only protection we get from using test_must_fail instead would be
to know if we introduced a crash, but the same command has run several
times already so IMHO it is unlikely.

will obviously not miss the opportunity to enhance test_must_fail and
remove the TODO otherwise ASAP.

> Overall, I like the simplicity and clarity of "do not start this
> test as 'root'" in the previous round much better.

I disagree, and think that the fact that the second test changes
behaviour with this series proves my point.

I agree I was abusing the prerequisites, but was in the name of making
this change simpler, I am hoping
and slightly more complicated version that doesn't abuse them would be
better than having a simpler one where those issues are hidden and
even if we currently have no "run as root CI jobs" and last time I
tried one found it is broken somewhere else.

Either Way those issues are orthogonal to this change and would be
happy to discuss again after v3 which is still not ready and will be
posted most likely as an RFC including as much as can from the
feedback provided so far.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 21:02             ` Carlo Arenas
@ 2022-04-28 21:07               ` Junio C Hamano
  2022-04-29  1:24                 ` Carlo Marcelo Arenas Belón
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28 21:07 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: git

Carlo Arenas <carenas@gmail.com> writes:

>> Overall, I like the simplicity and clarity of "do not start this
>> test as 'root'" in the previous round much better.
>
> I disagree, and think that the fact that the second test changes
> behaviour with this series proves my point.

I do not know which second test you are talking about, but anyway.

> Either Way those issues are orthogonal to this change and would be
> happy to discuss again after v3 which is still not ready and will be
> posted most likely as an RFC including as much as can from the
> feedback provided so far.

Sure. Looking forward to seeing a version that does *NOT* abuse
prerequisite mechanism.  We are interested in testing an ordinary
user becoming root via sudo in these tests, so reject whoever starts
the test under 'root' upfront like in the earlier round.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 20:56                   ` Carlo Arenas
@ 2022-04-28 21:55                     ` rsbecker
  2022-04-28 22:21                       ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: rsbecker @ 2022-04-28 21:55 UTC (permalink / raw)
  To: 'Carlo Arenas'; +Cc: 'Junio C Hamano', git

On April 28, 2022 4:56 PM, Carlo Arenas wrote:
>On Thu, Apr 28, 2022 at 1:43 PM <rsbecker@nexbridge.com> wrote:
>> I tried to find is_root in POSIX but could not. Do you have a reference? It is not in
>bash 4.3.48, which is on our older system.
>
>my bad; is_root is a helper function i provided as part of this file; the latest version
>which should work in your posix system AND was specifically written to hopefully
>not break with NON-STOP based on what you told us about it looks like (hand
>edited and not tested) :
>
>is_root() {
>  id -u >u
>  id -u root >r
>  cmp u r
>}

This is about as portable as I can find and works even in ksh. It could be optimized.

is_root() {
  id -u >u
  id -u root >r
  cmp -s u r
  if [ $? -ne 0 ]; then
    echo 0
  else
    echo 1
  fi
}

if [ `is_root` -ne 0 ]; then
        echo root
else
        echo Not root
fi


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 21:55                     ` rsbecker
@ 2022-04-28 22:21                       ` Junio C Hamano
  2022-04-28 22:45                         ` rsbecker
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-04-28 22:21 UTC (permalink / raw)
  To: rsbecker; +Cc: 'Carlo Arenas', git

<rsbecker@nexbridge.com> writes:

>>is_root() {
>>  id -u >u
>>  id -u root >r
>>  cmp u r
>>}
>
> This is about as portable as I can find and works even in ksh. It could be optimized.
>
> is_root() {
>   id -u >u
>   id -u root >r
>   cmp -s u r
>   if [ $? -ne 0 ]; then
>     echo 0
>   else
>     echo 1
>   fi
> }
>
> if [ `is_root` -ne 0 ]; then
>         echo root
> else
>         echo Not root
> fi

The above looks very roundabout way.  With the first three in
is_root that ends with "cmp", we already know from its exit status
if "id -u" output for ourselves matches that for root, i.e. if we
are root then cmp would have exited with 0.

So with the first one I quoted from your quote, the caller can say

	if is_root
	then
		echo root
	else
		echo not root
	fi

without turning the exit status into string "0" or "1" and comparing
that string with "[ `cmd` -ne 0 ]".  And the first one is just as
portable.  I agree that running cmp with "-s" is probably a good
idea.

What I used to often use in my previous life (in previous century)
is technically incorrect, but is a lot more succinct and works well
in practice on any sanely installed systems.  Just see if the root
directory is writable.  No sane system makes it writable by anybody
but root.

I.e.

	if test -w /
	then
		... we are running as root ...
	else
		... we are not running as root ...
	fi

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 22:21                       ` Junio C Hamano
@ 2022-04-28 22:45                         ` rsbecker
  0 siblings, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-04-28 22:45 UTC (permalink / raw)
  To: 'Junio C Hamano'; +Cc: 'Carlo Arenas', git

On April 28, 2022 6:22 PM, Junio C Hamano wrote:
><rsbecker@nexbridge.com> writes:
>
>>>is_root() {
>>>  id -u >u
>>>  id -u root >r
>>>  cmp u r
>>>}
>>
>> This is about as portable as I can find and works even in ksh. It could
be
>optimized.
>>
>> is_root() {
>>   id -u >u
>>   id -u root >r
>>   cmp -s u r
>>   if [ $? -ne 0 ]; then
>>     echo 0
>>   else
>>     echo 1
>>   fi
>> }
>>
>> if [ `is_root` -ne 0 ]; then
>>         echo root
>> else
>>         echo Not root
>> fi
>
>The above looks very roundabout way.  With the first three in is_root that
ends
>with "cmp", we already know from its exit status if "id -u" output for
ourselves
>matches that for root, i.e. if we are root then cmp would have exited with
0.
>
>So with the first one I quoted from your quote, the caller can say
>
>	if is_root
>	then
>		echo root
>	else
>		echo not root
>	fi
>
>without turning the exit status into string "0" or "1" and comparing that
string with
>"[ `cmd` -ne 0 ]".  And the first one is just as portable.  I agree that
running cmp
>with "-s" is probably a good idea.
>
>What I used to often use in my previous life (in previous century) is
technically
>incorrect, but is a lot more succinct and works well in practice on any
sanely
>installed systems.  Just see if the root directory is writable.  No sane
system makes
>it writable by anybody but root.
>
>I.e.
>
>	if test -w /
>	then
>		... we are running as root ...
>	else
>		... we are not running as root ...
>	fi

I agree. I think my ksh is rather broken. But we use bash for git testing
and can never go back to ksh, so no worries on that score. Thanks.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 21:07               ` Junio C Hamano
@ 2022-04-29  1:24                 ` Carlo Marcelo Arenas Belón
  2022-04-29 18:50                   ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-29  1:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Thu, Apr 28, 2022 at 02:07:56PM -0700, Junio C Hamano wrote:
> Carlo Arenas <carenas@gmail.com> writes:
> 
> >> Overall, I like the simplicity and clarity of "do not start this
> >> test as 'root'" in the previous round much better.
> >
> > I disagree, and think that the fact that the second test changes
> > behavior with this series proves my point.
> 
> I do not know which second test you are talking about, but anyway.

My bad; by "second test" I was referring to the one that I introduced to
track the regression and its fix and which has its behavior changed, but
you would only notice if looking at it from all angles (and yes, that
includes as a regular user, as well as root, with and without sudo :

If we do :

  [0] login as regular user || sudo to root || login as root
  [1] % mkdir -p root/r
  [2] % git init root/r
  [3] % cd root/r && git status

we get

  step \ type | regular user | sudo to root | root
--------------------------------------------------
            1 |    work      |     work     | work
  before    2 |    work      |     work     | work
            3 |    work      |     work     | work
---------------------------------------------------
            1 |    work      |     work     | work
   after    2 |    work      |     work     | work
            3 |    work      |     fail     | work

the reason why it fails is expected (git now finds the SUDO_UID variable
and rejects the repository because it is NOT owned by that id (it was created
by root anyway, even if there is no way for git to know that it was done
at a different time and with a different session, and therefore the SUDO_UID
variable it is honoring could be considered irrelevant in the current context.

in the documentation patch (which I think would be better to squash with the
fix) I explain what to do as a workaround, and I expect this use case to be
less common than the currently broken one (so a net positive)

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged
  2022-04-26 19:48 ` Derrick Stolee
  2022-04-26 19:56   ` Junio C Hamano
  2022-04-26 20:26   ` Carlo Arenas
@ 2022-04-29 16:16   ` Derrick Stolee
  2 siblings, 0 replies; 161+ messages in thread
From: Derrick Stolee @ 2022-04-29 16:16 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git
  Cc: philipoakley, me, Guy Maurel, SZEDER Gábor, Randall Becker,
	Johannes Schindelin

On 4/26/2022 3:48 PM, Derrick Stolee wrote:
> On 4/26/2022 2:31 PM, Carlo Marcelo Arenas Belón wrote:

>> +		const char *real_uid = getenv("SUDO_UID");
>> +		if (real_uid && *real_uid)
>> +			euid = atoi(real_uid);
>> +		else {
>> +			real_uid = getenv("DOAS_UID");
>> +			if (real_uid && *real_uid)
>> +				euid = atoi(real_uid);
>> +		}
> 
> I imagine that something else could be added here to help Windows
> users who have elevated to administrator privileges. It will use a
> completely different mechanism, though, if needed at all. We can
> delay that for now.

Just chiming in to say that the way we do this check on Windows,
there is no difference between a user and the same user with
elevated privileges (the "Security Identifier (SID)" is the same
in both cases).

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-29  1:24                 ` Carlo Marcelo Arenas Belón
@ 2022-04-29 18:50                   ` Junio C Hamano
  2022-04-29 20:05                     ` Carlo Marcelo Arenas Belón
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-04-29 18:50 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón; +Cc: git

Carlo Marcelo Arenas Belón <carenas@gmail.com> writes:

> If we do :
>
>   [0] login as regular user || sudo to root || login as root

Among these three, the last one is equivalent to "sudo and then
unset the environment", right?

As many installations no longer allow direct "login" as "root",
clarifying how users can get the behaviour for the third column
would help our documentation, and that is the reason behind the
question.  IOW, this is merely a simple yes-or-no question to
sanity-check our mutual understanding, and no need to get overly
defensive about being asked.

>   [1] % mkdir -p root/r
>   [2] % git init root/r
>   [3] % cd root/r && git status
>
>   step \ type | regular user | sudo to root | root
> --------------------------------------------------
>             1 |    work      |     work     | work
>   before    2 |    work      |     work     | work
>             3 |    work      |     work     | work
> ---------------------------------------------------
>             1 |    work      |     work     | work
>    after    2 |    work      |     work     | work
>             3 |    work      |     fail     | work

So the only difference is that in a repository created by a user who
did "sudo mkdir; sudo git init".  It used to be that the same user
can read the repository with "sudo git status" (because we did not
care about how we become 'root', we only saw the owner of the repo
and the current euid).  Now, such an access is no longer allowed.

And a workaround is to use the third-column behaviour, i.e. either
login as root (which is probably too cumbersome as a step in a
typical "make && make test && make install" sequence where at least
the last step need to be done as a privileged user) or use "sudo"
and drop the SUDO_UID environment, for which, the documentation was
added in this series.

But I do not see what relevance the above has to the argument you
were making, against "if you start these tests as 'root' (instead of
starting as an ordinary user), some tests may succeed but for a
wrong reason, and some tests may fail because they are not prepared
for it; it is wrong to mark only the latter with prerequisite and
not the former".  The change in the behaviour we see above is for
those who start as an ordinary user and uses "sudo" without dropping
SUDO_UID.  How is it relevant to allow those who start the test as
'root' (not an ordinary user) to try that?  Tests done under such
condition will see 'root' in euid, SUDO_UID, and st.st_uid, so there
is no way for them to detect any mismatch and behave differently,
so the transition from "it used to work" to "now it is forbidden"
is not even tested.

> and rejects the repository because it is NOT owned by that id (it was created
> by root anyway, even if there is no way for git to know that it was done
> at a different time and with a different session, and therefore the SUDO_UID
> variable it is honoring could be considered irrelevant in the current context.
>
> in the documentation patch (which I think would be better to squash with the
> fix) I explain what to do as a workaround, and I expect this use case to be
> less common than the currently broken one (so a net positive)

Both of these two paragraphs speak truth, but there is no relevance
to, and it does not justify, your "I disagree, and think that the
fact ... proves my point".

For example, this is the 'setup' step.

> +test_expect_success SUDO 'setup' '
> +	sudo rm -rf root &&
> +	mkdir -p root/r &&
> +	sudo chown root root &&
> +	(
> +		cd root/r &&
> +		git init
> +	)
> +'

If the test was started by an ordinary user, root/r is owned by the
user who is not 'root'.  If the test was started by 'root',
everything is owned by 'root'.  Either way, 'root' is owned by
'root'.  In such a repository, we see this test:

+test_expect_success SUDO 'sudo git status as original owner' '
+	(
+		cd root/r &&
+		git status &&
+		sudo git status
+	)
+'

The behaviour we are trying to ensure is that, even though root/r is
owned by non-root, accessing it with "git status" as the original
user and "git status" as root work, as long as if you used "sudo" in
the second "git status", so that "git status" can take SUDO_UID into
account.  The test is making sure that our "pay attention to
SUDO_UID" mechanism has not been broken by future changes.

If we start this test as 'root', we cannot test for that.  The setup
step made everything owned by 'root', and we go there as 'root' and
run "git status", which should succceed, but with "sudo git status",
even we broke SUDO_UID mechanism and compared euid with st.st_uid,
we'll allow an access.

So the test may succeed but it succeeds for a wrong reason even
after we break the mechanism added by this series.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-29 18:50                   ` Junio C Hamano
@ 2022-04-29 20:05                     ` Carlo Marcelo Arenas Belón
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-29 20:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git

On Fri, Apr 29, 2022 at 11:50:40AM -0700, Junio C Hamano wrote:
> Carlo Marcelo Arenas Belón <carenas@gmail.com> writes:
> 
> > If we do :
> >
> >   [0] login as regular user || sudo to root || login as root
> 
> Among these three, the last one is equivalent to "sudo and then
> unset the environment", right?

Correct, the differences from the most likely sequence of commands
of a potential tester on their workstation (which as you point,
most of the time doesn't allow login in as root) is :

  $ sudo -s -H
  # unset SUDO_UID
  # IKNOWWHATIAMDOING=yes ./t0034-root-safe-directory.sh

the unset only relevant for the third option, of course.

> As many installations no longer allow direct "login" as "root",
> clarifying how users can get the behaviour for the third column
> would help our documentation, and that is the reason behind the
> question.  IOW, this is merely a simple yes-or-no question to
> sanity-check our mutual understanding, and no need to get overly
> defensive about being asked.

Apologies if sounding otherwise, but I can assure I am not offended
by any of this questions, and looking forward to improve in the
documentation with the help you had provided on reviewing it.

I would also like to thank you personally, since I am well aware
that as a not native English speaker, and because Spanish is my
mother's tongue I usually write things in a way that is not natural
or expected and also overly verbose and without expected pauses.  So
having a second set of eyes on it is specially appreciated.

> >   [1] % mkdir -p root/r
> >   [2] % git init root/r
> >   [3] % cd root/r && git status
> >
> >   step \ type | regular user | sudo to root | root
> > --------------------------------------------------
> >             1 |    work      |     work     | work
> >   before    2 |    work      |     work     | work
> >             3 |    work      |     work     | work
> > ---------------------------------------------------
> >             1 |    work      |     work     | work
> >    after    2 |    work      |     work     | work
> >             3 |    work      |     fail     | work
> 
> So the only difference is that in a repository created by a user who
> did "sudo mkdir; sudo git init".  It used to be that the same user
> can read the repository with "sudo git status" (because we did not
> care about how we become 'root', we only saw the owner of the repo
> and the current euid).  Now, such an access is no longer allowed.

Correct, but more importantly we now allow the sequence documented
in the test case which is also IMHO more comnon and useful, where the
last step might need to run through sudo (unlike the previous ones)

> But I do not see what relevance the above has to the argument you
> were making, against "if you start these tests as 'root' (instead of
> starting as an ordinary user), some tests may succeed but for a
> wrong reason, and some tests may fail because they are not prepared
> for it; it is wrong to mark only the latter with prerequisite and
> not the former".  The change in the behaviour we see above is for
> those who start as an ordinary user and uses "sudo" without dropping
> SUDO_UID.  How is it relevant to allow those who start the test as
> 'root' (not an ordinary user) to try that?  Tests done under such
> condition will see 'root' in euid, SUDO_UID, and st.st_uid, so there
> is no way for them to detect any mismatch and behave differently,
> so the transition from "it used to work" to "now it is forbidden"
> is not even tested.

Correct again; guess my intention wasn't clear.  My concern was that by
blocking root, we don't allow for such a test (if it can be written,
since as you pointed out is challenging) to be added and we also lose
the currently failing run which can be used as an "explanation" of sorts
to the question of "is this supported and expected to work?" which I
think the tests help answer and regardless of what is in the documentation.

> > and rejects the repository because it is NOT owned by that id (it was created
> > by root anyway, even if there is no way for git to know that it was done
> > at a different time and with a different session, and therefore the SUDO_UID
> > variable it is honoring could be considered irrelevant in the current context.
> >
> > in the documentation patch (which I think would be better to squash with the
> > fix) I explain what to do as a workaround, and I expect this use case to be
> > less common than the currently broken one (so a net positive)
> 
> Both of these two paragraphs speak truth, but there is no relevance
> to, and it does not justify, your "I disagree, and think that the
> fact ... proves my point".

I ended up doing a bigger refactoring with v3 that split the tests in two and
which I think will help also in the long run, but kept the Documentation patch
independent since it already has your SO and don't want to waste your time
further by having to re-review it.

Assuming that there are no more improvements (or even if they are) to be made
to the documentation would be OK if I follow my own advice and squash it
together with the code change that introduces the change?

Apologies for not doing it earlier, but in my defense I would say that I sent
the currently applied version originally as an RFC ;)

> For example, this is the 'setup' step.
> 
> > +test_expect_success SUDO 'setup' '
> > +	sudo rm -rf root &&
> > +	mkdir -p root/r &&
> > +	sudo chown root root &&
> > +	(
> > +		cd root/r &&
> > +		git init
> > +	)
> > +'
> 
> If the test was started by an ordinary user, root/r is owned by the
> user who is not 'root'.  If the test was started by 'root',
> everything is owned by 'root'.  Either way, 'root' is owned by
> 'root'.  In such a repository, we see this test:
> 
> +test_expect_success SUDO 'sudo git status as original owner' '
> +	(
> +		cd root/r &&
> +		git status &&
> +		sudo git status
> +	)
> +'
> 
> The behaviour we are trying to ensure is that, even though root/r is
> owned by non-root, accessing it with "git status" as the original
> user and "git status" as root work, as long as if you used "sudo" in
> the second "git status", so that "git status" can take SUDO_UID into
> account.  The test is making sure that our "pay attention to
> SUDO_UID" mechanism has not been broken by future changes.
> 
> If we start this test as 'root', we cannot test for that.  The setup
> step made everything owned by 'root', and we go there as 'root' and
> run "git status", which should succceed, but with "sudo git status",
> even we broke SUDO_UID mechanism and compared euid with st.st_uid,
> we'll allow an access.

Depending of the way you become 'root' the test could fail as well,
but IMHO the failing should be expected (and as shown in the table
above) was introduced with this change.

The gist is that if we started the whole test with sudo, there is
no way for git (with the current implementation) to differenciate if that
SUDO_UID is relevant only to its current run or to the environment and
more importantly what the intentions are from the user that is running
on that environment.

Alternatively we could ammend the code to allow for such logic but I
don't think that would be easier to implement and I don't see much of
a benefit.

My thinking is that if we are going to let sudo tells us who the user is
then we must trust that SUDO_UID indicates also the intention of the user
behind that environment and therefore it is correct in this case to fail.

If the user really meant to not trust its SUDO_UID and instead use the
id we are running as, then he should have removed it from the environment
before invoking git as documented.

Long term, as you proposed, if we are running as root we might ALSO
consider that any root owned repositories should be fine to trust but
that change is orthogonal to fixing the 'sudo git <command> in my own
repository doesn't work after the last maint release'

It should also be important to note that starting the whole test with
sudo is not trivial, neither expected, and will also block because of
sudo most likely removing the IKNOWWHATIAMDOING environment variable,
so whichever way we decide we shouldn't expect someone accidentally
failing this test.

> So the test may succeed but it succeeds for a wrong reason even
> after we break the mechanism added by this series.

the test as root without a SUDO_UID would succeed for the wrong reasons
so I agree (and again apologize for suggesting it) that using a
prerequisite to squash that run (as done in the other tests) was wrong.

what I am still not sure about is if it is worth complicating the test
by adding logic that differenciate a root user with and without SUDO_UID
that might run it, and I have to admit that the current draft I have of
v3 does follow your advice of blocking root from even being able to run
it as suggested (and which is also what I am leaning for).

Carlo

PS. I apologize for not trimming on your responses more aggresively to
save some bytes as I would normally do, but wanted to make sure you
understand that by trimming those parts I wasn't implying they weren't
read or relevant, but just not strictly neccesary and implictly agreed
upon for further discussion

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo
  2022-04-28 10:58         ` [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
@ 2022-04-30  6:17           ` Bagas Sanjaya
  2022-04-30  6:39             ` Junio C Hamano
  2022-04-30 14:15             ` Carlo Marcelo Arenas Belón
  0 siblings, 2 replies; 161+ messages in thread
From: Bagas Sanjaya @ 2022-04-30  6:17 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón; +Cc: git, gitster

On Thu, Apr 28, 2022 at 03:58:51AM -0700, Carlo Marcelo Arenas Belón wrote:
> In a previous patch, the behavior of git was changed so it will be able
> to find the "effective uid" that is required when git was invoked with
> sudo to root, for example the internal calls made to git when calling
> the following in git's own repository:
> 
>   $ sudo make install
>

That is building Git, right?

-- 
An old man doll... just what I always wanted! - Clara

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo
  2022-04-30  6:17           ` Bagas Sanjaya
@ 2022-04-30  6:39             ` Junio C Hamano
  2022-04-30 14:15             ` Carlo Marcelo Arenas Belón
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-04-30  6:39 UTC (permalink / raw)
  To: Bagas Sanjaya; +Cc: Carlo Marcelo Arenas Belón, git

Bagas Sanjaya <bagasdotme@gmail.com> writes:

> On Thu, Apr 28, 2022 at 03:58:51AM -0700, Carlo Marcelo Arenas Belón wrote:
>> In a previous patch, the behavior of git was changed so it will be able
>> to find the "effective uid" that is required when git was invoked with
>> sudo to root, for example the internal calls made to git when calling
>> the following in git's own repository:
>> 
>>   $ sudo make install
>>
>
> That is building Git, right?

Why do you want to know?  In other words, if Carlo answers "yes" (or
"no" for that matter), what are you going to do with that piece of
information?  E-mailed communications with people on other
continents are inherently high latency, so it is more efficient to
try reducing number of round trips.  One trick is to make sure that
other side does not have to wonder "what do you need to know it
for?" by telling them upfront why you want to know.  Then they can
guess better what kind of information to what detail you need, in
order to move forward.  If they agree where you want to go with the
information is a good thing, it may even give them an incentive to
give as high quality information as they can as quickly as they can
to you.

Having said all that.

If you re-read the sentence before that sample command line, it
would be clear that the answer is "yes, as that sentence said, it is
building and installing Git".  Especially "when calling the
following in Git's own repository".

But Git in that message is merely an example.  Any piece of software
that wants to run "git" as part of the "make install" procedure
(e.g. to compute the version number, it may run "git describe", when
it knows the build is being run in a Git managed repository, instead
of being in a tarball extract) is affected.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo
  2022-04-30  6:17           ` Bagas Sanjaya
  2022-04-30  6:39             ` Junio C Hamano
@ 2022-04-30 14:15             ` Carlo Marcelo Arenas Belón
  1 sibling, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-04-30 14:15 UTC (permalink / raw)
  To: Bagas Sanjaya; +Cc: git

On Sat, Apr 30, 2022 at 01:17:46PM +0700, Bagas Sanjaya wrote:
> On Thu, Apr 28, 2022 at 03:58:51AM -0700, Carlo Marcelo Arenas Belón wrote:
> > In a previous patch, the behavior of git was changed so it will be able
> > to find the "effective uid" that is required when git was invoked with
> > sudo to root, for example the internal calls made to git when calling
> > the following in git's own repository:
> > 
> >   $ sudo make install
> >
> 
> That is building Git, right?

Technically, installing it.  The full report is more informative of what the
specific impact is, which I obviously forgot to link to:

  https://lore.kernel.org/git/20220412180510.GA2173@szeder.dev/

Another example of it (which started this whole regression fix) is available
in:

  https://lore.kernel.org/git/4ef9287b-6260-9538-7c89-cffb611520ee@maurel.de/

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v3 0/3] fix `sudo make install` regression in maint
  2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
                           ` (2 preceding siblings ...)
  2022-04-28 10:58         ` [PATCH v2 3/3] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
@ 2022-05-02 18:39         ` Carlo Marcelo Arenas Belón
  2022-05-02 18:39           ` [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
                             ` (2 more replies)
  2022-05-03  6:54         ` [PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  4 siblings, 3 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-02 18:39 UTC (permalink / raw)
  To: git; +Cc: gitster, bagasdotme, phillip.wood123, Carlo Marcelo Arenas Belón

A more involved refactoring, but mainly for the benefit of long term
maintanability, by making the code and documentation changes together
and splitting the tests into pre/post sections, which should hopefully
also make all deficiences of the approach taken clear but still be
useful enough to support the current usecases and allow for future
extensions.

Carlo Marcelo Arenas Belón (3):
  t: document regression git safe.directory when using sudo
  git-compat-util: avoid failing dir ownership checks if running
    privileged
  t0034: enhance framework to allow testing more commands under sudo

 Documentation/config/safe.txt  |   9 +++
 git-compat-util.h              |  40 ++++++++++++-
 t/lib-sudo.sh                  |  31 ++++++++++
 t/t0034-root-safe-directory.sh | 100 +++++++++++++++++++++++++++++++++
 4 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 t/lib-sudo.sh
 create mode 100755 t/t0034-root-safe-directory.sh

A range-diff from the previous version (in case someone finds it
useful) shown below:

-:  ----------- > 1:  51d0d485b5f t: document regression git safe.directory when using sudo
1:  b0436d4a50a ! 2:  4928ad698e2 git-compat-util: avoid failing dir ownership checks if running privileged
    @@ Commit message
         those tools create to keep track of the original user id, and do the
         ownership check using that instead.
     
    -    This assumes the environment the user is running with after going
    -    privileged can't be tampered with, and also does the check only for
    -    root to keep the most common case less complicated, but as a side effect
    -    will miss cases where sudo (or an equivalent) was used to change to
    -    another unprivileged user or where the equivalent tool used to raise
    +    This assumes the environment the user is running on after going
    +    privileged can't be tampered with, and also adds code to restrict that
    +    the new behavior only applies if running as root, therefore keeping the
    +    most common case, which runs unprivileged, from changing, but because of
    +    that, it will miss cases where sudo (or an equivalent) was used to change
    +    to another unprivileged user or where the equivalent tool used to raise
         privileges didn't track the original id in a sudo compatible way.
     
         Reported-by: Guy Maurel <guy.j@maurel.de>
    @@ Commit message
         Helped-by: Phillip Wood <phillip.wood123@gmail.com>
         Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
         Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>
    +
    + ## Documentation/config/safe.txt ##
    +@@ Documentation/config/safe.txt: 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.
    +++
    ++When git tries to check for ownership of git repositories, it will
    ++obviously do so with the uid of the user that is running git itself,
    ++but if git is running as root, it will check first if it might have
    ++been started through `sudo`, and if that is the case, will instead
    ++use the uid of the user that did so.
    ++If that is not what you would prefer and want git to only trust
    ++repositories that are owned by root instead, then you should remove
    ++the `SUDO_UID` variable from root's environment.
     
      ## git-compat-util.h ##
     @@ git-compat-util.h: static inline int git_offset_1st_component(const char *path)
    @@ git-compat-util.h: static inline int git_offset_1st_component(const char *path)
      }
      
      #define is_path_owned_by_current_user is_path_owned_by_current_uid
    +
    + ## t/t0034-root-safe-directory.sh ##
    +@@ t/t0034-root-safe-directory.sh: test_expect_success SUDO 'setup' '
    + 	)
    + '
    + 
    +-test_expect_failure SUDO 'sudo git status as original owner' '
    ++test_expect_success SUDO 'sudo git status as original owner' '
    + 	(
    + 		cd root/r &&
    + 		git status &&
2:  d05e886d394 < -:  ----------- Documentation: explain how safe.directory works when running under sudo
3:  10ec03d71e4 < -:  ----------- t: add tests for safe.directory when running with sudo
-:  ----------- > 3:  98aae872efd t0034: enhance framework to allow testing more commands under sudo

-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-02 18:39         ` [RFC PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
@ 2022-05-02 18:39           ` Carlo Marcelo Arenas Belón
  2022-05-02 21:35             ` Junio C Hamano
  2022-05-02 18:39           ` [RFC PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
  2022-05-02 18:39           ` [RFC PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
  2 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-02 18:39 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123,
	Carlo Marcelo Arenas Belón, SZEDER Gábor

Originally reported after release of v2.35.2 (and other maint branches)
for CVE-2022-24765 and blocking otherwise harmless commands that were
done using sudo in a repository that was owned by the user.

Add a new test script with very basic support to allow running git
commands through sudo, so a reproduction could be implemented and that
uses only `git status` as a proxy of the issue reported.

Note that because of the way sudo interacts with the system, a much
more complete integration with the test framework will require a lot
more work and that was therefore intentionally punted for now.

The current implementation requires the execution of a special cleanup
function which should always be kept as the last "test" or otherwise
the standard cleanup functions will fail because they can't remove
the root owned directories that are used.  This also means that if
failures are found while running the specifics of the failure might
not be kept for further debugging and if the test was interrupted, it
will be necessary to clean the working directory manually before
restarting by running:

  $ sudo rm -rf trash\ directory.t0034-root-safe-directory/

The test file also uses at least one initial "setup" test that creates
a parallel execution directory, while ignoring the repository created
by the test framework, and special care should be taken when invoking
commands through sudo, since the environment is otherwise independent
from what the test framework expects.  Indeed `git status` was used
as a proxy because it doesn't even require commits in the repository
to work.

A new SUDO prerequisite is provided that does some sanity checking
to make sure the sudo command that will be used allows for passwordless
execution as root and doesn't mess with git execution paths, but
otherwise additional work will be required to ensure additional
commands behave as expected and that will be addressed in a later patch.

Most of those characteristics make this test mostly suitable only for
CI, but it could be executed locally if special care is taken to provide
for some of them in the local configuration and maybe making use of the
sudo credential cache by first invoking sudo, entering your password if
needed, and then invoking the test by doing:

  $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh

Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/t0034-root-safe-directory.sh | 42 ++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 00000000000..bec73fe3c10
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.sh
+
+# this prerequisite should be added to all the tests, it not only prevents
+# the test from failing but also warms up any authentication cache sudo
+# might need to avoid asking for a password
+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
+'
+
+test_expect_success SUDO 'setup' '
+	sudo rm -rf root &&
+	mkdir -p root/r &&
+	sudo chown root root &&
+	(
+		cd root/r &&
+		git init
+	)
+'
+
+test_expect_failure SUDO 'sudo git status as original owner' '
+	(
+		cd root/r &&
+		git status &&
+		sudo git status
+	)
+'
+
+# this MUST be always the last test
+test_expect_success SUDO 'cleanup' '
+	sudo rm -rf root
+'
+
+test_done
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-02 18:39         ` [RFC PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-02 18:39           ` [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-02 18:39           ` Carlo Marcelo Arenas Belón
  2022-05-02 18:39           ` [RFC PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
  2 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-02 18:39 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123,
	Carlo Marcelo Arenas Belón, Guy Maurel, SZEDER Gábor,
	Randall Becker, Johannes Schindelin

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that user was
root (because git was invoked through sudo or a compatible tool) and the
original uid that repository trusted for its config was no longer known,
therefore failing the following otherwise safe call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using that instead.

This assumes the environment the user is running on after going
privileged can't be tampered with, and also adds code to restrict that
the new behavior only applies if running as root, therefore keeping the
most common case, which runs unprivileged, from changing, but because of
that, it will miss cases where sudo (or an equivalent) was used to change
to another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 Documentation/config/safe.txt  |  9 ++++++++
 git-compat-util.h              | 40 +++++++++++++++++++++++++++++++++-
 t/t0034-root-safe-directory.sh |  2 +-
 3 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 6d764fe0ccf..ee558ced8c7 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -26,3 +26,12 @@ 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.
++
+When git tries to check for ownership of git repositories, it will
+obviously do so with the uid of the user that is running git itself,
+but if git is running as root, it will check first if it might have
+been started through `sudo`, and if that is the case, will instead
+use the uid of the user that did so.
+If that is not what you would prefer and want git to only trust
+repositories that are owned by root instead, then you should remove
+the `SUDO_UID` variable from root's environment.
diff --git a/git-compat-util.h b/git-compat-util.h
index 63ba89dd31d..dfdd3e4f81a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -393,12 +393,50 @@ 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
+
+/*
+ * this helper function overrides a ROOT_UID with the one provided by
+ * an environment variable, do not use unless the original user is
+ * root
+ */
+static inline void extract_id_from_env(const char *env, uid_t *id)
+{
+	const char *real_uid = getenv(env);
+
+	/* discard any empty values */
+	if (real_uid && *real_uid) {
+		char *endptr;
+		unsigned long env_id;
+		int saved_errno = errno;
+
+		errno = 0;
+		env_id = strtoul(real_uid, &endptr, 10);
+		if (!errno && !*endptr && env_id <= (uid_t)-1)
+			*id = env_id;
+
+		errno = saved_errno;
+	}
+}
+
 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/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index bec73fe3c10..67dd96b9321 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -26,7 +26,7 @@ test_expect_success SUDO 'setup' '
 	)
 '
 
-test_expect_failure SUDO 'sudo git status as original owner' '
+test_expect_success SUDO 'sudo git status as original owner' '
 	(
 		cd root/r &&
 		git status &&
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo
  2022-05-02 18:39         ` [RFC PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-02 18:39           ` [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
  2022-05-02 18:39           ` [RFC PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-02 18:39           ` Carlo Marcelo Arenas Belón
  2022-05-02 22:10             ` Junio C Hamano
  2 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-02 18:39 UTC (permalink / raw)
  To: git; +Cc: gitster, bagasdotme, phillip.wood123, Carlo Marcelo Arenas Belón

When running under sudo, the environment gets altered in significant
ways, so make sure that PATH is respected by comparing the full path
to git outside and inside sudo and disabling the tests if they don't
match.

Additionally, invent a way to inject environment variables into that
environment and create a helper function to facilitate running more
than one command under sudo, while using those variables.

Add additional negative tests as suggested by Junio and export the
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME variable that will be used when
running init in one of those.

Note that in order to be able to call `test_must_fail sudo git status`
or an equivalent test_must_fail will need to be enhanced or be able
to run under sudo, so fixing that has been punted, since the only
protection it affords is for `git status` not crashing, and that is
covered already by other tests.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/lib-sudo.sh                  | 31 ++++++++++++++++++
 t/t0034-root-safe-directory.sh | 58 ++++++++++++++++++++++++++++++++++
 2 files changed, 89 insertions(+)
 create mode 100644 t/lib-sudo.sh

diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
new file mode 100644
index 00000000000..60046927f3b
--- /dev/null
+++ b/t/lib-sudo.sh
@@ -0,0 +1,31 @@
+# Helpers for running git commands under sudo.
+
+# Runs a scriplet passed through stdin under sudo.
+run_with_sudo () {
+	local ret
+	local SH=${1-"$TEST_SHELL_PATH"}
+	local RUN="$HOME/$$.sh"
+	{
+		echo "#!$SH"
+		echo "set -e"
+		echo ". \"$HOME/env\""
+		cat
+	} >"$RUN" &&
+	chmod +x "$RUN" &&
+	sudo "$SH" -c "\"$RUN\""
+	ret=$?
+	rm -f "$RUN"
+	return $ret
+}
+
+# Makes all variables passed as parameters available to the scriplet that
+# run under sudo with run_with_sudo
+export_to_sudo () {
+	while test -n "$1"
+	do
+		local v
+		eval v="\$$1"
+		echo "$1=$v" >>"$HOME/env"
+		shift
+	done
+}
diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index 67dd96b9321..0f79648a2fb 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -3,6 +3,19 @@
 test_description='verify safe.directory checks while running as root'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-sudo.sh
+
+if [ "$IKNOWWHATIAMDOING" != "YES" ]
+then
+	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
+	test_done
+fi
+
+if ! test_have_prereq NOT_ROOT
+then
+	skip_all="No, you don't; these tests can't run as root"
+	test_done
+fi
 
 # this prerequisite should be added to all the tests, it not only prevents
 # the test from failing but also warms up any authentication cache sudo
@@ -19,6 +32,7 @@ test_lazy_prereq SUDO '
 test_expect_success SUDO 'setup' '
 	sudo rm -rf root &&
 	mkdir -p root/r &&
+	export_to_sudo GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
 	sudo chown root root &&
 	(
 		cd root/r &&
@@ -34,6 +48,50 @@ test_expect_success SUDO 'sudo git status as original owner' '
 	)
 '
 
+test_expect_success SUDO 'setup root owned repository' '
+	sudo mkdir -p root/p &&
+	run_with_sudo <<-END
+		git init root/p
+	END
+'
+
+test_expect_success SUDO 'cannot access if owned by root' '
+	(
+		cd root/p &&
+		test_must_fail git status
+	)
+'
+
+test_expect_success SUDO 'cannot access with sudo' '
+	(
+		# TODO: test_must_fail needs additional functionality
+		# 6a67c759489 blocks its use with sudo
+		cd root/p &&
+		! sudo git status
+	)
+'
+
+test_expect_success SUDO 'can access using a workaround' '
+	# provide explicit GIT_DIR
+	(
+		cd root/p &&
+		run_with_sudo <<-END
+			GIT_DIR=.git
+			GIT_WORK_TREE=.
+			export GIT_DIR GIT_WORK_TREE
+			git status
+		END
+	) &&
+	# discard 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
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-02 18:39           ` [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-02 21:35             ` Junio C Hamano
  2022-05-02 23:07               ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-02 21:35 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, phillip.wood123, SZEDER Gábor

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> +test_lazy_prereq SUDO '
> +	sudo -n id -u >u &&
> +	id -u root >r &&
> +	test_cmp u r &&

OK.  We make sure we can become root with "sudo".

> +	command -v git >u &&
> +	sudo command -v git >r &&
> +	test_cmp u r

While this is not as thorough as the "make sure we see the
environment intact" that I alluded to during the previous review, it
at least makes sure PATH (which our test framework tweaks) is
propagated down to "sudo" environment intact, which should be good
enough to run just "init" and "status".  Keeping things simple is
good.

We may have to exclude this from tests that require specialized
environment like valgrind tests, but that is not of immediate
concern.

Will queue.

> +'
> +
> +test_expect_success SUDO 'setup' '
> +	sudo rm -rf root &&
> +	mkdir -p root/r &&
> +	sudo chown root root &&
> +	(
> +		cd root/r &&
> +		git init
> +	)
> +'
> +
> +test_expect_failure SUDO 'sudo git status as original owner' '
> +	(
> +		cd root/r &&
> +		git status &&
> +		sudo git status
> +	)
> +'
> +
> +# this MUST be always the last test
> +test_expect_success SUDO 'cleanup' '
> +	sudo rm -rf root
> +'
> +
> +test_done

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo
  2022-05-02 18:39           ` [RFC PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
@ 2022-05-02 22:10             ` Junio C Hamano
  2022-05-03  0:00               ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-02 22:10 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón; +Cc: git, bagasdotme, phillip.wood123

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> +# Runs a scriplet passed through stdin under sudo.
> +run_with_sudo () {
> +	local ret
> +	local SH=${1-"$TEST_SHELL_PATH"}
> +	local RUN="$HOME/$$.sh"
> +	{
> +		echo "#!$SH"
> +		echo "set -e"
> +		echo ". \"$HOME/env\""
> +		cat
> +	} >"$RUN" &&
> +	chmod +x "$RUN" &&
> +	sudo "$SH" -c "\"$RUN\""
> +	ret=$?
> +	rm -f "$RUN"
> +	return $ret
> +}

I wonder if write_script can be used for better readability.  It is
especially true as I am going to suggest ripping out $HOME/env stuff
that is not absolutely needed (and its support with this patch looks
inadequate when we do need one).

	local RUN=$HOME/$$.sh &&
	write_script "$RUN" "$TEST_SHELL_PATH" &&
	sudo "$RUN"

or something?

> +# Makes all variables passed as parameters available to the scriplet that
> +# run under sudo with run_with_sudo
> +export_to_sudo () {
> +	while test -n "$1"
> +	do
> +		local v
> +		eval v="\$$1"
> +		echo "$1=$v" >>"$HOME/env"
> +		shift
> +	done
> +}

Two potential issues:

 - This forces the caller to list _all_ the relevant environment
   variables that would ever matter, which would not be feasible and
   would not be maintainable.  For example, by forgetting to export
   GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS, "git" commands run in the
   sudo tests change their behaviour.  Whoever writing a new test
   need to see what obscure GIT_TEST_* thing may affect the test
   they want to write, and whoever enhancing the test framework to
   add new GIT_TEST_* knob need to pay attention to the users of
   export_to_sudo if their new knob need to be exported.

 - I think the assignment to $v under eval is correct, but I am not
   sure the string accumulated in the $HOME/env file is safe to
   eval.  We can pass TEST_DIRECTORY via this mechanism, where the
   value deliberately has a whitespace in it, but if the leading
   path to our source directory had a single-quote in it, it
   probably would not work well.  Of course, any variable that has
   LF in its value would not work without proper quoting.

I think both are not impossible but are hard to do right.  Because I
do not see anything that absolutely needs the $HOME/env mechanism to
work in the rest of the tests in this patch, I am inclined to say
that I'd prefer keeping things simple and only make sure we use the
right $SHELL to run our script (which write_script may help).

> diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
> index 67dd96b9321..0f79648a2fb 100755
> --- a/t/t0034-root-safe-directory.sh
> +++ b/t/t0034-root-safe-directory.sh
> @@ -3,6 +3,19 @@
>  test_description='verify safe.directory checks while running as root'
>  
>  . ./test-lib.sh
> +. "$TEST_DIRECTORY"/lib-sudo.sh
> +
> +if [ "$IKNOWWHATIAMDOING" != "YES" ]
> +then
> +	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
> +	test_done
> +fi
> +
> +if ! test_have_prereq NOT_ROOT
> +then
> +	skip_all="No, you don't; these tests can't run as root"
> +	test_done
> +fi

OK.

> @@ -19,6 +32,7 @@ test_lazy_prereq SUDO '
>  test_expect_success SUDO 'setup' '
>  	sudo rm -rf root &&
>  	mkdir -p root/r &&
> +	export_to_sudo GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
>  	sudo chown root root &&
>  	(
>  		cd root/r &&

I know I brought up the "git init" during the previous review, but
as long as the test does not depend on the GIT_TEST_* knob
(i.e. either we say "git init --initial-branch" explicitly, or we do
not rely on the initial branch having a certain name), we do not
have to worry.  We do not check what branch we are on after we do
this test, we do not check what branch "git status" reports that we
are on in later tests, we obviously do not care between main and
master in this test script.

I am tempted to suggest dropping the whole $HOME/env business.

> @@ -34,6 +48,50 @@ test_expect_success SUDO 'sudo git status as original owner' '
>  	)
>  '
>  
> +test_expect_success SUDO 'setup root owned repository' '
> +	sudo mkdir -p root/p &&
> +	run_with_sudo <<-END
> +		git init root/p
> +	END
> +'

OK.

> +test_expect_success SUDO 'cannot access if owned by root' '
> +	(
> +		cd root/p &&
> +		test_must_fail git status
> +	)
> +'

OK, but strictly speaking, we do not need the SUDO prerequisite for
this one.  It still need it for the test directories prepared in
previous steps anyway, so perhaps we want one check upfront, just
like we do for NOT_ROOT?

	if ! test_have_prereq SUDO
	then
		skip_all="You do not seem to have a working 'sudo'"
		test_done
	fi

> +test_expect_success SUDO 'cannot access with sudo' '
> +	(
> +		# TODO: test_must_fail needs additional functionality
> +		# 6a67c759489 blocks its use with sudo
> +		cd root/p &&
> +		! sudo git status
> +	)
> +'

OK.  So we cannot by default access root-owned repository by
default, which is OK.  I wonder what happens if we did "sudo sudo
git status".  Perhaps the inner sudo will notice that SUDO_UID is
set in its environment and does not update it to 0?

	... goes and checks ...
	$ sudo sudo sh -c 'echo $SUDO_UID; whoami'
	0
	root

So that gives us another workaround, I guess, which might be even
simpler.

> +test_expect_success SUDO 'can access using a workaround' '
> +	# provide explicit GIT_DIR
> +	(
> +		cd root/p &&
> +		run_with_sudo <<-END
> +			GIT_DIR=.git
> +			GIT_WORK_TREE=.
> +			export GIT_DIR GIT_WORK_TREE
> +			git status
> +		END
> +	) &&
> +	# discard SUDO_UID
> +	(
> +		cd root/p &&
> +		run_with_sudo <<-END
> +			unset SUDO_UID
> +			git status
> +		END
> +	)

	# double sudo
	(
		cd root/p &&
		sudo sudo git status
	)

I do not know if it is worth adding this third workaround.

> +'
> +
>  # this MUST be always the last test
>  test_expect_success SUDO 'cleanup' '
>  	sudo rm -rf root

Looking much better otherwise.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-02 21:35             ` Junio C Hamano
@ 2022-05-02 23:07               ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-02 23:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, bagasdotme, phillip.wood123, SZEDER Gábor

On Mon, May 2, 2022 at 2:35 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
>
> > +     command -v git >u &&
> > +     sudo command -v git >r &&
> > +     test_cmp u r
>
> While this is not as thorough as the "make sure we see the
> environment intact" that I alluded to during the previous review, it
> at least makes sure PATH (which our test framework tweaks) is
> propagated down to "sudo" environment intact, which should be good
> enough to run just "init" and "status".  Keeping things simple is
> good.

Sorry, I should have mentioned that explicitly in my response.

That is not possible.  sudo by design will try to minimize the
environment that root is running on, so the only way to make sure git
still works as expected there, is to add to that environment
everything else we might need.

That is why I have to invent that ugly looking "env" file and the
function to import variables with it, and I was assuming that for it
to be useful in the end we might need to maybe import everything
"GIT*" too, but obviously I didn't want to do that now, and that is
why I only did the branch name (more as an example, than as something
that was strictly needed) since you mentioned that in your review and
I could see how it was related to `git init` being added to the tests
in the next commit.

Guess I botched it in my refactoring anyway, since it also makes sense
for it to be added in the next commit together with `git init`instead
of here.

> may have to exclude this from tests that require specialized
> environment like valgrind tests, but that is not of immediate
> concern.

I didn't test valgrind, but I would assume it is probably broken now,
as well as anything else that relies on extra environmental things.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo
  2022-05-02 22:10             ` Junio C Hamano
@ 2022-05-03  0:00               ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-03  0:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, bagasdotme, phillip.wood123

On Mon, May 2, 2022 at 3:10 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
>
> > +# Runs a scriplet passed through stdin under sudo.
> > +run_with_sudo () {
> > +     local ret
> > +     local SH=${1-"$TEST_SHELL_PATH"}
> > +     local RUN="$HOME/$$.sh"
> > +     {
> > +             echo "#!$SH"
> > +             echo "set -e"
> > +             echo ". \"$HOME/env\""
> > +             cat
> > +     } >"$RUN" &&
> > +     chmod +x "$RUN" &&
> > +     sudo "$SH" -c "\"$RUN\""
> > +     ret=$?
> > +     rm -f "$RUN"
> > +     return $ret
> > +}
>
> I wonder if write_script can be used for better readability.  It is
> especially true as I am going to suggest ripping out $HOME/env stuff
> that is not absolutely needed (and its support with this patch looks
> inadequate when we do need one).
>
>         local RUN=$HOME/$$.sh &&
>         write_script "$RUN" "$TEST_SHELL_PATH" &&
>         sudo "$RUN"
>
> or something?

correct, I tried to use write_script initially, but I thought that
since I needed to inject code at the beginning for env, I could also
inject "set -e" and make sure that any scriplet used would work (and
more importantly fail when not) even without the "&&".

so the scriptlets themselves would be more readable.

My function is just a slightly modified version of wrte_script for
that reason, as making write_script do that seemed more complicated
than what I ended up doing.

> +# Makes all variables passed as parameters available to the scriplet that
> > +# run under sudo with run_with_sudo
> > +export_to_sudo () {
> > +     while test -n "$1"
> > +     do
> > +             local v
> > +             eval v="\$$1"
> > +             echo "$1=$v" >>"$HOME/env"
> > +             shift
> > +     done
> > +}
>
> Two potential issues:
>
>  - This forces the caller to list _all_ the relevant environment
>    variables that would ever matter, which would not be feasible and
>    would not be maintainable.  For example, by forgetting to export
>    GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS, "git" commands run in the
>    sudo tests change their behaviour.  Whoever writing a new test
>    need to see what obscure GIT_TEST_* thing may affect the test
>    they want to write, and whoever enhancing the test framework to
>    add new GIT_TEST_* knob need to pay attention to the users of
>    export_to_sudo if their new knob need to be exported.
>
>  - I think the assignment to $v under eval is correct, but I am not
>    sure the string accumulated in the $HOME/env file is safe to
>    eval.  We can pass TEST_DIRECTORY via this mechanism, where the
>    value deliberately has a whitespace in it, but if the leading
>    path to our source directory had a single-quote in it, it
>    probably would not work well.  Of course, any variable that has
>    LF in its value would not work without proper quoting.
>
> I think both are not impossible but are hard to do right.  Because I
> do not see anything that absolutely needs the $HOME/env mechanism to
> work in the rest of the tests in this patch, I am inclined to say
> that I'd prefer keeping things simple and only make sure we use the
> right $SHELL to run our script (which write_script may help).

makes sense, would prepare a formal v3 with those changes and that
should be safe to queue.

> > +test_expect_success SUDO 'cannot access if owned by root' '
> > +     (
> > +             cd root/p &&
> > +             test_must_fail git status
> > +     )
> > +'
>
> OK, but strictly speaking, we do not need the SUDO prerequisite for
> this one.  It still need it for the test directories prepared in
> previous steps anyway, so perhaps we want one check upfront, just
> like we do for NOT_ROOT?
>
>         if ! test_have_prereq SUDO
>         then
>                 skip_all="You do not seem to have a working 'sudo'"
>                 test_done
>         fi

my only concern doing that would be that then could confuse people
adding new tests later that might also not need SUDO and that would be
skipped because they were added at the end, but I agree it is overall
cleaner so would also include in the reroll

> > +test_expect_success SUDO 'cannot access with sudo' '
> > +     (
> > +             # TODO: test_must_fail needs additional functionality
> > +             # 6a67c759489 blocks its use with sudo
> > +             cd root/p &&
> > +             ! sudo git status
> > +     )
> > +'
>
> OK.  So we cannot by default access root-owned repository by
> default, which is OK.  I wonder what happens if we did "sudo sudo
> git status".  Perhaps the inner sudo will notice that SUDO_UID is
> set in its environment and does not update it to 0?

funny enough sudo itself doesn't honour SUDO_UID.

>       ... goes and checks ...
>         $ sudo sudo sh -c 'echo $SUDO_UID; whoami'
>         0
>         root
>
> So that gives us another workaround, I guess, which might be even
> simpler.

correct.

> > +test_expect_success SUDO 'can access using a workaround' '
> > +     # provide explicit GIT_DIR
> > +     (
> > +             cd root/p &&
> > +             run_with_sudo <<-END
> > +                     GIT_DIR=.git
> > +                     GIT_WORK_TREE=.
> > +                     export GIT_DIR GIT_WORK_TREE
> > +                     git status
> > +             END
> > +     ) &&
> > +     # discard SUDO_UID
> > +     (
> > +             cd root/p &&
> > +             run_with_sudo <<-END
> > +                     unset SUDO_UID
> > +                     git status
> > +             END
> > +     )
>
>         # double sudo
>         (
>                 cd root/p &&
>                 sudo sudo git status
>         )
>
> I do not know if it is worth adding this third workaround.

I think it is, as it helps document them, and makes sure they remain
viable, but still think that the one added to the documentation is
"better", because it is direct and not an implementation detail

removing SUDO_UID from your environment "means" you want to be
identified as root by git from then on, and that the fact that you
might have become root through sudo is no longer relevant.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v3 0/3] fix `sudo make install` regression in maint
  2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
                           ` (3 preceding siblings ...)
  2022-05-02 18:39         ` [RFC PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
@ 2022-05-03  6:54         ` Carlo Marcelo Arenas Belón
  2022-05-03  6:54           ` [PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
                             ` (3 more replies)
  4 siblings, 4 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-03  6:54 UTC (permalink / raw)
  To: git; +Cc: gitster, bagasdotme, phillip.wood123, Carlo Marcelo Arenas Belón

Changes since RFC v3:
* Cleaner split of tests so patch1 will not break if called under sudo
  and will be less likely to break any CI by restricting its runs to
  IKNOWWHATIAMDOING=YES
* Simplified patch3 with all requested enhancements, except that it is
  still keeping the (currently unused) possibility to override the shell
  used in run_with_sudo with an optional first parameter.

Carlo Marcelo Arenas Belón (3):
  t: document regression git safe.directory when using sudo
  git-compat-util: avoid failing dir ownership checks if running
    privileged
  t0034: enhance framework to allow testing more commands under sudo

 Documentation/config/safe.txt  |   9 +++
 git-compat-util.h              |  40 +++++++++++-
 t/lib-sudo.sh                  |  13 ++++
 t/t0034-root-safe-directory.sh | 115 +++++++++++++++++++++++++++++++++
 4 files changed, 176 insertions(+), 1 deletion(-)
 create mode 100644 t/lib-sudo.sh
 create mode 100755 t/t0034-root-safe-directory.sh

A range-diff against RFC v3 shown below to easy review:

1:  51d0d485b5f ! 1:  27192634476 t: document regression git safe.directory when using sudo
    @@ t/t0034-root-safe-directory.sh (new)
     +
     +. ./test-lib.sh
     +
    ++if [ "$IKNOWWHATIAMDOING" != "YES" ]
    ++then
    ++	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
    ++	test_done
    ++fi
    ++
     +# this prerequisite should be added to all the tests, it not only prevents
     +# the test from failing but also warms up any authentication cache sudo
     +# might need to avoid asking for a password
    @@ t/t0034-root-safe-directory.sh (new)
     +	)
     +'
     +
    -+# this MUST be always the last test
    ++# this MUST be always the last test, if used more than once, the next
    ++# test should do a full setup again.
     +test_expect_success SUDO 'cleanup' '
     +	sudo rm -rf root
     +'
2:  4928ad698e2 = 2:  58b4671b9ef git-compat-util: avoid failing dir ownership checks if running privileged
3:  98aae872efd < -:  ----------- t0034: enhance framework to allow testing more commands under sudo
-:  ----------- > 3:  7c029c66666 t0034: enhance framework to allow testing more commands under sudo

Albeit not ideal, if needef patches 1 and 2 could be fastracked to close the
regression while patch 3 goes through further iterations.

Carlo
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-03  6:54         ` [PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
@ 2022-05-03  6:54           ` Carlo Marcelo Arenas Belón
  2022-05-03 14:03             ` Phillip Wood
  2022-05-05 13:44             ` Johannes Schindelin
  2022-05-03  6:54           ` [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
                             ` (2 subsequent siblings)
  3 siblings, 2 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-03  6:54 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123,
	Carlo Marcelo Arenas Belón, SZEDER Gábor

Originally reported after release of v2.35.2 (and other maint branches)
for CVE-2022-24765 and blocking otherwise harmless commands that were
done using sudo in a repository that was owned by the user.

Add a new test script with very basic support to allow running git
commands through sudo, so a reproduction could be implemented and that
uses only `git status` as a proxy of the issue reported.

Note that because of the way sudo interacts with the system, a much
more complete integration with the test framework will require a lot
more work and that was therefore intentionally punted for now.

The current implementation requires the execution of a special cleanup
function which should always be kept as the last "test" or otherwise
the standard cleanup functions will fail because they can't remove
the root owned directories that are used.  This also means that if
failures are found while running the specifics of the failure might
not be kept for further debugging and if the test was interrupted, it
will be necessary to clean the working directory manually before
restarting by running:

  $ sudo rm -rf trash\ directory.t0034-root-safe-directory/

The test file also uses at least one initial "setup" test that creates
a parallel execution directory, while ignoring the repository created
by the test framework, and special care should be taken when invoking
commands through sudo, since the environment is otherwise independent
from what the test framework expects.  Indeed `git status` was used
as a proxy because it doesn't even require commits in the repository
to work.

A new SUDO prerequisite is provided that does some sanity checking
to make sure the sudo command that will be used allows for passwordless
execution as root and doesn't mess with git execution paths, but
otherwise additional work will be required to ensure additional
commands behave as expected and that will be addressed in a later patch.

Most of those characteristics make this test mostly suitable only for
CI, but it could be executed locally if special care is taken to provide
for some of them in the local configuration and maybe making use of the
sudo credential cache by first invoking sudo, entering your password if
needed, and then invoking the test by doing:

  $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh

Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/t0034-root-safe-directory.sh | 49 ++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 00000000000..6dac7a05cfd
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.sh
+
+if [ "$IKNOWWHATIAMDOING" != "YES" ]
+then
+	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
+	test_done
+fi
+
+# this prerequisite should be added to all the tests, it not only prevents
+# the test from failing but also warms up any authentication cache sudo
+# might need to avoid asking for a password
+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
+'
+
+test_expect_success SUDO 'setup' '
+	sudo rm -rf root &&
+	mkdir -p root/r &&
+	sudo chown root root &&
+	(
+		cd root/r &&
+		git init
+	)
+'
+
+test_expect_failure SUDO 'sudo git status as original owner' '
+	(
+		cd root/r &&
+		git status &&
+		sudo git status
+	)
+'
+
+# this MUST be always the last test, if used more than once, the next
+# test should do a full setup again.
+test_expect_success SUDO 'cleanup' '
+	sudo rm -rf root
+'
+
+test_done
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-03  6:54         ` [PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-03  6:54           ` [PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-03  6:54           ` Carlo Marcelo Arenas Belón
  2022-05-05 14:01             ` Johannes Schindelin
  2022-05-03  6:54           ` [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
  2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-03  6:54 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123,
	Carlo Marcelo Arenas Belón, Guy Maurel, SZEDER Gábor,
	Randall Becker, Johannes Schindelin

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that user was
root (because git was invoked through sudo or a compatible tool) and the
original uid that repository trusted for its config was no longer known,
therefore failing the following otherwise safe call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using that instead.

This assumes the environment the user is running on after going
privileged can't be tampered with, and also adds code to restrict that
the new behavior only applies if running as root, therefore keeping the
most common case, which runs unprivileged, from changing, but because of
that, it will miss cases where sudo (or an equivalent) was used to change
to another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 Documentation/config/safe.txt  |  9 ++++++++
 git-compat-util.h              | 40 +++++++++++++++++++++++++++++++++-
 t/t0034-root-safe-directory.sh |  2 +-
 3 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 6d764fe0ccf..ee558ced8c7 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -26,3 +26,12 @@ 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.
++
+When git tries to check for ownership of git repositories, it will
+obviously do so with the uid of the user that is running git itself,
+but if git is running as root, it will check first if it might have
+been started through `sudo`, and if that is the case, will instead
+use the uid of the user that did so.
+If that is not what you would prefer and want git to only trust
+repositories that are owned by root instead, then you should remove
+the `SUDO_UID` variable from root's environment.
diff --git a/git-compat-util.h b/git-compat-util.h
index 63ba89dd31d..dfdd3e4f81a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -393,12 +393,50 @@ 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
+
+/*
+ * this helper function overrides a ROOT_UID with the one provided by
+ * an environment variable, do not use unless the original user is
+ * root
+ */
+static inline void extract_id_from_env(const char *env, uid_t *id)
+{
+	const char *real_uid = getenv(env);
+
+	/* discard any empty values */
+	if (real_uid && *real_uid) {
+		char *endptr;
+		unsigned long env_id;
+		int saved_errno = errno;
+
+		errno = 0;
+		env_id = strtoul(real_uid, &endptr, 10);
+		if (!errno && !*endptr && env_id <= (uid_t)-1)
+			*id = env_id;
+
+		errno = saved_errno;
+	}
+}
+
 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/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index 6dac7a05cfd..dd659aed4e1 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -32,7 +32,7 @@ test_expect_success SUDO 'setup' '
 	)
 '
 
-test_expect_failure SUDO 'sudo git status as original owner' '
+test_expect_success SUDO 'sudo git status as original owner' '
 	(
 		cd root/r &&
 		git status &&
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo
  2022-05-03  6:54         ` [PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-03  6:54           ` [PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
  2022-05-03  6:54           ` [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-03  6:54           ` Carlo Marcelo Arenas Belón
  2022-05-03 14:12             ` Phillip Wood
  2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-03  6:54 UTC (permalink / raw)
  To: git; +Cc: gitster, bagasdotme, phillip.wood123, Carlo Marcelo Arenas Belón

Add a support library that provides one function that can be used
to run a "scriplet" of commands through sudo and that has an
optional parameter (currently unused) to indicate which shell to
use to do so.

Add additional negative tests as suggested by Junio and that use
new workspace that is owned by root.

Note that in order to be able to call `test_must_fail sudo git status`
or an equivalent, test_must_fail will need to be enhanced or be able
to run under sudo, so fixing that has been punted, since the only
protection it affords is for `git status` not crashing, and that is
covered already by other tests.

Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/lib-sudo.sh                  | 13 +++++++
 t/t0034-root-safe-directory.sh | 70 +++++++++++++++++++++++++++++++++-
 2 files changed, 81 insertions(+), 2 deletions(-)
 create mode 100644 t/lib-sudo.sh

diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
new file mode 100644
index 00000000000..9ebb30fc82b
--- /dev/null
+++ b/t/lib-sudo.sh
@@ -0,0 +1,13 @@
+# Helpers for running git commands under sudo.
+
+# Runs a scriplet passed through stdin under sudo.
+run_with_sudo () {
+	local ret
+	local SH=${1-"$TEST_SHELL_PATH"}
+	local RUN="$HOME/$$.sh"
+	write_script "$RUN" "$SH"
+	sudo "$SH" -c "\"$RUN\""
+	ret=$?
+	rm -f "$RUN"
+	return $ret
+}
diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index dd659aed4e1..a68e1d7602b 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -3,6 +3,7 @@
 test_description='verify safe.directory checks while running as root'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-sudo.sh
 
 if [ "$IKNOWWHATIAMDOING" != "YES" ]
 then
@@ -10,6 +11,12 @@ then
 	test_done
 fi
 
+if ! test_have_prereq NOT_ROOT
+then
+	skip_all="No, you don't; these tests can't run as root"
+	test_done
+fi
+
 # this prerequisite should be added to all the tests, it not only prevents
 # the test from failing but also warms up any authentication cache sudo
 # might need to avoid asking for a password
@@ -40,8 +47,67 @@ test_expect_success SUDO 'sudo git status as original owner' '
 	)
 '
 
-# this MUST be always the last test, if used more than once, the next
-# test should do a full setup again.
+# this destroys the test environment used above
+test_expect_success SUDO 'cleanup regression' '
+	sudo rm -rf root
+'
+
+if ! test_have_prereq SUDO
+then
+	skip_all="You need sudo to root for all remaining tests"
+	test_done
+fi
+
+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 SUDO 'cannot access with sudo' '
+	(
+		# TODO: test_must_fail needs additional functionality
+		# 6a67c759489 blocks its use with sudo
+		cd root/p &&
+		! sudo git status
+	)
+'
+
+test_expect_success SUDO 'can access using a workaround' '
+	# run sudo twice
+	(
+		cd root/p &&
+		run_with_sudo <<-END
+			sudo git status
+		END
+	) &&
+	# provide explicit GIT_DIR
+	(
+		cd root/p &&
+		run_with_sudo <<-END
+			GIT_DIR=.git &&
+			GIT_WORK_TREE=. &&
+			export GIT_DIR GIT_WORK_TREE &&
+			git status
+		END
+	) &&
+	# discard 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
 '
-- 
2.36.0.352.g0cd7feaf86f


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-03  6:54           ` [PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-03 14:03             ` Phillip Wood
  2022-05-03 15:56               ` Carlo Marcelo Arenas Belón
  2022-05-05 13:44             ` Johannes Schindelin
  1 sibling, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-05-03 14:03 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git
  Cc: gitster, bagasdotme, SZEDER Gábor

Hi Carlo

On 03/05/2022 07:54, Carlo Marcelo Arenas Belón wrote:
> Originally reported after release of v2.35.2 (and other maint branches)
> for CVE-2022-24765 and blocking otherwise harmless commands that were
> done using sudo in a repository that was owned by the user.
> 
> Add a new test script with very basic support to allow running git
> commands through sudo, so a reproduction could be implemented and that
> uses only `git status` as a proxy of the issue reported.
> 
> Note that because of the way sudo interacts with the system, a much
> more complete integration with the test framework will require a lot
> more work and that was therefore intentionally punted for now.
> 
> The current implementation requires the execution of a special cleanup
> function which should always be kept as the last "test" or otherwise
> the standard cleanup functions will fail because they can't remove
> the root owned directories that are used.  This also means that if
> failures are found while running the specifics of the failure might
> not be kept for further debugging and if the test was interrupted, it
> will be necessary to clean the working directory manually before
> restarting by running:
> 
>    $ sudo rm -rf trash\ directory.t0034-root-safe-directory/
> 
> The test file also uses at least one initial "setup" test that creates
> a parallel execution directory, while ignoring the repository created
> by the test framework, and special care should be taken when invoking
> commands through sudo, since the environment is otherwise independent
> from what the test framework expects.  Indeed `git status` was used
> as a proxy because it doesn't even require commits in the repository
> to work.
> 
> A new SUDO prerequisite is provided that does some sanity checking
> to make sure the sudo command that will be used allows for passwordless
> execution as root and doesn't mess with git execution paths, but
> otherwise additional work will be required to ensure additional
> commands behave as expected and that will be addressed in a later patch.
> 
> Most of those characteristics make this test mostly suitable only for
> CI, but it could be executed locally if special care is taken to provide
> for some of them in the local configuration and maybe making use of the
> sudo credential cache by first invoking sudo, entering your password if
> needed, and then invoking the test by doing:
> 
>    $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh
> 
> Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
>   t/t0034-root-safe-directory.sh | 49 ++++++++++++++++++++++++++++++++++
>   1 file changed, 49 insertions(+)
>   create mode 100755 t/t0034-root-safe-directory.sh
> 
> diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
> new file mode 100755
> index 00000000000..6dac7a05cfd
> --- /dev/null
> +++ b/t/t0034-root-safe-directory.sh
> @@ -0,0 +1,49 @@
> +#!/bin/sh
> +
> +test_description='verify safe.directory checks while running as root'
> +
> +. ./test-lib.sh
> +
> +if [ "$IKNOWWHATIAMDOING" != "YES" ]
> +then
> +	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
> +	test_done
> +fi
> +
> +# this prerequisite should be added to all the tests, it not only prevents
> +# the test from failing but also warms up any authentication cache sudo
> +# might need to avoid asking for a password

If this is required for all the tests then it would be simpler just to 
skip all the tests if it is not satisfied as you do above.

Running "sudo env" shows that it sets $HOME to /root which means that 
these tests will pick up /root/.gitconfig if it exists. Normally when 
running the tests we set $HOME to $TEST_DIRECTORY so they are run in a 
predictable environment. At least anything pointed to by core.hooksPath 
or core.fsmontior in that file is expecting to be run as root. I think 
it is worth spelling this out explicitly in the commit message 
(currently it is a bit vague about what the implications of not having 
better integration with the test framework are) and the top of the test 
file. Note that t1509 sources test-lib.sh as the root user so does not 
have this issue.

> +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
> +'
> +
> +test_expect_success SUDO 'setup' '
> +	sudo rm -rf root &&
> +	mkdir -p root/r &&
> +	sudo chown root root &&
> +	(
> +		cd root/r &&
> +		git init

Using git -C <directory> would eliminate a lot of the sub shells in this 
file

Best Wishes

Phillip

> +	)
> +'
> +
> +test_expect_failure SUDO 'sudo git status as original owner' '
> +	(
> +		cd root/r &&
> +		git status &&
> +		sudo git status
> +	)
> +'
> +
> +# this MUST be always the last test, if used more than once, the next
> +# test should do a full setup again.
> +test_expect_success SUDO 'cleanup' '
> +	sudo rm -rf root
> +'
> +
> +test_done

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo
  2022-05-03  6:54           ` [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
@ 2022-05-03 14:12             ` Phillip Wood
  2022-05-03 15:27               ` Junio C Hamano
  2022-05-06 16:54               ` Carlo Arenas
  0 siblings, 2 replies; 161+ messages in thread
From: Phillip Wood @ 2022-05-03 14:12 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git; +Cc: gitster, bagasdotme

Hi Carlo

On 03/05/2022 07:54, Carlo Marcelo Arenas Belón wrote:
> Add a support library that provides one function that can be used
> to run a "scriplet" of commands through sudo and that has an
> optional parameter (currently unused) to indicate which shell to
> use to do so.
> 
> Add additional negative tests as suggested by Junio and that use
> new workspace that is owned by root.
> 
> Note that in order to be able to call `test_must_fail sudo git status`
> or an equivalent, test_must_fail will need to be enhanced or be able
> to run under sudo, so fixing that has been punted, since the only
> protection it affords is for `git status` not crashing, and that is
> covered already by other tests.
> 
> Helped-by: Junio C Hamano <gitster@pobox.com>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
>   t/lib-sudo.sh                  | 13 +++++++
>   t/t0034-root-safe-directory.sh | 70 +++++++++++++++++++++++++++++++++-
>   2 files changed, 81 insertions(+), 2 deletions(-)
>   create mode 100644 t/lib-sudo.sh
> 
> diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
> new file mode 100644
> index 00000000000..9ebb30fc82b
> --- /dev/null
> +++ b/t/lib-sudo.sh
> @@ -0,0 +1,13 @@
> +# Helpers for running git commands under sudo.
> +
> +# Runs a scriplet passed through stdin under sudo.
> +run_with_sudo () {
> +	local ret
> +	local SH=${1-"$TEST_SHELL_PATH"}

What use do you envisage for this? It would be simpler just to use 
$TEST_SHELL_PATH directly below

> +	local RUN="$HOME/$$.sh"

Can we used a fixed name for the script? That would make things simpler 
especially debugging as one would know what file to look for. Also using 
$TEST_DIRECTORY rather than $HOME would make it clear where the file 
ends up.

> +	write_script "$RUN" "$SH"
> +	sudo "$SH" -c "\"$RUN\""

I think using write_script means we can just do 'sudo "$RUN"'

> +	ret=$?
> +	rm -f "$RUN" > +	return $ret
> +}
> diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
> index dd659aed4e1..a68e1d7602b 100755
> --- a/t/t0034-root-safe-directory.sh
> +++ b/t/t0034-root-safe-directory.sh
> @@ -3,6 +3,7 @@
>   test_description='verify safe.directory checks while running as root'
>   
>   . ./test-lib.sh
> +. "$TEST_DIRECTORY"/lib-sudo.sh
>   
>   if [ "$IKNOWWHATIAMDOING" != "YES" ]
>   then
> @@ -10,6 +11,12 @@ then
>   	test_done
>   fi
>   
> +if ! test_have_prereq NOT_ROOT
> +then
> +	skip_all="No, you don't; these tests can't run as root"

I think the message would be friendlier without the "No, you don't" and 
just said that the tests cannot be run as root.

> +	test_done
> +fi
> +
>   # this prerequisite should be added to all the tests, it not only prevents
>   # the test from failing but also warms up any authentication cache sudo
>   # might need to avoid asking for a password
> @@ -40,8 +47,67 @@ test_expect_success SUDO 'sudo git status as original owner' '
>   	)
>   '
>   
> -# this MUST be always the last test, if used more than once, the next
> -# test should do a full setup again.

Why is the comment being changed? If you want the shorter version at the 
end of this patch can't we just use that wording in patch 1?


> +# this destroys the test environment used above
> +test_expect_success SUDO 'cleanup regression' '
> +	sudo rm -rf root
> +'
> +
> +if ! test_have_prereq SUDO
> +then
> +	skip_all="You need sudo to root for all remaining tests"
> +	test_done
> +fi
> +
> +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 SUDO 'cannot access with sudo' '
> +	(
> +		# TODO: test_must_fail needs additional functionality
> +		# 6a67c759489 blocks its use with sudo
> +		cd root/p &&
> +		! sudo git status
> +	)
> +'

I think Junio suggested that this should work and showed it was simple 
to make it work. It seems funny that if sudo is started as root it does 
not work.

> +test_expect_success SUDO 'can access using a workaround' '
> +	# run sudo twice
> +	(
> +		cd root/p &&
> +		run_with_sudo <<-END
> +			sudo git status
> +		END
> +	) &&
> +	# provide explicit GIT_DIR
> +	(
> +		cd root/p &&
> +		run_with_sudo <<-END
> +			GIT_DIR=.git &&
> +			GIT_WORK_TREE=. &&
> +			export GIT_DIR GIT_WORK_TREE &&
> +			git status

I'm confused by this. Does this mean we don't do the ownership checks if 
GIT_DIR and or GIT_WORK_TREE are set in the environment?


Thanks for working on this

Best Wishes

Phillip


> +		END
> +	) &&
> +	# discard 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
>   '

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo
  2022-05-03 14:12             ` Phillip Wood
@ 2022-05-03 15:27               ` Junio C Hamano
  2022-05-06 16:54               ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-03 15:27 UTC (permalink / raw)
  To: Phillip Wood; +Cc: Carlo Marcelo Arenas Belón, git, bagasdotme

Phillip Wood <phillip.wood123@gmail.com> writes:

(I'd be brief as it is my day-off today).

>> +test_expect_success SUDO 'cannot access with sudo' '
>> +	(
>> +		# TODO: test_must_fail needs additional functionality
>> +		# 6a67c759489 blocks its use with sudo
>> +		cd root/p &&
>> +		! sudo git status
>> +	)
>> +'
>
> I think Junio suggested that this should work and showed it was simple
> to make it work. It seems funny that if sudo is started as root it
> does not work.

It does feel odd.  Any attacker who can prepare a root-owned trap do
not have to trick "sudo git" to fall there, so I personally do not
see much value in stopping this particular pattern, but since
workarounds are easy (like double sudo), I do not mind if this does
not work *IF* there is a good reason to stop this.

>> +test_expect_success SUDO 'can access using a workaround' '
>> +	# run sudo twice
>> +	(
>> +		cd root/p &&
>> +		run_with_sudo <<-END
>> +			sudo git status
>> +		END
>> +	) &&
>> +	# provide explicit GIT_DIR
>> +	(
>> +		cd root/p &&
>> +		run_with_sudo <<-END
>> +			GIT_DIR=.git &&
>> +			GIT_WORK_TREE=. &&
>> +			export GIT_DIR GIT_WORK_TREE &&
>> +			git status
>
> I'm confused by this. Does this mean we don't do the ownership checks
> if GIT_DIR and or GIT_WORK_TREE are set in the environment?

That's by design.  Remember, we are protecting people from "cd"ing
into a (sub)directory, unknowingly, that happens to be controled by
a Git repository somebody else prepared as a trap.  So we try to
ensure who owns such a repository when we do the discovery.  Users
who set these environment variables to tell Git that they are aware
that they are working with that directory are different from the
target audience.

> Thanks for working on this

Ditto.  And thanks for reviewing and raising questions.  I agree
with all the things you said in the part of the message I did not
touch in this reply.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-03 14:03             ` Phillip Wood
@ 2022-05-03 15:56               ` Carlo Marcelo Arenas Belón
  2022-05-04 11:15                 ` Phillip Wood
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-03 15:56 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, gitster, bagasdotme, SZEDER Gábor

On Tue, May 03, 2022 at 03:03:59PM +0100, Phillip Wood wrote:
> > +
> > +# this prerequisite should be added to all the tests, it not only prevents
> > +# the test from failing but also warms up any authentication cache sudo
> > +# might need to avoid asking for a password
> 
> If this is required for all the tests then it would be simpler just to skip
> all the tests if it is not satisfied as you do above.

it is obviously not required (as shown by some tests in patch 3 not having
it and by my choice if the word "should"), but it still recommended, which
I was hoping would be explained by that comment since if sudo to root is
only allowed "temporarily" by someone typing their password, then sudo keeps
that authentication in a cache, that could probably expire otherwise.

Ironically, this comment was meant to explain why it was not checked once
at the beginning and being used instead in almost every test, but presume
I wasn't clear enough, not sure if worth resubmitting either.

> Running "sudo env" shows that it sets $HOME to /root which means that these
> tests will pick up /root/.gitconfig if it exists.

I think this depends on how sudo is configured, but yes ANY environment
variables could be set to unsafe values that would confuse git if it assumes
it is still running as part of the test suite.

My approach was to make sure (with the prerequisite) that at least we have
PATH set to the right value, so we won't start accidentally running the
system provided git, but you are correct that at least for patch1, the only
thing I can WARRANT to work is `git status`, but it should be also clear
to whoever writes tests using sudo, that it can't be otherwise since git it
is not only running as root, but it is running in the environment that sudo
provides when doing so.

> Normally when running the
> tests we set $HOME to $TEST_DIRECTORY so they are run in a predictable
> environment. At least anything pointed to by core.hooksPath or
> core.fsmontior in that file is expecting to be run as root.

which should be the same expectation of anyone running `sudo make install`
in their own repository, so we are just mimicking the use case we care
about.

core.hooksPath or core.fsmonitor might be relevant now, but there is no way
for me to predict what else might be in the future, and then again `sudo -H`
will behave differently than `sudo` and there is nothing git can do to
prevent that, so I keep thinking $HOME is not that special eitherway.

it might be worth adding that as well as a constrain into the prerequisite
though, so if your sudo does change HOME then we skip these tests, or we
try harder to call sudo in a way that doesn't change HOME instead.

> I think it is
> worth spelling this out explicitly in the commit message (currently it is a
> bit vague about what the implications of not having better integration with
> the test framework are) and the top of the test file. Note that t1509
> sources test-lib.sh as the root user so does not have this issue.

As explained below, there is no way to "explictly" document all things that
might be relevant, and being vague was therefore by design.

t1509 has also a different objective AFAIK, which is to test in an environment
where everything is running as root, which is not what we want to do here.

root is relevant only when we got it through sudo, hence I don't think that
just reading test-lib.sh through sudo as well would be a "solution" in this
case, but I agree with you that for a full integration a lot more would be
needed, which was again documented and punted explicitly.

> > +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
> > +'
> > +
> > +test_expect_success SUDO 'setup' '
> > +	sudo rm -rf root &&
> > +	mkdir -p root/r &&
> > +	sudo chown root root &&
> > +	(
> > +		cd root/r &&
> > +		git init
> 
> Using git -C <directory> would eliminate a lot of the sub shells in this
> file

My assumption (and help me understand if it was incorrect) is that these
tests should document the expected use cases, so you are correct that
both cd and -C accomplish the same in the end, but I think that cd is what
users would more normally use, and by writing with it (specially since it
requires a subshell) is also more easy to spot and understand that an
invocation of git with -C.

I have to admit I didn't even thought of using -C originally because of
that, but if you think that makes the test easier to understand and better
I am sure happy to include that in a reroll.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-03 15:56               ` Carlo Marcelo Arenas Belón
@ 2022-05-04 11:15                 ` Phillip Wood
  2022-05-04 13:02                   ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-05-04 11:15 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, phillip.wood
  Cc: git, gitster, bagasdotme, SZEDER Gábor

Hi Carlo

On 03/05/2022 16:56, Carlo Marcelo Arenas Belón wrote:
> On Tue, May 03, 2022 at 03:03:59PM +0100, Phillip Wood wrote:
>>> +
>>> +# this prerequisite should be added to all the tests, it not only prevents
>>> +# the test from failing but also warms up any authentication cache sudo
>>> +# might need to avoid asking for a password
>>
>> If this is required for all the tests then it would be simpler just to skip
>> all the tests if it is not satisfied as you do above.
> 
> it is obviously not required (as shown by some tests in patch 3 not having
> it and by my choice if the word "should"),

I'm afraid it is not obvious to me. As far as I can see the only test 
that does not have this prerequisite is 'cannot access if owned by root' 
added in patch 3. That test needs a setup test to run first which 
requires sudo so there is no point running it if this prerequisite is 
not satisfied.

> but it still recommended, which
> I was hoping would be explained by that comment since if sudo to root is
> only allowed "temporarily" by someone typing their password, then sudo keeps
> that authentication in a cache, that could probably expire otherwise.
> 
> Ironically, this comment was meant to explain why it was not checked once
> at the beginning and being used instead in almost every test, but presume
> I wasn't clear enough, not sure if worth resubmitting either.

That was not clear to me. Prerequisites are evaluated once and the 
result is cached. Making it lazy just means it is evaluated when it is 
first required rather than when it is defined. You're right that we want 
to avoid sudo hanging because it is waiting for a password. We should 
define something like

sudo () {
	command sudo -n "$@"
}

to avoid that.

>> Running "sudo env" shows that it sets $HOME to /root which means that these
>> tests will pick up /root/.gitconfig if it exists.
> 
> I think this depends on how sudo is configured, but yes ANY environment
> variables could be set to unsafe values that would confuse git if it assumes
> it is still running as part of the test suite.

I think I'm using the default configuration for that setting (or at 
least the default configured by the linux distribution I'm using).

> My approach was to make sure (with the prerequisite) that at least we have
> PATH set to the right value, so we won't start accidentally running the
> system provided git, but you are correct that at least for patch1, the only
> thing I can WARRANT to work is `git status`, but it should be also clear
> to whoever writes tests using sudo, that it can't be otherwise since git it
> is not only running as root, but it is running in the environment that sudo
> provides when doing so.
> 
>> Normally when running the
>> tests we set $HOME to $TEST_DIRECTORY so they are run in a predictable
>> environment. At least anything pointed to by core.hooksPath or
>> core.fsmontior in that file is expecting to be run as root.
> 
> which should be the same expectation of anyone running `sudo make install`
> in their own repository, so we are just mimicking the use case we care
> about.

Two of the most important promises the suite makes are that (i) tests do 
not write outside $TEST_DIRECTORY and (ii) the tests are not affected by 
the user's or system's git config files. By having $HOME point to /root 
we are clearly violating the second promise and making it much easier to 
accidentally violate the first by inadvertently writing to $HOME.

> core.hooksPath or core.fsmonitor might be relevant now, but there is no way
> for me to predict what else might be in the future,

exactly, they are just examples and show why setting HOME=root is a bad idea

> and then again `sudo -H`
> will behave differently than `sudo` and there is nothing git can do to
> prevent that, so I keep thinking $HOME is not that special eitherway.

I think $HOME is important enough to worry about because the test suite 
deliberately resets to avoid reading the user's config. Whether some 
other random variable such as GIT_COMMITTER_DATE is set or not does not 
matter in the same way.

> it might be worth adding that as well as a constrain into the prerequisite
> though, so if your sudo does change HOME then we skip these tests, or we
> try harder to call sudo in a way that doesn't change HOME instead.

It would be better to call git via a wrapper that sets HOME correctly

>> I think it is
>> worth spelling this out explicitly in the commit message (currently it is a
>> bit vague about what the implications of not having better integration with
>> the test framework are) and the top of the test file. Note that t1509
>> sources test-lib.sh as the root user so does not have this issue.
> 
> As explained below, there is no way to "explictly" document all things that
> might be relevant, and being vague was therefore by design.

Being vague by design is unhelpful, just because it is difficult to list 
all the possible implications of a changes does not mean that one should 
not list the important known issues. Commit messages should be 
transparent about the known implications of the changes the commit 
introduces and whether there are likely to be other unanticipated 
implications.

> t1509 has also a different objective AFAIK, which is to test in an environment
> where everything is running as root, which is not what we want to do here.

Indeed - I brought it up because we're reusing IKNOWWHATIAMDOING but not 
documenting that we using it in a different way.

> root is relevant only when we got it through sudo, hence I don't think that
> just reading test-lib.sh through sudo as well would be a "solution" in this
> case, but I agree with you that for a full integration a lot more would be
> needed, which was again documented and punted explicitly.
> 
>>> +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
>>> +'
>>> +
>>> +test_expect_success SUDO 'setup' '
>>> +	sudo rm -rf root &&
>>> +	mkdir -p root/r &&
>>> +	sudo chown root root &&
>>> +	(
>>> +		cd root/r &&
>>> +		git init
>>
>> Using git -C <directory> would eliminate a lot of the sub shells in this
>> file
> 
> My assumption (and help me understand if it was incorrect) is that these
> tests should document the expected use cases, so you are correct that
> both cd and -C accomplish the same in the end, but I think that cd is what
> users would more normally use, and by writing with it (specially since it
> requires a subshell) is also more easy to spot and understand that an
> invocation of git with -C.
> 
> I have to admit I didn't even thought of using -C originally because of
> that, but if you think that makes the test easier to understand and better
> I am sure happy to include that in a reroll.

I think it's pretty common to use -C in the test suite when running git 
in a repository that is a subdirectory of $TEST_DIRECTORY.

Best Wishes

Phillip

> Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-04 11:15                 ` Phillip Wood
@ 2022-05-04 13:02                   ` Carlo Arenas
  2022-05-04 14:11                     ` Phillip Wood
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-04 13:02 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, gitster, bagasdotme, SZEDER Gábor

On Wed, May 4, 2022 at 4:15 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 03/05/2022 16:56, Carlo Marcelo Arenas Belón wrote:
> > On Tue, May 03, 2022 at 03:03:59PM +0100, Phillip Wood wrote:
> >>> +
> >>> +# this prerequisite should be added to all the tests, it not only prevents
> >>> +# the test from failing but also warms up any authentication cache sudo
> >>> +# might need to avoid asking for a password
> >>
> >> If this is required for all the tests then it would be simpler just to skip
> >> all the tests if it is not satisfied as you do above.
> >
> > it is obviously not required (as shown by some tests in patch 3 not having
> > it and by my choice if the word "should"),
>
> I'm afraid it is not obvious to me. As far as I can see the only test
> that does not have this prerequisite is 'cannot access if owned by root'
> added in patch 3. That test needs a setup test to run first which
> requires sudo so there is no point running it if this prerequisite is
> not satisfied.

We are in violent agreement here, but again (and don't take it wrong,
since it is most likely my fault for not being clear enough in my
request), the issue is I can't figure out how to make it obvious to
you since just the use of the world "should" made it obvious to me.

Do you have any suggestion I could include in a v4 reroll?, which will
be also sent as an RFC, so hopefully this time, we get at least
agreement in patches 1 and 2, so we could move forward and unblock the
development pipeline.

> > but it still recommended, which
> > I was hoping would be explained by that comment since if sudo to root is
> > only allowed "temporarily" by someone typing their password, then sudo keeps
> > that authentication in a cache, that could probably expire otherwise.
> >
> > Ironically, this comment was meant to explain why it was not checked once
> > at the beginning and being used instead in almost every test, but presume
> > I wasn't clear enough, not sure if worth rerolling either.
>
> That was not clear to me. Prerequisites are evaluated once and the
> result is cached.

This is indeed a bug, my intention was that it will be called in every
request so I need to at least make it "not lazy"

> Making it lazy just means it is evaluated when it is
> first required rather than when it is defined. You're right that we want
> to avoid sudo hanging because it is waiting for a password. We should
> define something like
>
> sudo () {
>         command sudo -n "$@"
> }

This gets us half way to what is needed.  Indeed I explicitly use sudo
-n in the "prerequisite" for this reason, and originally I used a perl
function that called sudo with a timeout and then killed it after a
fixed time.

The problem is we ALSO don't want the tests to fail if sudo suddenly
decides to ask for a password, so by design I wanted to detect that
issue in the prerequisite and disable the test instead, which I
obviously didn't get right.

> >> Running "sudo env" shows that it sets $HOME to /root which means that these
> >> tests will pick up /root/.gitconfig if it exists.
> >
> > I think this depends on how sudo is configured, but yes ANY environment
> > variables could be set to unsafe values that would confuse git if it assumes
> > it is still running as part of the test suite.
>
> I think I'm using the default configuration for that setting (or at
> least the default configured by the linux distribution I'm using).

As Junio pointed out in the discussion, this is likely not going to
run in a lot of places because of issues like that.
Indeed in Debian 10 (and therefore most likely all our Ubuntu based
CI) the prerequisite fails probably because of the same reason it
fails in your workstation.

sudo is configured to do '-H' by default (which we can't disable
unless we change the configuration AFAIK) and also doesn't run with
'-s' which means that even the following shows an error

  sudo command -v git

my hope was that it will at least run in the macOS part of the CI
which is better than nothing, or alternatively we could define an
alias as you suggested and force -s on it.

The problem of doing that though, is that then sudo might run the
wrong shell, which is also part of the reason why I was forcing it by
calling the script explicitly through the shell we want to use
instead.

> > My approach was to make sure (with the prerequisite) that at least we have
> > PATH set to the right value, so we won't start accidentally running the
> > system provided git, but you are correct that at least for patch1, the only
> > thing I can WARRANT to work is `git status`, but it should be also clear
> > to whoever writes tests using sudo, that it can't be otherwise since git it
> > is not only running as root, but it is running in the environment that sudo
> > provides when doing so.
> >
> >> Normally when running the
> >> tests we set $HOME to $TEST_DIRECTORY so they are run in a predictable
> >> environment. At least anything pointed to by core.hooksPath or
> >> core.fsmontior in that file is expecting to be run as root.
> >
> > which should be the same expectation of anyone running `sudo make install`
> > in their own repository, so we are just mimicking the use case we care
> > about.
>
> Two of the most important promises the suite makes are that (i) tests do
> not write outside $TEST_DIRECTORY and (ii) the tests are not affected by
> the user's or system's git config files. By having $HOME point to /root
> we are clearly violating the second promise and making it much easier to
> accidentally violate the first by inadvertently writing to $HOME.

While I think I'd seen my fair set of violations of those 2 principles
before, I agree that not violating them here would be a good option,
but I also consider this as part of the "integration with the
framework" that was explicitly punted.

Patch 1 only concerns with making an accurate reproduction of the
problem that was presented as a regression, so having the wrong shell
or having root's HOME if your sudo so prefers is by design what we
should do as well, and my ONLY warranty is that I would be able to
call the `git status` command I am trying to test by making sure that
at least sudo will (mostly) respect the PATH the test suite provides,
and which is also why I would rather have the prerequisite fail than
to make it work where it does not by default create a shell.

FWIW I don't think even that is perfect because a sufficiently evil
sudo configuration could force git to call a different status builtin,
but I thought calling the builtin directly by using git-status was
probably too much and also likely to fail in places where that binary
doesn't get created.

Patch 3 got what would be the beginnings of that "integration with the
framework", with an ugly and minimal "this is how we can inject
environment variables we care about" implementation that got of course
discarded in the RFC because it wasn't good enough and not strictly
needed.

I think your suggestion makes sense as an enhancement to patch3, but I
am not sure of how to get it done without reintroducing the
environment I got wrong already in the previous round.

> > core.hooksPath or core.fsmonitor might be relevant now, but there is no way
> > for me to predict what else might be in the future,
>
> exactly, they are just examples and show why setting HOME=root is a bad idea

I think that the current prerequisite prevents that already by failing
to work as I described before, is the prerequisite working on your
setup and still changing HOME?

this is what I get in macOS 11:

  $ sudo 'env' | grep HOME
  HOME=/Users/carlo
  $ sudo -H 'env' | grep HOME
  HOME=/var/root

> > and then again `sudo -H`
> > will behave differently than `sudo` and there is nothing git can do to
> > prevent that, so I keep thinking $HOME is not that special eitherway.
>
> I think $HOME is important enough to worry about because the test suite
> deliberately resets to avoid reading the user's config. Whether some
> other random variable such as GIT_COMMITTER_DATE is set or not does not
> matter in the same way.

I meant not important to our concerns that it will negatively impact
running these tests, I cannot provide any warranties that the sudo
environment provided wouldn't be evil enough, for example by setting
the path where git looks for its builtins.

but then again, I think that worrying about that is a stretch.

If root in a system we are running can change the sudoers
configurations or put configurations in root's home, that system has
more things to worry about than having this test running.

> > it might be worth adding that as well as a constraint into the prerequisite
> > though, so if your sudo does change HOME then we skip these tests, or we
> > try harder to call sudo in a way that doesn't change HOME instead.
>
> It would be better to call git via a wrapper that sets HOME correctly

I would rather make sure the prerequisite fails and all these tests
are skipped in that system.

Getting a wrapper that fixes THIS specific case won't protect against
many others

> >> I think it is
> >> worth spelling this out explicitly in the commit message (currently it is a
> >> bit vague about what the implications of not having better integration with
> >> the test framework are) and the top of the test file. Note that t1509
> >> sources test-lib.sh as the root user so does not have this issue.
> >
> > As explained before, there is no way to "explicitly" document all things that
> > might be relevant, and being vague was therefore by design.
>
> Being vague by design is unhelpful, just because it is difficult to list
> all the possible implications of a changes does not mean that one should
> not list the important known issues. Commit messages should be
> transparent about the known implications of the changes the commit
> introduces and whether there are likely to be other unanticipated
> implications.

fair enough, care to come with a suggestion?
again I think the "we are running things as root folks!!" is enough to
trigger a "better do not set that IKNOWWHATIAMDOING" variable on me,
but it might be my sysadmin experience talking.

> > t1509 has also a different objective AFAIK, which is to test in an environment
> > where everything is running as root, which is not what we want to do here.
>
> Indeed - I brought it up because we're reusing IKNOWWHATIAMDOING but not
> documenting that we using it in a different way.

It is not used in a different way.

IKNOWWHATIAMDOING is meant to keep developers from running potentially
dangerous stuff, that yes could mess with your system badly and which
also applies here.

BTW while trying to test this in CI, I realized it is not set there,
so might as well be changed to something different that will be and
that would ease your concerns.

> >>> +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
> >>> +'
> >>> +
> >>> +test_expect_success SUDO 'setup' '
> >>> +   sudo rm -rf root &&
> >>> +   mkdir -p root/r &&
> >>> +   sudo chown root root &&
> >>> +   (
> >>> +           cd root/r &&
> >>> +           git init
> >>
> >> Using git -C <directory> would eliminate a lot of the sub shells in this
> >> file
> >
> > My assumption (and help me understand if it was incorrect) is that these
> > tests should document the expected use cases, so you are correct that
> > both cd and -C accomplish the same in the end, but I think that cd is what
> > users would more normally use, and by writing with it (specially since it
> > requires a subshell) is also more easy to spot and understand that an
> > invocation of git with -C.
> >
> > I have to admit I didn't even thought of using -C originally because of
> > that, but if you think that makes the test easier to understand and better
> > I am sure happy to include that in a reroll.
>
> I think it's pretty common to use -C in the test suite when running git
> in a repository that is a subdirectory of $TEST_DIRECTORY.

I get that, but do you think that in this case makes the tests simpler
and more importantly more recognizable?

I'll try to use it more in the reroll, but examples where it actually
improve the tests would be useful.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-04 13:02                   ` Carlo Arenas
@ 2022-05-04 14:11                     ` Phillip Wood
  0 siblings, 0 replies; 161+ messages in thread
From: Phillip Wood @ 2022-05-04 14:11 UTC (permalink / raw)
  To: Carlo Arenas, phillip.wood; +Cc: git, gitster, bagasdotme, SZEDER Gábor

Hi Carlo

Just a quick reply for now with some brief thoughts - I'll try and 
answer more fully tomorrow.

On 04/05/2022 14:02, Carlo Arenas wrote:
 >[...]
> 
> This is indeed a bug, my intention was that it will be called in every
> request so I need to at least make it "not lazy"

Unfortunately don't think that will work, it just evaluates the 
prerequisite when you define it and uses the cached result for each 
test. (The lazy one evaluates the prerequisite on its first use and then 
caches the result)

>> Making it lazy just means it is evaluated when it is
>> first required rather than when it is defined. You're right that we want
>> to avoid sudo hanging because it is waiting for a password. We should
>> define something like
>>
>> sudo () {
>>          command sudo -n "$@"
>> }
> 
> This gets us half way to what is needed.  Indeed I explicitly use sudo
> -n in the "prerequisite" for this reason, and originally I used a perl
> function that called sudo with a timeout and then killed it after a
> fixed time.
> 
> The problem is we ALSO don't want the tests to fail if sudo suddenly
> decides to ask for a password, so by design I wanted to detect that
> issue in the prerequisite and disable the test instead, which I
> obviously didn't get right.

I don't think we have a mechanism to do that. I think the best we can do 
is just to skip the whole file if the SUDO prerequisite fails. Depending 
on the configuration sudo will delay the expiration of the cache 
password each time it is called. In any case this test file is not going 
to take much time to run so if the prerequisite passes the tests should 
hopefully run before the cached password expires.

Another possibility is to call a function at the start of each test that 
skips the test if 'sudo -n' fails.

> [...] 
> again I think the "we are running things as root folks!!" is enough to
> trigger a "better do not set that IKNOWWHATIAMDOING" variable on me,
> but it might be my sysadmin experience talking.

It is the fact that we're not just changing the uid that is used to run 
the tests but we're changing the environment as well that I think we 
need to call out. It is not obvious that running the tests with a 
different uid will stop $HOME pointing to $TEST_DIRECTORY.


I'll try and get back to you on the other points tomorrow

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-03  6:54           ` [PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
  2022-05-03 14:03             ` Phillip Wood
@ 2022-05-05 13:44             ` Johannes Schindelin
  2022-05-05 14:34               ` Phillip Wood
                                 ` (3 more replies)
  1 sibling, 4 replies; 161+ messages in thread
From: Johannes Schindelin @ 2022-05-05 13:44 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, gitster, bagasdotme, phillip.wood123, SZEDER Gábor

[-- Attachment #1: Type: text/plain, Size: 5833 bytes --]

Hi Carlo,

On Mon, 2 May 2022, Carlo Marcelo Arenas Belón wrote:

> Originally reported after release of v2.35.2 (and other maint branches)
> for CVE-2022-24765 and blocking otherwise harmless commands that were
> done using sudo in a repository that was owned by the user.
>
> Add a new test script with very basic support to allow running git
> commands through sudo, so a reproduction could be implemented and that
> uses only `git status` as a proxy of the issue reported.
>
> Note that because of the way sudo interacts with the system, a much
> more complete integration with the test framework will require a lot
> more work and that was therefore intentionally punted for now.
>
> The current implementation requires the execution of a special cleanup
> function which should always be kept as the last "test" or otherwise
> the standard cleanup functions will fail because they can't remove
> the root owned directories that are used.  This also means that if
> failures are found while running the specifics of the failure might
> not be kept for further debugging and if the test was interrupted, it
> will be necessary to clean the working directory manually before
> restarting by running:
>
>   $ sudo rm -rf trash\ directory.t0034-root-safe-directory/
>
> The test file also uses at least one initial "setup" test that creates
> a parallel execution directory, while ignoring the repository created
> by the test framework, and special care should be taken when invoking
> commands through sudo, since the environment is otherwise independent
> from what the test framework expects.  Indeed `git status` was used
> as a proxy because it doesn't even require commits in the repository
> to work.
>
> A new SUDO prerequisite is provided that does some sanity checking
> to make sure the sudo command that will be used allows for passwordless
> execution as root and doesn't mess with git execution paths, but
> otherwise additional work will be required to ensure additional
> commands behave as expected and that will be addressed in a later patch.
>
> Most of those characteristics make this test mostly suitable only for
> CI, but it could be executed locally if special care is taken to provide
> for some of them in the local configuration and maybe making use of the
> sudo credential cache by first invoking sudo, entering your password if
> needed, and then invoking the test by doing:
>
>   $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh

Hmm. I would like to suggest that we can side-step all of these issues
(and the ones I outline below) by considering a similar approach to the
one Stolee took in t0033: use one or more `GIT_TEST_*` environment
variables to pretend the exact scenario we want to test for.

>
> Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
>  t/t0034-root-safe-directory.sh | 49 ++++++++++++++++++++++++++++++++++
>  1 file changed, 49 insertions(+)
>  create mode 100755 t/t0034-root-safe-directory.sh
>
> diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
> new file mode 100755
> index 00000000000..6dac7a05cfd
> --- /dev/null
> +++ b/t/t0034-root-safe-directory.sh
> @@ -0,0 +1,49 @@
> +#!/bin/sh
> +
> +test_description='verify safe.directory checks while running as root'
> +
> +. ./test-lib.sh
> +
> +if [ "$IKNOWWHATIAMDOING" != "YES" ]
> +then
> +	skip_all="You must set env var IKNOWWHATIAMDOING=YES in order to run this test"
> +	test_done
> +fi
> +
> +# this prerequisite should be added to all the tests, it not only prevents
> +# the test from failing but also warms up any authentication cache sudo
> +# might need to avoid asking for a password
> +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 &&

In my Ubuntu setup, `/bin/sh` is a symbolic link to `/bin/dash`, which
does not understand the `command`. It might make more sense to use `type`
here, but it is quite possible that `type git` uses a different output
format than `sudo type git` if they use different shells.

Another complication is that the `/etc/sudoers` I have over here specifies
a `secure_path`, which prevents the directory with the just-built `git`
executable from being left in `PATH`. I had to edit `/etc/sudoers` _and_
change the script to using `sudo -sE` to fix these woes.

It took me a good chunk of time to figure out how to run these tests, and
I will have to remember to revert the temporary edit of `/etc/sudoers`
file. This is definitely not something I plan on doing often, so I wonder
how these regression tests can guarantee that no regressions are
introduced if they are so hard to run ;-)

> +	test_cmp u r
> +'
> +
> +test_expect_success SUDO 'setup' '
> +	sudo rm -rf root &&
> +	mkdir -p root/r &&
> +	sudo chown root root &&
> +	(
> +		cd root/r &&
> +		git init
> +	)
> +'
> +
> +test_expect_failure SUDO 'sudo git status as original owner' '
> +	(
> +		cd root/r &&
> +		git status &&
> +		sudo git status
> +	)
> +'
> +
> +# this MUST be always the last test, if used more than once, the next
> +# test should do a full setup again.
> +test_expect_success SUDO 'cleanup' '
> +	sudo rm -rf root

This would be more canonical as `test_when_finished "sudo rm -rf root"` in
the preceding test cases.

But as I said above, I would prefer it if we could figure out a way to
pretend a specific scenario via `GIT_TEST_*`. That would ensure not only
that those tests are easy to run, but also that they run whenever the test
suite runs.

Thank you for working on this!
Dscho

> +'
> +
> +test_done
> --
> 2.36.0.352.g0cd7feaf86f
>
>

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-03  6:54           ` [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-05 14:01             ` Johannes Schindelin
  2022-05-05 14:32               ` Phillip Wood
                                 ` (2 more replies)
  0 siblings, 3 replies; 161+ messages in thread
From: Johannes Schindelin @ 2022-05-05 14:01 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, gitster, bagasdotme, phillip.wood123, Guy Maurel,
	SZEDER Gábor, Randall Becker

[-- Attachment #1: Type: text/plain, Size: 5560 bytes --]

Hi Carlo,

On Mon, 2 May 2022, Carlo Marcelo Arenas Belón wrote:

> bdc77d1d685 (Add a function to determine whether a path is owned by the
> current user, 2022-03-02) checks for the effective uid of the running
> process using geteuid() but didn't account for cases where that user was
> root (because git was invoked through sudo or a compatible tool) and the
> original uid that repository trusted for its config was no longer known,
> therefore failing the following otherwise safe call:
>
>   guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
>   [sudo] password for guy:
>   fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)
>
> Attempt to detect those cases by using the environment variables that
> those tools create to keep track of the original user id, and do the
> ownership check using that instead.
>
> This assumes the environment the user is running on after going
> privileged can't be tampered with, and also adds code to restrict that
> the new behavior only applies if running as root, therefore keeping the
> most common case, which runs unprivileged, from changing, but because of
> that, it will miss cases where sudo (or an equivalent) was used to change
> to another unprivileged user or where the equivalent tool used to raise
> privileges didn't track the original id in a sudo compatible way.

Hmm. I do realize that this is a quite common scenario, and I wish we
would not need to rush for a fix here: Otherwise we could carefully design
an "untrusted" mode in which Git errors out on spawning user-specified
commands and on writing files (and avoids refreshing the index to avoid
having to write a file), but runs normally if none of that is needed.

> diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
> index 6d764fe0ccf..ee558ced8c7 100644
> --- a/Documentation/config/safe.txt
> +++ b/Documentation/config/safe.txt
> @@ -26,3 +26,12 @@ 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.
> ++
> +When git tries to check for ownership of git repositories, it will
> +obviously do so with the uid of the user that is running git itself,
> +but if git is running as root, it will check first if it might have
> +been started through `sudo`, and if that is the case, will instead
> +use the uid of the user that did so.
> +If that is not what you would prefer and want git to only trust
> +repositories that are owned by root instead, then you should remove
> +the `SUDO_UID` variable from root's environment.
> diff --git a/git-compat-util.h b/git-compat-util.h
> index 63ba89dd31d..dfdd3e4f81a 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -393,12 +393,50 @@ 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

I do wonder whether we have to play this kind of fragile game. Why not
simply respect `SUDO_UID` if it is set? It's not like we expect attackers
to have control over the environment and could set this malicously.

> +
> +/*
> + * this helper function overrides a ROOT_UID with the one provided by
> + * an environment variable, do not use unless the original user is
> + * root
> + */
> +static inline void extract_id_from_env(const char *env, uid_t *id)
> +{
> +	const char *real_uid = getenv(env);
> +
> +	/* discard any empty values */
> +	if (real_uid && *real_uid) {
> +		char *endptr;
> +		unsigned long env_id;
> +		int saved_errno = errno;
> +
> +		errno = 0;
> +		env_id = strtoul(real_uid, &endptr, 10);
> +		if (!errno && !*endptr && env_id <= (uid_t)-1)

We should not look at `errno` here unless the return value of `strtoul()`
indicates that there might have been an error (i.e. when it is
`ULONG_MAX`).

Likewise, we need to either initialize `endptr` or only look at it when
`strtoul()` succeeded.

We could side-step all of this, of course, if we simply did this:

	euid = getuid();
	if (euid == ROOT_UID)
		euid = git_env_ulong("SUDO_UID", euid);

> +			*id = env_id;
> +
> +		errno = saved_errno;
> +	}

> +}
> +
>  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;

Since this code is not even compiled on Windows, I believe we need to
adjust the documentation accordingly ("On systems other than Windows,
where `sudo` is available, ...").

>  }
>
>  #define is_path_owned_by_current_user is_path_owned_by_current_uid
> diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
> index 6dac7a05cfd..dd659aed4e1 100755
> --- a/t/t0034-root-safe-directory.sh
> +++ b/t/t0034-root-safe-directory.sh
> @@ -32,7 +32,7 @@ test_expect_success SUDO 'setup' '
>  	)
>  '
>
> -test_expect_failure SUDO 'sudo git status as original owner' '
> +test_expect_success SUDO 'sudo git status as original owner' '
>  	(
>  		cd root/r &&
>  		git status &&
> --
> 2.36.0.352.g0cd7feaf86f

Again, thank you for working on this!
Dscho

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-05 14:01             ` Johannes Schindelin
@ 2022-05-05 14:32               ` Phillip Wood
  2022-05-06 19:15                 ` Carlo Arenas
  2022-05-05 16:09               ` Junio C Hamano
  2022-05-06 20:02               ` Carlo Arenas
  2 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-05-05 14:32 UTC (permalink / raw)
  To: Johannes Schindelin, Carlo Marcelo Arenas Belón
  Cc: git, gitster, bagasdotme, Guy Maurel, SZEDER Gábor, Randall Becker

Hi Dscho

On 05/05/2022 15:01, Johannes Schindelin wrote:
> [...]
>> +
>> +/*
>> + * this helper function overrides a ROOT_UID with the one provided by
>> + * an environment variable, do not use unless the original user is
>> + * root
>> + */
>> +static inline void extract_id_from_env(const char *env, uid_t *id)
>> +{
>> +	const char *real_uid = getenv(env);
>> +
>> +	/* discard any empty values */
>> +	if (real_uid && *real_uid) {
>> +		char *endptr;
>> +		unsigned long env_id;
>> +		int saved_errno = errno;
>> +
>> +		errno = 0;
>> +		env_id = strtoul(real_uid, &endptr, 10);
>> +		if (!errno && !*endptr && env_id <= (uid_t)-1)
> 
> We should not look at `errno` here unless the return value of `strtoul()`
> indicates that there might have been an error (i.e. when it is
> `ULONG_MAX`). >
> Likewise, we need to either initialize `endptr` or only look at it when
> `strtoul()` succeeded.

I don't think we need to do either of those, and indeed the function you 
suggest below does not do them. The standard guarantees that endptr is 
always set and there is no harm in unconditionally checking errno.

> We could side-step all of this, of course, if we simply did this:
> 
> 	euid = getuid();
> 	if (euid == ROOT_UID)
> 		euid = git_env_ulong("SUDO_UID", euid);

That's a nice suggestion, I didn't know that function existed. It means 
we would die() if we could not parse SUDO_UID which I think is 
reasonable (we'd also accept a units suffix an the uid)

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-05 13:44             ` Johannes Schindelin
@ 2022-05-05 14:34               ` Phillip Wood
  2022-05-05 15:50               ` Junio C Hamano
                                 ` (2 subsequent siblings)
  3 siblings, 0 replies; 161+ messages in thread
From: Phillip Wood @ 2022-05-05 14:34 UTC (permalink / raw)
  To: Johannes Schindelin, Carlo Marcelo Arenas Belón
  Cc: git, gitster, bagasdotme, SZEDER Gábor

Hi Dscho

On 05/05/2022 14:44, Johannes Schindelin wrote:
>> A new SUDO prerequisite is provided that does some sanity checking
>> to make sure the sudo command that will be used allows for passwordless
>> execution as root and doesn't mess with git execution paths, but
>> otherwise additional work will be required to ensure additional
>> commands behave as expected and that will be addressed in a later patch.
>>
>> Most of those characteristics make this test mostly suitable only for
>> CI, but it could be executed locally if special care is taken to provide
>> for some of them in the local configuration and maybe making use of the
>> sudo credential cache by first invoking sudo, entering your password if
>> needed, and then invoking the test by doing:
>>
>>    $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh
> 
> Hmm. I would like to suggest that we can side-step all of these issues
> (and the ones I outline below) by considering a similar approach to the
> one Stolee took in t0033: use one or more `GIT_TEST_*` environment
> variables to pretend the exact scenario we want to test for.

That's an excellent suggestion. Trying to use sudo in the tests leads to 
all sorts of issues, if we can use a GIT_TEST_* approach instead that 
would be much better.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-05 13:44             ` Johannes Schindelin
  2022-05-05 14:34               ` Phillip Wood
@ 2022-05-05 15:50               ` Junio C Hamano
  2022-05-05 18:33               ` Junio C Hamano
  2022-05-06 17:39               ` Carlo Arenas
  3 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-05 15:50 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Carlo Marcelo Arenas Belón, git, bagasdotme,
	phillip.wood123, SZEDER Gábor

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

>> A new SUDO prerequisite is provided that does some sanity checking
>> to make sure the sudo command that will be used allows for passwordless
>> execution as root and doesn't mess with git execution paths, but
>> otherwise additional work will be required to ensure additional
>> commands behave as expected and that will be addressed in a later patch.

This part probably needs to be stressed, not just here but near the
part where we require IKNOWWHATIAMDOING=YES to be set.  For regular
interactive boxes, this test should pretty much be useless, as on a
normally configured machine with human users, it is likely that
"sudo" updates/restricts PATH to a limited set of directories and
exclude the path to our just-built-and-being-tested "git".

IOW, this is primarily (and likely to be solely) for a specialized
CI job in a very controlled environment.

>> +# this prerequisite should be added to all the tests, it not only prevents
>> +# the test from failing but also warms up any authentication cache sudo
>> +# might need to avoid asking for a password
>> +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 &&
>
> In my Ubuntu setup, `/bin/sh` is a symbolic link to `/bin/dash`, which
> does not understand the `command`. It might make more sense to use `type`
> here, but it is quite possible that `type git` uses a different output
> format than `sudo type git` if they use different shells.

So with that in mind, shell portability is still an issue, but ...

> Another complication is that the `/etc/sudoers` I have over here specifies

... /etc/sudoers (both people allowed to use and how environments
are futzed with) is not.  If your /etc/sudoers do not allow SUDO
prereq here to pass, then that is OK.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-05 14:01             ` Johannes Schindelin
  2022-05-05 14:32               ` Phillip Wood
@ 2022-05-05 16:09               ` Junio C Hamano
  2022-05-06 20:02               ` Carlo Arenas
  2 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-05 16:09 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Carlo Marcelo Arenas Belón, git, bagasdotme,
	phillip.wood123, Guy Maurel, SZEDER Gábor, Randall Becker

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> We could side-step all of this, of course, if we simply did this:
>
> 	euid = getuid();
> 	if (euid == ROOT_UID)
> 		euid = git_env_ulong("SUDO_UID", euid);

Yes, that is not "side-stepping" at all.  It is "we already have a
function that knows how to use strto*l() correctly" ;-)

Very good suggestion.  Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-05 13:44             ` Johannes Schindelin
  2022-05-05 14:34               ` Phillip Wood
  2022-05-05 15:50               ` Junio C Hamano
@ 2022-05-05 18:33               ` Junio C Hamano
  2022-05-05 19:39                 ` Junio C Hamano
  2022-05-09  8:21                 ` Phillip Wood
  2022-05-06 17:39               ` Carlo Arenas
  3 siblings, 2 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-05 18:33 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Carlo Marcelo Arenas Belón, git, bagasdotme,
	phillip.wood123, SZEDER Gábor

Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:

> Hmm. I would like to suggest that we can side-step all of these issues
> (and the ones I outline below) by considering a similar approach to the
> one Stolee took in t0033: use one or more `GIT_TEST_*` environment
> variables to pretend the exact scenario we want to test for.

I like the GIT_TEST_ASSUME_DIFFERENT_OWNER because it is fairly
clear that it cannot be used as a new attack vector, even with
social engineering.

It would be nice if we can do something similar, but I am coming up
empty while trying to think of "we are testing; pretend that ..."
that is good for testing this SUDO_UID special case *and* clearly
cannot be used as an attack target.

So I very much like the suggestion in principle, but I am not sure
how useful the suggestion would be to make the resulting code better
in practice.

Perhaps this may be a way to pretend we are running a command under
'sudo'?

	test_pretend_sudo () {	
            GIT_TEST_PRETEND_GETEUID_RETURNING_ROOT=1 \
	    GIT_TEST_PRETEND_LSTAT_RETURNING_ROOT=root/p \
                SUDO_UID=0 "$@"
	}

	test_expect_success 'access root-owned repository as root' '
		mkdir root/p &&
		git init root/p &&
		test_pretend_sudo git status
	'

That way we can avoid having to run "chown" while preparing for the
test fixture, and running "git status" under root, but I am not sure
if we want our shipped production binaries to have these "pretend"
knobs.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-05 18:33               ` Junio C Hamano
@ 2022-05-05 19:39                 ` Junio C Hamano
  2022-05-06 21:03                   ` Carlo Arenas
  2022-05-09  8:21                 ` Phillip Wood
  1 sibling, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-05 19:39 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: Carlo Marcelo Arenas Belón, git, bagasdotme,
	phillip.wood123, SZEDER Gábor

Junio C Hamano <gitster@pobox.com> writes:

> I like the GIT_TEST_ASSUME_DIFFERENT_OWNER because it is fairly
> clear that it cannot be used as a new attack vector, even with
> social engineering.
>
> It would be nice if we can do something similar, but I am coming up
> empty while trying to think of "we are testing; pretend that ..."
> that is good for testing this SUDO_UID special case *and* clearly
> cannot be used as an attack target.
>
> So I very much like the suggestion in principle, but I am not sure
> how useful the suggestion would be to make the resulting code better
> in practice.
> ...

The worst part is that the SUDO_UID stuff is about _loosening_ the
protection the other parts of the mechanism implements.  We do not
allow access when euid does not match st_uid, but with SUDO_UID, we
instead use that for checking when euid is root.  So setting for
testing such a feature works to loosen the protection, which would
make the attack surface larger.  So I am not so optimistic that we
can invent a GIT_TEST_* knob as good as ASSUME_DIFFERENT_OWNER for
that.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo
  2022-05-03 14:12             ` Phillip Wood
  2022-05-03 15:27               ` Junio C Hamano
@ 2022-05-06 16:54               ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 16:54 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, gitster, bagasdotme

On Tue, May 3, 2022 at 7:12 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> > diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
> > new file mode 100644
> > index 00000000000..9ebb30fc82b
> > --- /dev/null
> > +++ b/t/lib-sudo.sh
> > @@ -0,0 +1,13 @@
> > +# Helpers for running git commands under sudo.
> > +
> > +# Runs a scriplet passed through stdin under sudo.
> > +run_with_sudo () {
> > +     local ret
> > +     local SH=${1-"$TEST_SHELL_PATH"}
>
> What use do you envisage for this? It would be simpler just to use
> $TEST_SHELL_PATH directly below

Since this function is a ripoff of write_script which has this feature
(except I changed the shell used), I thought it might come handy later
and that getting the first version with a hardcoded value wasn't
elegant enough, but you are right it is not needed, so is gone in the
next RFC.

> > +     local RUN="$HOME/$$.sh"
>
> Can we use a fixed name for the script? That would make things simpler
> especially debugging as one would know what file to look for. Also using
> $TEST_DIRECTORY rather than $HOME would make it clear where the file
> ends up.

Frankly (and speaking by experience because this broke a lot for me),
debugging is painful enough already that having to find the shell
script would be the least of your concerns.

The main reason why it is not hardcoded to a known name is that by
adding a slightly more random name, it makes it slightly more
difficult for someone to try to trick us into running something else
they prepared instead, and the fact that it will be run as root makes
it much more enticing.

Sorry about not using TEST_DIRECTORY to begin with, they are set to
the same value and thought HOME was clearer and shorter, but has been
fixed in the next RFC.

> > +     write_script "$RUN" "$SH"
> > +     sudo "$SH" -c "\"$RUN\""
>
> I think using write_script means we can just do 'sudo "$RUN"'

We could, but by doing it this way we ensure:
* we specifically require sudo to allow running a shell, and that
shell is the one we want it to run (a strict sudo wouldn't do that)
* we obscure a little bit what sudo is running from it and rely more
in the shell, so it won't try to be helpful and block it (running
shell scripts is something a strict sudo might not allow as well)

On that last point I am still debating if the prerequisite should be
enhanced to detect that case and fail, but since it is restrictive
enough already it might not be worth doing now.

> > -# this MUST be always the last test, if used more than once, the next
> > -# test should do a full setup again.
>
> Why is the comment being changed? If you want the shorter version at the
> end of this patch can't we just use that wording in patch 1?

It was my failed attempt to document better how they are used, since
they are tricky enough to use already, but it is no longer needed and
is gone in the next RFC.

> > +test_expect_success SUDO 'cannot access with sudo' '
> > +     (
> > +             # TODO: test_must_fail needs additional functionality
> > +             # 6a67c759489 blocks its use with sudo
> > +             cd root/p &&
> > +             ! sudo git status
> > +     )
> > +'
>
> I think Junio suggested that this should work and showed it was simple
> to make it work. It seems funny that if sudo is started as root it does
> not work.

It is not when sudo is started as root, but when the user runs `sudo
-s` to get a shell and then LATER tries to use git with that shell.

I make a better attempt to explain it in a different thread[1], and is
tied to the documentation patch which might need further improvements
to make clear (hopefully not as extensive as my attempts to do so)

I am instead marking it as a known bug for the next RFC

Carlo

[1] https://lore.kernel.org/git/20220429012438.37o4uaxsrfdu2b6x@carlos-mbp.lan/

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-05 13:44             ` Johannes Schindelin
                                 ` (2 preceding siblings ...)
  2022-05-05 18:33               ` Junio C Hamano
@ 2022-05-06 17:39               ` Carlo Arenas
  3 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 17:39 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: git, gitster, bagasdotme, phillip.wood123, SZEDER Gábor

On Thu, May 5, 2022 at 6:44 AM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
>
> On Mon, 2 May 2022, Carlo Marcelo Arenas Belón wrote:
> >
> >   $ IKNOWWHATIAMDOING=YES ./t0034-root-safe-directory.sh
>
> Hmm. I would like to suggest that we can side-step all of these issues
> (and the ones I outline below) by considering a similar approach to the
> one Stolee took in t0033: use one or more `GIT_TEST_*` environment
> variables to pretend the exact scenario we want to test for.

I wish we could, but I think it is not possible in principle because
it would break the trust chain that we are relying on here to make
this work.

As explained in the commit message from the next patch, for this to be
useful as well as safe our ONLY chance is to trust SUDO_UID hasn't
been tampered with, which also requires that we run through sudo so
git is running as root and not as the regular user the test suite
would use.

If we remove the (I am really root, before I trust SUDO_UID)
requirement from our code, we have just opened ourselves to a way to
weaken the protections that were added with CVE-2022-24765.

to be frank while Junio mention this "weakens" the checks, I consider
I was strengthened them by introducing a mechanism the user could use
(only when he is root) to safely tell us that he wants to trust a
repository that is not owned by him without having to create an
exception, and also improving the usability of it, but "magically"
detecting which uid they are most likely to trust.

> It took me a good chunk of time to figure out how to run these tests, and
> I will have to remember to revert the temporary edit of `/etc/sudoers`
> file. This is definitely not something I plan on doing often, so I wonder
> how these regression tests can guarantee that no regressions are
> introduced if they are so hard to run ;-)

by running in the CI (at least the macOS hosts, and maybe others if we
decide later to butcher their sudoers config as well)
I am adding more instructions in the commit message from the next RFC
to help anyone that might want to run this locally (which I
wouldn't recommend myself)

> This would be more canonical as `test_when_finished "sudo rm -rf root"` in
> the preceding test cases.

correct, but I was attempting not to do that to make it less of a pain
to write more tests since (probably incorrectly) I assumed it would be
simpler to remember that there is a test at the end that does the
cleanup and at least one at the beginning that does the setup than
probably having to take care of those 2 things on each test that you
write.

Ideally, the test framework would be able to know that this test
creates files as root and cleanup itself, but that was specifically
punted.

I am keeping this for the next RFC, but I am open to changing it to
whatever you would prefer, until a proper integration could be written
to clean that mess up.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-04-28 18:12               ` Junio C Hamano
@ 2022-05-06 17:50                 ` Carlo Arenas
  2022-05-06 21:43                   ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 17:50 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Phillip Wood, git

On Thu, Apr 28, 2022 at 11:12 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
> > On 28/04/2022 17:55, Junio C Hamano wrote:
> >> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
> >>
> >>> +test_description='verify safe.directory checks while running as root'
> >>> +
> >>> +. ./test-lib.sh
> >>> +
> >>> +if [ "$IKNOWWHATIAMDOING" != "YES" ]; then
> >> Style.
> >>      if test "$IKNOWWHATIAMDOING" != "YES"
> >>      then
> >
> > Also naming - we normally prefix test environment variables with
> > GIT_TEST_. IKNOWWHATIAMDOING does not tell us what we are allowing by
> > setting the variable. Something like GIT_TEST_ALLOW_SUDO would tell us
> > what we're letting ourselves in for by setting it.
>
> If this weren't "let's reuse the same mechanism as already used in
> 1509", I would have had the same reaction.  Renaming would be better
> done outside the topic, I would think.

Since I am renaming it anyway as part of this topic with RFC v4, would
it be a good idea to require both?

I see the "IKNOWHATIAMDOING" not as a normal GIT_TEST flag, but as a
"here be dragons!" warning, and I later found that I either
misremembered it being enabled in the CI, or it was dropped with one
of those refactors we do often there.

My RFC v4 includes a new nice looking GIT_TEST variable as suggested
by Phillip which I am also enabling in the CI to hopefully make it
even more clear that this is only meant to run there, but sadly that
also means that this patch will likely have a conflict when merged
upwards.

Alternatively I could not enable the CI in this series that is aimed
at maint or at least do it in an independent patch so it could be
dropped where it is not strictly needed?

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-05 14:32               ` Phillip Wood
@ 2022-05-06 19:15                 ` Carlo Arenas
  2022-05-06 20:00                   ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 19:15 UTC (permalink / raw)
  To: phillip.wood
  Cc: Johannes Schindelin, git, gitster, bagasdotme, Guy Maurel,
	SZEDER Gábor, Randall Becker

On Thu, May 5, 2022 at 7:32 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 05/05/2022 15:01, Johannes Schindelin wrote:
> > [...]
> >> +
> >> +/*
> >> + * this helper function overrides a ROOT_UID with the one provided by
> >> + * an environment variable, do not use unless the original user is
> >> + * root
> >> + */
> >> +static inline void extract_id_from_env(const char *env, uid_t *id)
> >> +{
> >> +    const char *real_uid = getenv(env);
> >> +
> >> +    /* discard any empty values */
> >> +    if (real_uid && *real_uid) {
> >> +            char *endptr;
> >> +            unsigned long env_id;
> >> +            int saved_errno = errno;
> >> +
> >> +            errno = 0;
> >> +            env_id = strtoul(real_uid, &endptr, 10);
> >> +            if (!errno && !*endptr && env_id <= (uid_t)-1)
> >
> > We should not look at `errno` here unless the return value of `strtoul()`
> > indicates that there might have been an error (i.e. when it is
> > `ULONG_MAX`). >
> > Likewise, we need to either initialize `endptr` or only look at it when
> > `strtoul()` succeeded.
>
> I don't think we need to do either of those, and indeed the function you
> suggest below does not do them. The standard guarantees that endptr is
> always set and there is no harm in unconditionally checking errno.

I think the point dscho was trying to make is that while you are
correct that the standard guarantees those two things and
implementation might decide to not do them, we obviously support
systems that are not POSIX.

The irony is that my first confusing attempt to implement this did
that before by explicitly ignoring errno and instead relying in the
"expected overflow/underflow" values to detect the cases we care for
(which is valid UID numbers that are most likely to be uint32_t) and
which was the same thing we got from my original (but hated) atoi.

> > We could side-step all of this, of course, if we simply did this:
> >
> >       euid = getuid();
> >       if (euid == ROOT_UID)
> >               euid = git_env_ulong("SUDO_UID", euid);
>
> That's a nice suggestion, I didn't know that function existed. It means
> we would die() if we could not parse SUDO_UID which I think is
> reasonable (we'd also accept a units suffix an the uid)

which is also why we can't use it, any possibly bogus or suspicious
value we get from SUDO_UID MUST be ignored.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-06 19:15                 ` Carlo Arenas
@ 2022-05-06 20:00                   ` Junio C Hamano
  2022-05-06 20:22                     ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-06 20:00 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: phillip.wood, Johannes Schindelin, git, bagasdotme, Guy Maurel,
	SZEDER Gábor, Randall Becker

Carlo Arenas <carenas@gmail.com> writes:

> which is also why we can't use it, any possibly bogus or suspicious
> value we get from SUDO_UID MUST be ignored.

I do not think I agree.  If we have strange value in SUDO_UID, it
would be much better and safer to err on the safe side.  

Instead of ignoring, in the situation where we care about the value
we read from SUDO_UID (i.e. when euid==0), we should die loudly when
it has a strange value.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-05 14:01             ` Johannes Schindelin
  2022-05-05 14:32               ` Phillip Wood
  2022-05-05 16:09               ` Junio C Hamano
@ 2022-05-06 20:02               ` Carlo Arenas
  2 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 20:02 UTC (permalink / raw)
  To: Johannes Schindelin
  Cc: git, gitster, bagasdotme, phillip.wood123, Guy Maurel,
	SZEDER Gábor, Randall Becker

On Thu, May 5, 2022 at 7:01 AM Johannes Schindelin
<Johannes.Schindelin@gmx.de> wrote:
> On Mon, 2 May 2022, Carlo Marcelo Arenas Belón wrote:
> > bdc77d1d685 (Add a function to determine whether a path is owned by the
> > current user, 2022-03-02) checks for the effective uid of the running
> > process using geteuid() but didn't account for cases where that user was
> > root (because git was invoked through sudo or a compatible tool) and the
> > original uid that repository trusted for its config was no longer known,
> > therefore failing the following otherwise safe call:
> >
> >   guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
> >   [sudo] password for guy:
> >   fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)
> >
> > Attempt to detect those cases by using the environment variables that
> > those tools create to keep track of the original user id, and do the
> > ownership check using that instead.
> >
> > This assumes the environment the user is running on after going
> > privileged can't be tampered with, and also adds code to restrict that
> > the new behavior only applies if running as root, therefore keeping the
> > most common case, which runs unprivileged, from changing, but because of
> > that, it will miss cases where sudo (or an equivalent) was used to change
> > to another unprivileged user or where the equivalent tool used to raise
> > privileges didn't track the original id in a sudo compatible way.
>
> Hmm. I do realize that this is a quite common scenario, and I wish we
> would not need to rush for a fix here:

not sure what are you referring by "this", and I read the whole snip
just in case, but assuming is about the last paragraph

* sudo between unprivileged users is still safe because we only look
if we are running as root, my comment doesn't imply a regression
there, but just that the "feature" wouldn't work for them.
* doas is a common tool that is used sometimes as a sudo alternative
and I can see there might be even a version of it that would probably
provide a SUDO_UID for compatibility, once word goes out of how useful
that is for working with git, but until then only sudo is supported.

> Otherwise we could carefully design
> an "untrusted" mode in which Git errors out on spawning user-specified
> commands and on writing files (and avoids refreshing the index to avoid
> having to write a file), but runs normally if none of that is needed.

This seems like a useful feature to have, and would definitely make
this solution irrelevant, but this one is already implemented and I
don't see yet why there is concern, probably until "this" could be
clarified.

> > diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
> > index 6d764fe0ccf..ee558ced8c7 100644
> > --- a/Documentation/config/safe.txt
> > +++ b/Documentation/config/safe.txt
> > @@ -26,3 +26,12 @@ 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.
> > ++
> > +When git tries to check for ownership of git repositories, it will
> > +obviously do so with the uid of the user that is running git itself,
> > +but if git is running as root, it will check first if it might have
> > +been started through `sudo`, and if that is the case, will instead
> > +use the uid of the user that did so.
> > +If that is not what you would prefer and want git to only trust
> > +repositories that are owned by root instead, then you should remove
> > +the `SUDO_UID` variable from root's environment.
> > diff --git a/git-compat-util.h b/git-compat-util.h
> > index 63ba89dd31d..dfdd3e4f81a 100644
> > --- a/git-compat-util.h
> > +++ b/git-compat-util.h
> > @@ -393,12 +393,50 @@ 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
>
> I do wonder whether we have to play this kind of fragile game. Why not
> simply respect `SUDO_UID` if it is set? It's not like we expect attackers
> to have control over the environment and could set this maliciously.

The problem is that it indeed would lower the bar on how this feature
might weaken the current protections.

Getting an environment variable set "maliciously" is not that hard
with some social engineering, so making sure only root would have that
escape hatch, and knowing that there is a way to infer from the
environment what the relevant user is, is a powerful way to solve this
regression without making the protections weaker.

My original implementation did not use SUDO_UID but the owner of the
pty as that is harder to fake, and therefore safer even for non root
users, but it makes it much more intrusive for the same reason.

If your concern is the introduced regression where `sudo git` will no
longer work without adding a safe.directory exception or removing
SUDO_UID from the environment (or another of the workaround documented
in the test case), I would actually argue that it instead increases
the security of the original solution by implementing a mechanism that
user can use to communicate their intention and that would prefer the
lower privilege by default.

Still I am considering it as a "known bug" which will hopefully be
fixed soon, since Junio already provided the code to improve this one
so that a root user can access both repositories owned by them and by
their SUDO_UID.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-06 20:00                   ` Junio C Hamano
@ 2022-05-06 20:22                     ` Carlo Arenas
  2022-05-06 20:59                       ` Junio C Hamano
  2022-05-06 21:07                       ` rsbecker
  0 siblings, 2 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 20:22 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: phillip.wood, Johannes Schindelin, git, bagasdotme, Guy Maurel,
	SZEDER Gábor, Randall Becker

On Fri, May 6, 2022 at 1:00 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Arenas <carenas@gmail.com> writes:
>
> > which is also why we can't use it, any possibly bogus or suspicious
> > value we get from SUDO_UID MUST be ignored.
>
> I do not think I agree.  If we have a strange value in SUDO_UID, it
> would be much better and safer to err on the safe side.

ignoring it is the safe side; for example if we replace the current
function with the proposed one then some user lucky enough to have
access to the latest linux supercomputer that has been patched to have
a 64-bit uid_t (because who makes 32-bit supercomputers nowadays)
would get root[1] access by simply faking his SUDO_UID to be UINT_MAX
+ 1.

We will also honour probably SUDO_UID=0M as root instead of the
current action which is to ignore that nonsense and most likely die by
telling the pranker that he still can't run `git status` on that root
owned repository he got access to even after he managed to get sudo to
generate that as a SUDO_UID.

> Instead of ignoring, in the situation where we care about the value
> we read from SUDO_UID (i.e. when euid==0), we should die loudly when
> it has a strange value.

that is fair, but then it would then make this feature into a denial
of service attack target ;)

The current implementation instead keeps git running under the UID it
was started as, which should be root if it gets to use this code under
the current implementation.

I am still open to changing it if you would rather let git be the last
line of defense, I just think that the current implementation of
ignoring it is more user friendly and better at punking would be
attackers.

Carlo

[1] https://lwn.net/Articles/727490/

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-06 20:22                     ` Carlo Arenas
@ 2022-05-06 20:59                       ` Junio C Hamano
  2022-05-06 21:40                         ` Carlo Arenas
  2022-05-06 21:07                       ` rsbecker
  1 sibling, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-06 20:59 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: phillip.wood, Johannes Schindelin, git, bagasdotme, Guy Maurel,
	SZEDER Gábor, Randall Becker

Carlo Arenas <carenas@gmail.com> writes:

> On Fri, May 6, 2022 at 1:00 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Carlo Arenas <carenas@gmail.com> writes:
>>
>> > which is also why we can't use it, any possibly bogus or suspicious
>> > value we get from SUDO_UID MUST be ignored.
>>
>> I do not think I agree.  If we have a strange value in SUDO_UID, it
>> would be much better and safer to err on the safe side.
>
> ignoring it is the safe side; for example if we replace the current
> function with the proposed one then some user lucky enough to have
> access to the latest linux supercomputer that has been patched to have
> a 64-bit uid_t (because who makes 32-bit supercomputers nowadays)
> would get root[1] access by simply faking his SUDO_UID to be UINT_MAX
> + 1.

Since we do not pay attention to SUDO_UID unless euid is root,
anybody who can attack by faking SUDO_UID to affect what Git does
can already become root on the box.  So such an attacker would
already have root access without our help, or they would not.

In any case, if we notice that SUDO_UID is not a valid number and
die(), we deny the access anyway, so there is no need to write more
code to ignore.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-05 19:39                 ` Junio C Hamano
@ 2022-05-06 21:03                   ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 21:03 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Johannes Schindelin, git, bagasdotme, phillip.wood123, SZEDER Gábor

On Thu, May 5, 2022 at 12:39 PM Junio C Hamano <gitster@pobox.com> wrote:
> So I am not so optimistic that we
> can invent a GIT_TEST_* knob as good as ASSUME_DIFFERENT_OWNER for
> that.

the only option I can think of (if the pain point is running git
through sudo is just too cumbersome) AND we don't want to weaken our
implementation by allowing the SUDO_UID escape hatch to non-root would
be to still use sudo to change the ownership of the git binaries we
are testing with to root and SUID them.

but at that point we are likely to deal with similar platform specific
issues for why running git as root is still problematic regardless,
and for whatever reason I feel even more compelled to ever run that
script in my workstation since at least with the current
implementation I know exactly which commands are running as root.  It
also makes this functionality slightly more dangerous since it will be
included as part of the production binaries as you pointed out.

My hope to broaden its visibility was to instead (since this was
mainly meant to be a CI only test as explained[1] originally) was to
add to our CI setup ways to fix the agents sudoers configuration to
fit what we need, but I won't do that now, and will probably wait for
a while until the on the fly CI changes settle.

Carlo

[1] https://lore.kernel.org/git/CAPUEspitAQrEjMpUyw8e2pyT1MT+h_dO5wSU0wWDWTqSye5TwA@mail.gmail.com/

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-06 20:22                     ` Carlo Arenas
  2022-05-06 20:59                       ` Junio C Hamano
@ 2022-05-06 21:07                       ` rsbecker
  1 sibling, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-05-06 21:07 UTC (permalink / raw)
  To: 'Carlo Arenas', 'Junio C Hamano'
  Cc: phillip.wood, 'Johannes Schindelin',
	git, bagasdotme, 'Guy Maurel',
	'SZEDER Gábor'

On May 6, 2022 4:23 PM, Carlo Arenas wrote:
>On Fri, May 6, 2022 at 1:00 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Carlo Arenas <carenas@gmail.com> writes:
>>
>> > which is also why we can't use it, any possibly bogus or suspicious
>> > value we get from SUDO_UID MUST be ignored.
>>
>> I do not think I agree.  If we have a strange value in SUDO_UID, it
>> would be much better and safer to err on the safe side.
>
>ignoring it is the safe side; for example if we replace the current function with the
>proposed one then some user lucky enough to have access to the latest linux
>supercomputer that has been patched to have a 64-bit uid_t (because who makes
>32-bit supercomputers nowadays) would get root[1] access by simply faking his
>SUDO_UID to be UINT_MAX
>+ 1.
>
>We will also honour probably SUDO_UID=0M as root instead of the current action
>which is to ignore that nonsense and most likely die by telling the pranker that he
>still can't run `git status` on that root owned repository he got access to even after
>he managed to get sudo to generate that as a SUDO_UID.
>
>> Instead of ignoring, in the situation where we care about the value we
>> read from SUDO_UID (i.e. when euid==0), we should die loudly when it
>> has a strange value.
>
>that is fair, but then it would then make this feature into a denial of service attack
>target ;)
>
>The current implementation instead keeps git running under the UID it was
>started as, which should be root if it gets to use this code under the current
>implementation.
>
>I am still open to changing it if you would rather let git be the last line of defense, I
>just think that the current implementation of ignoring it is more user friendly and
>better at punking would be attackers.

Please keep in mind the uid_t == 65535 on __TANDEM. uid_t == 0 actually means "not logged in".

Thanks,
Randall


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-06 20:59                       ` Junio C Hamano
@ 2022-05-06 21:40                         ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 21:40 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: phillip.wood, Johannes Schindelin, git, bagasdotme, Guy Maurel,
	SZEDER Gábor, Randall Becker

On Fri, May 6, 2022 at 2:00 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Arenas <carenas@gmail.com> writes:
>
> > On Fri, May 6, 2022 at 1:00 PM Junio C Hamano <gitster@pobox.com> wrote:
> >>
> >> Carlo Arenas <carenas@gmail.com> writes:
> >>
> >> > which is also why we can't use it, any possibly bogus or suspicious
> >> > value we get from SUDO_UID MUST be ignored.
> >>
> >> I do not think I agree.  If we have a strange value in SUDO_UID, it
> >> would be much better and safer to err on the safe side.
> >
> > ignoring it is the safe side; for example if we replace the current
> > function with the proposed one then some user lucky enough to have
> > access to the latest linux supercomputer that has been patched to have
> > a 64-bit uid_t (because who makes 32-bit supercomputers nowadays)
> > would get root[1] access by simply faking his SUDO_UID to be UINT_MAX
> > + 1.
>
> Since we do not pay attention to SUDO_UID unless euid is root,
> anybody who can attack by faking SUDO_UID to affect what Git does
> can already become root on the box.

Normally yes, but they just might be too clever and found instead a way to
fool sudo into generating a bogus SUDO_UID with the hopes that whoever
implemented this forgot to check for bogus values and used atoi() and
therefore grants them uid = 0 for whatever nefarious objective they
might have.

While we can't protect against a buggy sudo, we can't do the next best
thing and that is to NOT let a would-be attacker still get their 5 min
of fame by reporting a vulnerability in git, because they just made it
crash through sudo.

Especially when the impact of just ignoring them and going our merry
way is that (with the current code) we will use the euid of the user
that started sudo and "do the right thing" (tm).

> In any case, if we notice that SUDO_UID is not a valid number and
> die(), we deny the access anyway, so there is no need to write more
> code to ignore.

not sure which additional code you are referring to here, but
currently the only code to ignore is embedded in the one that is
needed to read the value, so it is not additional, except maybe for
the (uid_t)-1 check that was discussed before[1] and that is what
protects us from overflows when sizeof(long) > sizeof(uid_t) &&
UID_T_MIN >= 0, which are likely most of the systems we are expected
to run on.

if you are referring to the whole function that could be replaced by
using instead git_env_ulong() as dscho suggested, additional code
would be needed anyway to make sure that we truncate it appropriately
as what we really need is a git_env_uint32() which I am really glad to
add if you would rather go that way, but is definitely going to
require more code than what we have right now.

Note also that we will need to add a flag to it, so it doesn't try to
be helpful and allow SUDO_UID=0M to sneak in.

Carlo

[1] https://lore.kernel.org/git/CAPUEsphFb-=BcV-mxS5RZpJQ8UVq23ni0Lo8tQ4J3TP04B4KQg@mail.gmail.com/

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-05-06 17:50                 ` Carlo Arenas
@ 2022-05-06 21:43                   ` Junio C Hamano
  2022-05-06 22:57                     ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-06 21:43 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: Phillip Wood, git

Carlo Arenas <carenas@gmail.com> writes:

> Since I am renaming it anyway as part of this topic with RFC v4, would
> it be a good idea to require both?
>
> I see the "IKNOWHATIAMDOING" not as a normal GIT_TEST flag, but as a
> "here be dragons!" warning, and I later found that I either
> misremembered it being enabled in the CI, or it was dropped with one
> of those refactors we do often there.
>
> My RFC v4 includes a new nice looking GIT_TEST variable as suggested
> by Phillip which I am also enabling in the CI to hopefully make it
> even more clear that this is only meant to run there, but sadly that
> also means that this patch will likely have a conflict when merged
> upwards.

This must build from the older mainteance tracks like maint-2.30, so
let's keep the changes to absolute minimum, especially since that
will become the base for any further usability tweaks (in an earlier
round you suggested to cover "doas", and other changes may want to
be applied but all of them should be deferred to later changes).

I actually think 1/3 and 3/3 are OK.  Are there remaining issues in
these two patches (which only are tests)?

As to 2/3, I think the code is basically already fine, but a
simplification like the following on top would be a good idea.

 * The callers do not care how errno is modified by the call made
   into extract_id_from_env(); we are potentially clobbering errno
   by calling getenv(), lstat(), geteuid(), etc, and we have no
   "preserve errno as the caller had" around them.  So let's lose
   the saved_errno thing.

 * We clear errno before making strtoul() call, so any non-zero
   errno must have happeneed in strtoul(), which includes ERANGE.
   There is no point chekcing the returned value env_id; if it is
   ULONG_MAX but errno is 0, then the SUDO_UID legitimately is
   naming a user whose UID is that special value, and it is not an
   indication of an overflow.

With the change squashed in, [2/3] can have 

Reviewed-by: Junio C Hamano <gitster@pobox.com>

Thanks.

 git-compat-util.h | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git c/git-compat-util.h w/git-compat-util.h
index dfdd3e4f81..43c9cd0b48 100644
--- c/git-compat-util.h
+++ w/git-compat-util.h
@@ -413,14 +413,11 @@ static inline void extract_id_from_env(const char *env, uid_t *id)
 	if (real_uid && *real_uid) {
 		char *endptr;
 		unsigned long env_id;
-		int saved_errno = errno;
 
 		errno = 0;
 		env_id = strtoul(real_uid, &endptr, 10);
-		if (!errno && !*endptr && env_id <= (uid_t)-1)
+		if (!*endptr && !errno)
 			*id = env_id;
-
-		errno = saved_errno;
 	}
 }
 

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-05-06 21:43                   ` Junio C Hamano
@ 2022-05-06 22:57                     ` Carlo Arenas
  2022-05-06 23:55                       ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-06 22:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Phillip Wood, git

On Fri, May 6, 2022 at 2:43 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Arenas <carenas@gmail.com> writes:
>
> > Since I am renaming it anyway as part of this topic with RFC v4, would
> > it be a good idea to require both?
> >
> > I see the "IKNOWHATIAMDOING" not as a normal GIT_TEST flag, but as a
> > "here be dragons!" warning, and I later found that I either
> > misremembered it being enabled in the CI, or it was dropped with one
> > of those refactors we do often there.
> >
> > My RFC v4 includes a new nice looking GIT_TEST variable as suggested
> > by Phillip which I am also enabling in the CI to hopefully make it
> > even more clear that this is only meant to run there, but sadly that
> > also means that this patch will likely have a conflict when merged
> > upwards.
>
> This must build from the older mainteance tracks like maint-2.30, so
> let's keep the changes to absolute minimum

makes sense, but I still unsure about the two questions I had above:

* would it make sense to make it run ONLY if both variables are set to
YES or is it enough to use one (most likely the GIT_TEST one) that we
would enable independently in CI?

the advantage I see of having both variables is that it is even less
likely to even run accidentally in a desktop somewhere and maybe break
that test, and also keeps the meaning I wanted to attach to it with
that ugly looking flag that no one should ever try to enable in their
workstations unless they really know what they are doing.

The advantage of ONLY having the GIT_TEST one is that it will be
easier to enable in CI and for whoever wants to play with it on their
workstation as well, but might still encourage people trying to make
it work and wasting their time.

* since we have to enable CI for these to be useful, would that be
something to be done in an additional patch as part of this topic
branch and you will only pull the commits before it to maint to avoid
conflicts, or should it be done completely independently as a mini
feature branch that depends on this one that will be pulled to seen
and merged downwards from it?

somehow offtopic but just curious about your process, presume that if
we go with a single topic branch adding it instead as 2/4 would break
your flow/scripts since the only way to get that merged would be to
cherry-pick, right?

> I actually think 1/3 and 3/3 are OK.  Are there remaining issues in
> these two patches (which only are tests)?

The versions of them in RFCv4 have more documentation and are cleaner
since they mostly include most of the feedback that was collected on
them (even if I am still unsure because it is spread around and
difficult to track, hence why I was planning an RFC)

I don't think there is anything significantly different though but
they are and will need another review (which I am hoping would be
uncontroversial)

> As to 2/3, I think the code is basically already fine, but a
> simplification like the following on top would be a good idea.
>
>  * We clear errno before making strtoul() call, so any non-zero
>    errno must have happeneed in strtoul(), which includes ERANGE.
>    There is no point checking the returned value env_id; if it is
>    ULONG_MAX but errno is 0, then the SUDO_UID legitimately is
>    naming a user whose UID is that special value, and it is not an
>    indication of an overflow.

true, but the apparent check for ULONG_MAX (which should have a
comment added) was really a check not to overflow when assigning the
value we got into uid_t, by sacrificing an unlikely to be valid
ULONG_MAX as an uid.

it also has the intentional side effect of breaking this code if uid_t
is signed and hopefully triggering a warning in the process since it
would be always false, that way whoever has a system where this type
is signed will have to carry their own version of the code and we
don't have to deal with the portability of it.

lastly (since it really made me proud and would be sad to see it go)
it ALSO avoids someone trying to sneak a value that would overflow in
one of the most common configurations we will run where sizeof(long) >
sizeof(uid_t) && MIN_UID_T >=0, by using an equivalent to MAX_UID_T
(which only exists in a few systems and therefore can't be relied on)
to discard values that would overflow the range uid_t has.

without it, we would probably find ourselves in the future having to
deal with an embarrassing bug that others before us had suffered.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-05-06 22:57                     ` Carlo Arenas
@ 2022-05-06 23:55                       ` Junio C Hamano
  2022-05-07 11:57                         ` Carlo Marcelo Arenas Belón
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-06 23:55 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: Phillip Wood, git

Carlo Arenas <carenas@gmail.com> writes:

> makes sense, but I still unsure about the two questions I had above:
>
> * would it make sense to make it run ONLY if both variables are set to
> YES or is it enough to use one (most likely the GIT_TEST one) that we
> would enable independently in CI?
>
> the advantage I see of having both variables is that it is even less
> likely to even run accidentally in a desktop somewhere and maybe break
> that test, and also keeps the meaning I wanted to attach to it with
> that ugly looking flag that no one should ever try to enable in their
> workstations unless they really know what they are doing.
>
> The advantage of ONLY having the GIT_TEST one is that it will be
> easier to enable in CI and for whoever wants to play with it on their
> workstation as well, but might still encourage people trying to make
> it work and wasting their time.

Those who want to use it in CI would need to be told (or have to
figure it out) by the patch that adds either IKNOWWHATIAMDOING or
GIT_TEST_WHATEVER, and as long as the patch does its job well
enough, I do not see much difference either way.  The only possible
difference is if we use IKNOWWHATIAMDOING then a special CI job that
may run with tweaked /etc/sudoers would run the test in this series
*and* the other test we borrowed IKNOWWHATIAMDOING from, which may
not necessarily be what we want to do.

> * since we have to enable CI for these to be useful, would that be
> something to be done in an additional patch as part of this topic
> branch and you will only pull the commits before it to maint to avoid
> conflicts, or should it be done completely independently as a mini
> feature branch that depends on this one that will be pulled to seen
> and merged downwards from it?

I'd expect that nobody pays attention to GitHub CI runs on
maint-2.30 - maint-2.35 branches when I push out.  I am hoping that
these fixes are built on maint-2.30 _without_ CI integration (i.e.
the SUDO tests won't be run due to lack of IKNOWWHATIAMDOING in the
CI environment).

A single branch, without CI integration, based on maint-2.30 would
be prepared.  Let's call that cb/path-owner-check-with-sudo topic.

It is merged to another branch, based on v2.36.0.  Let's call that
cb/test-path-owner-check-with-sudo-in-ci.

On that latter branch, changes to CI to tweak /etc/sudoers and set
IKNOWWHATIAMDOING would be created.  That latter branch will
percolate down starting at 'seen', through 'next', 'master' and
finally to 'maint'.

After all that happens to prove the primary topic (sans CI) is
sound, the tip of maint-2.30 would be updated by merging it, i.e.
cb/path-owner-check-with-sudo, and then the result would be merged
to maint-2.31, ..., percolating upwards to maint-2.35.  The
resulting maint-2.35 may be merged to 'maint' after that but that
should become a "already up-to-date" merge, I would expect, because
'maint' by that time would have got cb/path-owner-check-with-sudo as
part of merging cb/test-path-owner-check-with-sudo-in-ci already,
and the merge of cb/path-owner-check-with-sudo is the only thing
'maint-2.35' has that hasn't merged to 'maint' at that point.

> true, but the apparent check for ULONG_MAX (which should have a
> comment added) was really a check not to overflow when assigning the
> value we got into uid_t, by sacrificing an unlikely to be valid
> ULONG_MAX as an uid.

Are you worried about uid_t wider than ulong?  strtoul() with !errno
test covers the case, doesn't it?  SUDO_UID cannot have any integer
that cannot be represented in uid_t and if strtoul() does not say
ERANGE, we know whatever value in SUID_UID did not overflow ulong.

> it ALSO avoids someone trying to sneak a value that would overflow in
> one of the most common configurations we will run where sizeof(long) >
> sizeof(uid_t) && MIN_UID_T >=0, by using an equivalent to MAX_UID_T

Sorry, -ECANNOTPARSE.  If strtoul() can parse everything in uid_t
then where is the room for overflowing?  We are trying to protect an
unsuspecting user who temporary has become 'root' via sudo, and not
somebody deliberately hurt themselves or others by setting SUDO_UID
deliberately to strange values (once you are 'root', you have easier
ways to hurt other people).

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v2 3/3] t: add tests for safe.directory when running with sudo
  2022-05-06 23:55                       ` Junio C Hamano
@ 2022-05-07 11:57                         ` Carlo Marcelo Arenas Belón
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-07 11:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Phillip Wood, git

On Fri, May 06, 2022 at 04:55:36PM -0700, Junio C Hamano wrote:
> Carlo Arenas <carenas@gmail.com> writes:
> 
> > true, but the apparent check for ULONG_MAX (which should have a
> > comment added) was really a check not to overflow when assigning the
> > value we got into uid_t, by sacrificing an unlikely to be valid
> > ULONG_MAX as an uid.
> 
> Are you worried about uid_t wider than ulong?  strtoul() with !errno
> test covers the case, doesn't it?

No, I am worried about uid_t narrower than ulong, which is also the
most likely scenatio with a typeof(uid_t) == uint32_t

> SUDO_UID cannot have any integer
> that cannot be represented in uid_t and if strtoul() does not say
> ERANGE, we know whatever value in SUID_UID did not overflow ulong.

It is a little subtle, but strtoul doesn't warrant always an ERANGE
because it tries to be helpful when given a negative integer and returns
instead an equivalent unsigned long as per spec[1] (or in this case the
commentary from OpenBSD man page which is also easier to link)

"If the minus sign was part of the input sequence, the numeric value calculated from the sequence of digits is negated as if by unary minus in the result type, which applies unsigned integer wraparound rules."

> > it ALSO avoids someone trying to sneak a value that would overflow in
> > one of the most common configurations we will run where sizeof(long) >
> > sizeof(uid_t) && MIN_UID_T >=0, by using an equivalent to MAX_UID_T
> 
> Sorry, -ECANNOTPARSE.  If strtoul() can parse everything in uid_t
> then where is the room for overflowing?

So lets assume a 32bit unsigned uid_t, that wraparounds at 2^32+1, if we
get a negative value that is equivalent to something bigger than it, or
even a positive value bigger than it, then the assignment will overflow
unless we keep it in check by that obviously too clever condition that
was removed and we MIGHT even assume an uid_t of 0, which is embarrasing[0].

> We are trying to protect an
> unsuspecting user who temporary has become 'root' via sudo, and not
> somebody deliberately hurt themselves or others by setting SUDO_UID
> deliberately to strange values (once you are 'root', you have easier
> ways to hurt other people).

You are correct for the current code that even has a big warning telling
people NEVER to run that function for anyone other than root, but who
knows how this will evolve in the future.

Removing it also has other sideeffect, like making this code work in
incorrect ways if uid_t is signed, which I mentioned before but probably
should had been added as a comment, but that was part of the requirements[2]
we had when Phillip argued correctly that I was restricting the valid uid
to only half was possible in 32bit systems.

FWIW, sudo prints the uid using "%u" so using unsigned long makes more
sense and all these problems are unlikely to be practical issues now so
I am ok taking your code if you insist, but I still think that the original
one was safer in case things change in the future or if there is a platform
we currently run on with has signed uid_t, so I will keep it in the RFC with
hopefully enough comments to convince you.

Carlo

[0] https://github.com/systemd/systemd/issues/11026
[1] https://man.openbsd.org/strtoul.3
[2] https://lore.kernel.org/git/CAPUEspjoTYtv9K=rvpkFnyGnEz_uxefED820rx09b6qGG93SqA@mail.gmail.com/

^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v4 0/3] fix `sudo make install` regression in maint
  2022-05-03  6:54         ` [PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
                             ` (2 preceding siblings ...)
  2022-05-03  6:54           ` [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
@ 2022-05-07 16:35           ` Carlo Marcelo Arenas Belón
  2022-05-07 16:35             ` [RFC PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
                               ` (4 more replies)
  3 siblings, 5 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-07 16:35 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón

A reroll for cb/path-owner-check-with-sudo with most of the suggestions
included but planned originally as an RFC, because I am frankly not sure
that I read and addressed all of it, but also because after seeing how
my only chance to get an official Reviewed-by: Junio vanished I also
realized that I wasn't been clear enough and careful enough from the
beginning to explain the code correctly and therefore had maybe wasted
more of the review quota this change should require.

Important changes (eventhough most are not affecting the logic)
* Document hopefully better which environments are supported and what
  to do if you want to test it in one that is not (thanks to dscho)
* Removed the arguably premature optimization to try to keep the sudo
  cache warm which was actually buggy, as it was also not needed.
  The CI does passwordless sudo unconditionally and even when running
  it locally it will go so fast that is shouldn't be an issue (thanks
  to Philip)
* No longer using the ugly and controversial variable name so now it
  will need GIT_TEST_ENABLE_SUDO to be used to enable it on CI (which
  is not done in this series, but a run with it enabled on top of
  seen is available[1])
* Stop the arguing about what is or not a regression worth fixing and
  instead document it as one through a test, which would be easy to
  fix in a follow up since the code was already provided by Junio

Lastly I am little concerned by the fact this is going to maint but
has a "weather balloon" of sorts, which might not be wise, since it
might prevent people that might be affected from upgrading if they
have a -Werror policy.

The effect is minor though, as worst case, if someone has a system
with a signed uid_t then this "feature" wouldn't work for them and
nothing has changed but I think it is worth to consider the alternatives
which are (in my own order of preference)

* Revert the change to use unsigned long and strtoul()

  This will mean that people running in a 32bit system with an uid bigger
  than INT_MAX wouldn't be able to use the feature

* Move the code out (which is indeed an artificial restriction) so that
  we can use intmax_t and strtoimax() instead and a cast to compare the
  uid_t.

  This avoids all issues and restrictions but means more code changes

* Throw away the custom function and expand the API ones to be used
  instead as dscho suggested.

  Even more code changes, but maybe less risk as we will be building
  on top of battle tested code.

[1] https://github.com/carenas/git/actions/runs/2286452160

Carlo Marcelo Arenas Belón (3):
  t: regression git needs safe.directory when using sudo
  git-compat-util: avoid failing dir ownership checks if running
    privileged
  t0034: add negative tests and allow git init to mostly work under sudo

 Documentation/config/safe.txt  |  10 ++++
 git-compat-util.h              |  49 +++++++++++++++-
 t/lib-sudo.sh                  |  12 ++++
 t/t0034-root-safe-directory.sh | 103 +++++++++++++++++++++++++++++++++
 4 files changed, 173 insertions(+), 1 deletion(-)
 create mode 100644 t/lib-sudo.sh
 create mode 100755 t/t0034-root-safe-directory.sh

-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v4 1/3] t: regression git needs safe.directory when using sudo
  2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
@ 2022-05-07 16:35             ` Carlo Marcelo Arenas Belón
  2022-05-07 16:35             ` [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
                               ` (3 subsequent siblings)
  4 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-07 16:35 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón, SZEDER Gábor

Originally reported after release of v2.35.2 (and other maint branches)
for CVE-2022-24765 and blocking otherwise harmless commands that were
done using sudo in a repository that was owned by the user.

Add a new test script with very basic support to allow running git
commands through sudo, so a reproduction could be implemented and that
uses only `git status` as a proxy of the issue reported.

Note that because of the way sudo interacts with the system, a much
more complete integration with the test framework will require a lot
more work and that was therefore intentionally punted for now.

The current implementation requires the execution of a special cleanup
function which should always be kept as the last "test" or otherwise
the standard cleanup functions will fail because they can't remove
the root owned directories that are used.  This also means that if
failures are found while running, the specifics of the failure might
not be kept for further debugging and if the test was interrupted, it
will be necessary to clean the working directory manually before
restarting by running:

  $ sudo rm -rf trash\ directory.t0034-root-safe-directory/

The test file also uses at least one initial "setup" test that creates
a parallel execution directory, while ignoring the repository created
by the test framework, and special care should be taken when invoking
commands through sudo, since the environment is otherwise independent
from what the test framework expects.  Indeed `git status` was used
as a proxy because it doesn't even require commits in the repository
to work.

A new SUDO prerequisite is provided that does some sanity checking
to make sure the sudo command that will be used allows for passwordless
execution as root without restrictions and doesn't mess with git's
execution path.  This matches what is provided by the macOS agents that
are used as part of GitHub actions and probably nowhere else.

Most of those characteristics make this test mostly suitable only for
CI, but it might be executed locally if special care is taken to provide
for some of them in the local configuration and maybe making use of the
sudo credential cache by first invoking sudo, entering your password if
needed, and then invoking the test with:

  $ GIT_TEST_ALLOW_SUDO=YES ./t0034-root-safe-directory.sh

If it fails to run, then it means your local setup wouldn't work for the
test and things that might help is to comment out sudo's secure_path config
and make sure your account has similar privileges than what the CI
provides (for example an entry in /etc/sudoers for the user marta like)

  marta	ALL=(ALL:ALL) NOPASSWD: ALL

Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/t0034-root-safe-directory.sh | 45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 0000000000..2e4492a66d
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.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
+
+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
+'
+
+test_expect_success SUDO 'setup' '
+	sudo rm -rf root &&
+	mkdir -p root/r &&
+	sudo chown root root &&
+	(
+		cd root/r &&
+		git init
+	)
+'
+
+test_expect_failure SUDO 'sudo git status as original owner' '
+	(
+		cd root/r &&
+		git status &&
+		sudo git status
+	)
+'
+
+# this MUST be always the last test
+test_expect_success SUDO 'cleanup' '
+	sudo rm -rf root
+'
+
+test_done
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-07 16:35             ` [RFC PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-07 16:35             ` Carlo Marcelo Arenas Belón
  2022-05-07 17:34               ` Junio C Hamano
  2022-05-07 16:35             ` [RFC PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
                               ` (2 subsequent siblings)
  4 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-07 16:35 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón, Guy Maurel, SZEDER Gábor,
	Randall Becker

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that user was
root (because git was invoked through sudo or a compatible tool) and the
original uid that repository trusted for its config was no longer known,
therefore failing the following otherwise safe call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using that instead.

This assumes the environment the user is running on after going
privileged can't be tampered with, and also adds code to restrict that
the new behavior only applies if running as root, therefore keeping the
most common case, which runs unprivileged, from changing, but because of
that, it will miss cases where sudo (or an equivalent) was used to change
to another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Because of compatibility with sudo, the code assumes that uid_t is an
unsigned integer type, but adds additional logic to protect itself
against possibly malicious ids outside the expected range and ignore
them.

A warning should be generated if uid_t is signed and the code would
need to be locally patched to work correctly, but this is also a
weather balloon of sorts so we will then now which systems those are
and whether we should accommodate for their portability in our codebase.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config/safe.txt  | 10 +++++++
 git-compat-util.h              | 49 +++++++++++++++++++++++++++++++++-
 t/t0034-root-safe-directory.sh |  2 +-
 3 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 6d764fe0cc..a6b81f6cfc 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -26,3 +26,13 @@ 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.
++
+When git tries to check for ownership of git repositories, it will
+obviously do so with the uid of the user that is running git itself,
+but if git is running as root, in a platform that provides sudo and is
+not Windows, it will check first if it might have been started through
+it, and if that is the case, will instead use the uid of the user that
+did invoke that instead.
+If that is not what you would prefer and want git to only trust
+repositories that are owned by root instead, then you should remove
+the `SUDO_UID` variable from root's environment.
diff --git a/git-compat-util.h b/git-compat-util.h
index 63ba89dd31..409df99463 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -393,12 +393,59 @@ 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
+
+/*
+ * this helper function overrides a ROOT_UID with the one provided by
+ * an environment variable, do not use unless the original user is
+ * root
+ * WARNING: this function assumes uid_t is unsigned, if you got here
+ *          because of a warning or a bug will need a patch and would
+ *          be nice if you let us know
+ */
+static inline void extract_id_from_env(const char *env, uid_t *id)
+{
+	const char *real_uid = getenv(env);
+
+	/* discard any empty values */
+	if (real_uid && *real_uid) {
+		char *endptr = NULL;
+		unsigned long env_id;
+
+		errno = 0;
+		env_id = strtoul(real_uid, &endptr, 10);
+		/*
+		 * env_id could underflow/overflow in the previous call
+		 * and if it will still fit in a long it will not report
+		 * it as error with ERANGE, instead silently using an
+		 * equivalent positive number that might be bogus.
+		 * if uid_t is narrower than long, it might not fit,
+		 * hence why we  need to check it against the maximum
+		 * possible uid_t value before accepting it.
+		 */
+		if (!*endptr && !errno && env_id <= (uid_t)-1)
+			*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/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index 2e4492a66d..ecd9dca6b3 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -29,7 +29,7 @@ test_expect_success SUDO 'setup' '
 	)
 '
 
-test_expect_failure SUDO 'sudo git status as original owner' '
+test_expect_success SUDO 'sudo git status as original owner' '
 	(
 		cd root/r &&
 		git status &&
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [RFC PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-07 16:35             ` [RFC PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
  2022-05-07 16:35             ` [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-07 16:35             ` Carlo Marcelo Arenas Belón
  2022-05-10 14:17             ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Phillip Wood
  2022-05-10 17:46             ` [PATCH " Carlo Marcelo Arenas Belón
  4 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-07 16:35 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón

Add a support library that provides one function that can be used
to run a "scriplet" of commands through sudo and that helps invoking
sudo in the slightly awkward way that is required to ensure it doesn't
block the call (if shell was allowed as tested in the prerequisite)
and it doesn't run the command through a different shell than the one
we intended.

Add additional negative tests as suggested by Junio and that use a
new workspace that is owned by root.

Note that the specific test that documents that after the previous
changes, it is no longer possible for root (if obtained through sudo)
to NOT add an exception or need a "workaround" to be able to run git
commands in a repository owned by thyself, is marked as a regression
and is expected to be fixed with a future change, which hasn't been
provided yet and that is not part of this series.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/lib-sudo.sh                  | 12 +++++++
 t/t0034-root-safe-directory.sh | 58 ++++++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+)
 create mode 100644 t/lib-sudo.sh

diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
new file mode 100644
index 0000000000..d8a88fb9db
--- /dev/null
+++ b/t/lib-sudo.sh
@@ -0,0 +1,12 @@
+# 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"
+	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
index ecd9dca6b3..5bc416ab81 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -3,6 +3,7 @@
 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
@@ -10,6 +11,12 @@ then
 	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 &&
@@ -19,6 +26,12 @@ test_lazy_prereq SUDO '
 	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 &&
@@ -37,6 +50,51 @@ test_expect_success SUDO 'sudo git status as original owner' '
 	)
 '
 
+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_failure SUDO 'can access with sudo if root' '
+	(
+		cd root/p &&
+		sudo git status
+	)
+'
+
+test_expect_success SUDO 'can access with sudo using a workaround' '
+	# run sudo twice; would fail is root is not in the sudoers
+	(
+		cd root/p &&
+		sudo sudo git status
+	) &&
+	# provide explicit GIT_DIR
+	(
+		cd root/p &&
+		run_with_sudo <<-END
+			GIT_DIR=.git &&
+			GIT_WORK_TREE=. &&
+			export GIT_DIR GIT_WORK_TREE &&
+			git status
+		END
+	) &&
+	# discard 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
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-07 16:35             ` [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-07 17:34               ` Junio C Hamano
  2022-05-07 18:56                 ` Carlo Marcelo Arenas Belón
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-07 17:34 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> +		/*
> +		 * env_id could underflow/overflow in the previous call
> +		 * and if it will still fit in a long it will not report
> +		 * it as error with ERANGE, instead silently using an
> +		 * equivalent positive number that might be bogus.
> +		 * if uid_t is narrower than long, it might not fit,
> +		 * hence why we  need to check it against the maximum
> +		 * possible uid_t value before accepting it.
> +		 */
> +		if (!*endptr && !errno && env_id <= (uid_t)-1)
> +			*id = env_id;

Thanks for very clearly spelling out why you care.  It makes it much
easier to explain why I disagree with the line of reasoning ;-)

This code may be exercised by a potential attacker, but we know that
the codepath is entered only when euid==ROOT_UID.  The attacker may
or may not have used 'sudo', and we cannot trust the value of
SUDO_UID at all.  But that is OK.  If the attacker already is root
on the box, they do not have to use "git" or exercise this new code
in order to attack anybody on the box already.  This requires us to
exclude social engineering attack to tell a victim to run "sudo",
set SUDO_UID to a specific value, and run something, but at last I
have been excluding that from the beginning.  There are easier
things you can tell the potential victim to cause harm while being
root.

Now the whole point of adding this new code to _weaken_ the existing
check is to help legitimate users who are authorised to become root
via "sudo" on the box.  Making it easier for them to use "git" while
tentatively gaining root priviledge so that they can do "make
install" in a repository they own.

We know that this code is meant to be exercised after a potential
victim gained euid==ROOT_UID via 'sudo', and SUDO_UID is exported by
the command for the original user.  If uid_t is narrower than ulong
(e.g. 16-bit uid_t vs 64-bit ulong), and if it is unsigned, the only
effect the extra check is doing is to exclude the unfortunate user
with uid==65535 from using "sudo git describe".

In exchange, the only attack scenario the code prevents is this,
IIUC.

 * You, the aspiring cracker, are a user not allowed to run "sudo" on
   the box, and you know your uid is 1000

 * You look for another user, a potential victim, whose uid is 1000
   modulo 65536 (if your uid_t is 16-bit) and who can run "sudo" on
   the box.

 * You prepare a malicious repository, invite that user there and
   ask them to run "sudo something" there.

I'd say such an attack vector is not likely, and a user with maximum
allowed uid_t value is equally not that likely, so I do not care too
deeply either way---and in such a case, I do prefer a simpler code.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-07 17:34               ` Junio C Hamano
@ 2022-05-07 18:56                 ` Carlo Marcelo Arenas Belón
  2022-05-09 16:54                   ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-07 18:56 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

On Sat, May 07, 2022 at 10:34:44AM -0700, Junio C Hamano wrote:
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
> 
> > +		/*
> > +		 * env_id could underflow/overflow in the previous call
> > +		 * and if it will still fit in a long it will not report
> > +		 * it as error with ERANGE, instead silently using an
> > +		 * equivalent positive number that might be bogus.
> > +		 * if uid_t is narrower than long, it might not fit,
> > +		 * hence why we  need to check it against the maximum
> > +		 * possible uid_t value before accepting it.
> > +		 */
> > +		if (!*endptr && !errno && env_id <= (uid_t)-1)
> > +			*id = env_id;
> 
> Thanks for very clearly spelling out why you care.  It makes it much
> easier to explain why I disagree with the line of reasoning ;-)

Funny enough the comment doesn't even scratch the surface on the genius
of that (uid_t)-1 and how it also prevents people with a signed uid_t
to misuse this code as well, while still allowing uid_t > INT_MAX.

As mentioned in the cover letter, the code without this is indeed broken
and unsafe to use in that case, so we will need to do something else (as
spelled there as well), I we were to proceed with it, which as I said
before I am totally OK with (granted we do that something else, and
promess not to misuse this somewhere later and open us to a potentially
embarrasing bug.

> This code may be exercised by a potential attacker, but we know that
> the codepath is entered only when euid==ROOT_UID.  The attacker may
> or may not have used 'sudo', and we cannot trust the value of
> SUDO_UID at all.  But that is OK.  If the attacker already is root
> on the box, they do not have to use "git" or exercise this new code
> in order to attack anybody on the box already.  This requires us to
> exclude social engineering attack to tell a victim to run "sudo",
> set SUDO_UID to a specific value, and run something, but at last I
> have been excluding that from the beginning.  There are easier
> things you can tell the potential victim to cause harm while being
> root.

Agree, but even if I put a scary looking warning and tried to make this
code harder to use than it needs to be, is fairly visible and there had
been already suggestions of removing that restriction.

Which is why I said this is more a defensive programming solution than
real protection under the current constrains.

> Now the whole point of adding this new code to _weaken_ the existing
> check is to help legitimate users who are authorised to become root
> via "sudo" on the box.  Making it easier for them to use "git" while
> tentatively gaining root priviledge so that they can do "make
> install" in a repository they own.
> 
> We know that this code is meant to be exercised after a potential
> victim gained euid==ROOT_UID via 'sudo', and SUDO_UID is exported by
> the command for the original user.  If uid_t is narrower than ulong
> (e.g. 16-bit uid_t vs 64-bit ulong), and if it is unsigned, the only
> effect the extra check is doing is to exclude the unfortunate user
> with uid==65535 from using "sudo git describe".

not quite, the check does "<=" so excludes no legitimate users, what
is excludes is bit multiplers of those valid users ids to work, so
if an obviously impossible to get legitimately and therefore bogus if
uid_t is 16bit value of 65536 even gets here we will not assume it was
root instead, which I would find personally embarrasing.

> In exchange, the only attack scenario the code prevents is this,
> IIUC.
> 
>  * You, the aspiring cracker, are a user not allowed to run "sudo" on
>    the box, and you know your uid is 1000
> 
>  * You look for another user, a potential victim, whose uid is 1000
>    modulo 65536 (if your uid_t is 16-bit) and who can run "sudo" on
>    the box.
> 
>  * You prepare a malicious repository, invite that user there and
>    ask them to run "sudo something" there.
> 
> I'd say such an attack vector is not likely, and a user with maximum
> allowed uid_t value is equally not that likely, so I do not care too
> deeply either way---and in such a case, I do prefer a simpler code.

as I do as well, but this is only 4 characters long and getting rid of
it means we now have to do either of :

  * hope nobody has a signed uid_t as this code is broken in that case
    and will misbehave widly, more importantly we also lost our only
    way to find them and warn them ahead of time that they need to patch
    it.
  * add some aditional way to detect it and avoid this code and maybe
    even provide an alternative.

and also :

  * move back to strtol and potentially break the feature for half the
    valid users if uid_t is 32-bit unsigned as it is most likely to be
    in 32-bit systems.
  * move to an even wider integer so it will be always wider than uid_t
    which means we have to move a lot more code around at least.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-05 18:33               ` Junio C Hamano
  2022-05-05 19:39                 ` Junio C Hamano
@ 2022-05-09  8:21                 ` Phillip Wood
  2022-05-09 14:51                   ` Carlo Arenas
  2022-05-09 16:01                   ` Junio C Hamano
  1 sibling, 2 replies; 161+ messages in thread
From: Phillip Wood @ 2022-05-09  8:21 UTC (permalink / raw)
  To: Junio C Hamano, Johannes Schindelin
  Cc: Carlo Marcelo Arenas Belón, git, bagasdotme, SZEDER Gábor

Hi Junio

On 05/05/2022 19:33, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> 
>> Hmm. I would like to suggest that we can side-step all of these issues
>> (and the ones I outline below) by considering a similar approach to the
>> one Stolee took in t0033: use one or more `GIT_TEST_*` environment
>> variables to pretend the exact scenario we want to test for.
> 
> I like the GIT_TEST_ASSUME_DIFFERENT_OWNER because it is fairly
> clear that it cannot be used as a new attack vector, even with
> social engineering.
> 
> It would be nice if we can do something similar, but I am coming up
> empty while trying to think of "we are testing; pretend that ..."
> that is good for testing this SUDO_UID special case *and* clearly
> cannot be used as an attack target.
> 
> So I very much like the suggestion in principle, but I am not sure
> how useful the suggestion would be to make the resulting code better
> in practice.
> 
> Perhaps this may be a way to pretend we are running a command under
> 'sudo'?
> 
> 	test_pretend_sudo () {	
>              GIT_TEST_PRETEND_GETEUID_RETURNING_ROOT=1 \
> 	    GIT_TEST_PRETEND_LSTAT_RETURNING_ROOT=root/p \
>                  SUDO_UID=0 "$@"
> 	}
> 
> 	test_expect_success 'access root-owned repository as root' '
> 		mkdir root/p &&
> 		git init root/p &&
> 		test_pretend_sudo git status
> 	'
> 
> That way we can avoid having to run "chown" while preparing for the
> test fixture, and running "git status" under root, but I am not sure
> if we want our shipped production binaries to have these "pretend"
> knobs.

Lets ask ourselves "How could an attacker use these knobs to facilitate 
an attack?". They need to either (a) set these variables in the user's 
environment themselves or (b) persuade the user to set them. For (a) if 
an attacker can set them in the user's environment then they can change 
the user's $PATH or $GIT_DIR and $GIT_WORK_TREE so this does not open up 
a new way to compromise the user. For (b) I'm not sure it is easier to 
socially engineer the user to set these new variables rather than 
GIT_DIR and GIT_WORK_TREE which will also bypass the check.

Best Wishes

Phillip


> Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-09  8:21                 ` Phillip Wood
@ 2022-05-09 14:51                   ` Carlo Arenas
  2022-05-09 15:18                     ` Phillip Wood
  2022-05-09 16:01                   ` Junio C Hamano
  1 sibling, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-09 14:51 UTC (permalink / raw)
  To: phillip.wood
  Cc: Junio C Hamano, Johannes Schindelin, git, bagasdotme, SZEDER Gábor

On Mon, May 9, 2022 at 1:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 05/05/2022 19:33, Junio C Hamano wrote:
> > Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> >
> >> Hmm. I would like to suggest that we can side-step all of these issues
> >> (and the ones I outline below) by considering a similar approach to the
> >> one Stolee took in t0033: use one or more `GIT_TEST_*` environment
> >> variables to pretend the exact scenario we want to test for.
> >
> > Perhaps this may be a way to pretend we are running a command under
> > 'sudo'?
> >
> >       test_pretend_sudo () {
> >              GIT_TEST_PRETEND_GETEUID_RETURNING_ROOT=1 \
> >           GIT_TEST_PRETEND_LSTAT_RETURNING_ROOT=root/p \
> >                  SUDO_UID=0 "$@"
> >       }
> >
> >       test_expect_success 'access root-owned repository as root' '
> >               mkdir root/p &&
> >               git init root/p &&
> >               test_pretend_sudo git status
> >       '
> >
> > That way we can avoid having to run "chown" while preparing for the
> > test fixture, and running "git status" under root, but I am not sure
> > if we want our shipped production binaries to have these "pretend"
> > knobs.
>
> Lets ask ourselves "How could an attacker use these knobs to facilitate
> an attack?".

That is not the question raised by having those "pretend" knobs in the
production binary, but instead how can an attacker abuse them to get
themself and UID he doesn't have and therefore additional access.

The fact that the current code requires you to be root to even enable
the logic makes it more difficult to use SUDO_UID that way, because if
you already got root, you don't really need them, but take into
consideration that this discussion starts with (how can we run these
things as a the test user and avoid sudo, hence root).

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-09 14:51                   ` Carlo Arenas
@ 2022-05-09 15:18                     ` Phillip Wood
  0 siblings, 0 replies; 161+ messages in thread
From: Phillip Wood @ 2022-05-09 15:18 UTC (permalink / raw)
  To: Carlo Arenas, phillip.wood
  Cc: Junio C Hamano, Johannes Schindelin, git, bagasdotme, SZEDER Gábor

Hi Carlo

On 09/05/2022 15:51, Carlo Arenas wrote:
> On Mon, May 9, 2022 at 1:21 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
>> On 05/05/2022 19:33, Junio C Hamano wrote:
>>> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>>>
>>>> Hmm. I would like to suggest that we can side-step all of these issues
>>>> (and the ones I outline below) by considering a similar approach to the
>>>> one Stolee took in t0033: use one or more `GIT_TEST_*` environment
>>>> variables to pretend the exact scenario we want to test for.
>>>
>>> Perhaps this may be a way to pretend we are running a command under
>>> 'sudo'?
>>>
>>>        test_pretend_sudo () {
>>>               GIT_TEST_PRETEND_GETEUID_RETURNING_ROOT=1 \
>>>            GIT_TEST_PRETEND_LSTAT_RETURNING_ROOT=root/p \
>>>                   SUDO_UID=0 "$@"
>>>        }
>>>
>>>        test_expect_success 'access root-owned repository as root' '
>>>                mkdir root/p &&
>>>                git init root/p &&
>>>                test_pretend_sudo git status
>>>        '
>>>
>>> That way we can avoid having to run "chown" while preparing for the
>>> test fixture, and running "git status" under root, but I am not sure
>>> if we want our shipped production binaries to have these "pretend"
>>> knobs.
>>
>> Lets ask ourselves "How could an attacker use these knobs to facilitate
>> an attack?".
> 
> That is not the question raised by having those "pretend" knobs in the
> production binary, but instead how can an attacker abuse them to get
> themself and UID he doesn't have and therefore additional access.

Maybe I'm missing something but I thought the idea was that these knobs 
were only for the safe.directory check and the normal file permissions 
would apply to all the other code.

Best Wishes

Phillip

> The fact that the current code requires you to be root to even enable
> the logic makes it more difficult to use SUDO_UID that way, because if
> you already got root, you don't really need them, but take into
> consideration that this discussion starts with (how can we run these
> things as a the test user and avoid sudo, hence root).
> 
> Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-09  8:21                 ` Phillip Wood
  2022-05-09 14:51                   ` Carlo Arenas
@ 2022-05-09 16:01                   ` Junio C Hamano
  2022-05-09 16:21                     ` Carlo Arenas
  1 sibling, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-09 16:01 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Johannes Schindelin, Carlo Marcelo Arenas Belón, git,
	bagasdotme, SZEDER Gábor

Phillip Wood <phillip.wood123@gmail.com> writes:

>> That way we can avoid having to run "chown" while preparing for the
>> test fixture, and running "git status" under root, but I am not sure
>> if we want our shipped production binaries to have these "pretend"
>> knobs.
>
> Lets ask ourselves "How could an attacker use these knobs to
> facilitate an attack?". They need to either (a) set these variables in
> the user's environment themselves or (b) persuade the user to set
> them.

I actually was not worried about the scenario where an attacker
fools potential victims, who are more privileged than the attacker,
into doing stupid things to hurt themselves.  I mentioned that the
existing knob for testing, "pretend that euid and st_uid are
different", because it tightens the check (even if you are trying
the code with your own directory, euid==st.st_uid check would not
give you an access and you are forced to rely on the safe.directory
cofniguration allowing you in), not loosens it, and it felt much
less risky than "pretend we are root" or "pretend the directory is
owned by root", which just felt much riskier things to allow people
to have us pretend.

But I was totally wrong ;-)  No matter what a unprivileged attacker
does with these knobs, the actual access will be done by a process
run by the attacker, and the actual security at the filesystem level
still kicks in to prevent the attacker from doing anything that the
attacker cannot normally do.  So the only qualm about the proliferation
of these GIT_TEST_* environment variable checks in the production code
is that it makes the logic ugly with more code.

Thanks.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v3 1/3] t: document regression git safe.directory when using sudo
  2022-05-09 16:01                   ` Junio C Hamano
@ 2022-05-09 16:21                     ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-09 16:21 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Phillip Wood, Johannes Schindelin, git, bagasdotme, SZEDER Gábor

On Mon, May 9, 2022 at 9:01 AM Junio C Hamano <gitster@pobox.com> wrote:

> But I was totally wrong ;-)  No matter what a unprivileged attacker
> does with these knobs, the actual access will be done by a process
> run by the attacker, and the actual security at the filesystem level
> still kicks in to prevent the attacker from doing anything that the
> attacker cannot normally do.

While I agree with you in principle, wouldn't this approach (while far
more flexible) also be more risky?

Let's imagine a scenario where we enhance SUDO_UID (because it
actually makes sense to do so now since it's probably better to run
hooks as a non privileged user by default instead of root). At that
point all an attacker needs to do to escalate to root is to set its
own SUDO_UID=0 and whatever else we put in the production binary for
him to use.  At least by restricting this overriding functionality to
a real root user (as done in the proposal) we could make that attack
vector less likely.

After all, I am sure that when (I know it is not fair, because it is
not the only way to do so) core.fsmonitor was invented as a useful way
to run things in a repository, nobody expected it could be weaponized
later into a privilege escalation, and feels like doing the same here
might show we have not learned that lesson.

Eitherway RFC v4 is out and waiting for feedback, and I even have an
enhanced (with even more comments and hopefully even better commit
messages) in which every single message has gone through a free
grammar checker[1] which I am planning to publish as v4 proper
sometime this week.

Carlo

[1] https://www.gingersoftware.com/grammarcheck

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-07 18:56                 ` Carlo Marcelo Arenas Belón
@ 2022-05-09 16:54                   ` Junio C Hamano
  2022-05-09 17:36                     ` rsbecker
  2022-05-09 18:48                     ` Carlo Arenas
  0 siblings, 2 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-09 16:54 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

Carlo Marcelo Arenas Belón <carenas@gmail.com> writes:

>> In exchange, the only attack scenario the code prevents is this,
>> IIUC.
>> 
>>  * You, the aspiring cracker, are a user not allowed to run "sudo" on
>>    the box, and you know your uid is 1000
>> 
>>  * You look for another user, a potential victim, whose uid is 1000
>>    modulo 65536 (if your uid_t is 16-bit) and who can run "sudo" on
>>    the box.
>> 
>>  * You prepare a malicious repository, invite that user there and
>>    ask them to run "sudo something" there.
>> 
>> I'd say such an attack vector is not likely,...

Sorry, I was totally wrong here.

It is not just "not likely", but such an attack, with a potential
victim not futzing with SUDO_UID environment themselves, would not
work at all.  The value of SUDO_UID and the original uid of the
potential victim by definition should fit in the uid_t type.  So if
you, the aspiring cracker, have UID 1000, nobody else on the system
has UID that is congruent modulo uid_t and wrap-around attack does
not exist.  As long as the type we use to read SUDO_UID string into
a variable is not narrower than uid_t, there.

Of course you can tell any user who runs "sudo" to set SUDO_UID to
1000 + 64k and cause wrap-around, but then you can tell them to set
SUDO_UID to 1000 without relying on wrap-around and have the same
effect.  So, let's stop worrying about this bogus scenario.

As to the "we can break compilation with -Wsign-compare on a system
with signed uid_t", I agree that is true if we have

	env_id <= (uid_t) -1

there.  But I am not sure if that is the most effective way to smoke
out platforms where this code has trouble working correctly.  Also,
I would think that a system with signed uid_t is a possibility, but
a user with a negative UID?

I do not think even nobody4 was negative ;-)

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-09 16:54                   ` Junio C Hamano
@ 2022-05-09 17:36                     ` rsbecker
  2022-05-09 18:48                     ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-05-09 17:36 UTC (permalink / raw)
  To: 'Junio C Hamano', 'Carlo Marcelo Arenas Belón'
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	'Guy Maurel', 'SZEDER Gábor'

On May 9, 2022 12:55 PM, Junio C Hamano wrote:
>Carlo Marcelo Arenas Belón <carenas@gmail.com> writes:
>
>>> In exchange, the only attack scenario the code prevents is this,
>>> IIUC.
>>>
>>>  * You, the aspiring cracker, are a user not allowed to run "sudo" on
>>>    the box, and you know your uid is 1000
>>>
>>>  * You look for another user, a potential victim, whose uid is 1000
>>>    modulo 65536 (if your uid_t is 16-bit) and who can run "sudo" on
>>>    the box.
>>>
>>>  * You prepare a malicious repository, invite that user there and
>>>    ask them to run "sudo something" there.
>>>
>>> I'd say such an attack vector is not likely,...
>
>Sorry, I was totally wrong here.
>
>It is not just "not likely", but such an attack, with a potential victim not futzing with
>SUDO_UID environment themselves, would not work at all.  The value of
>SUDO_UID and the original uid of the potential victim by definition should fit in the
>uid_t type.  So if you, the aspiring cracker, have UID 1000, nobody else on the
>system has UID that is congruent modulo uid_t and wrap-around attack does not
>exist.  As long as the type we use to read SUDO_UID string into a variable is not
>narrower than uid_t, there.
>
>Of course you can tell any user who runs "sudo" to set SUDO_UID to
>1000 + 64k and cause wrap-around, but then you can tell them to set SUDO_UID to
>1000 without relying on wrap-around and have the same effect.  So, let's stop
>worrying about this bogus scenario.
>
>As to the "we can break compilation with -Wsign-compare on a system with signed
>uid_t", I agree that is true if we have
>
>	env_id <= (uid_t) -1
>
>there.  But I am not sure if that is the most effective way to smoke out platforms
>where this code has trouble working correctly.  Also, I would think that a system
>with signed uid_t is a possibility, but a user with a negative UID?
>
>I do not think even nobody4 was negative ;-)

I can test the code when it's ready.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-09 16:54                   ` Junio C Hamano
  2022-05-09 17:36                     ` rsbecker
@ 2022-05-09 18:48                     ` Carlo Arenas
  2022-05-09 19:16                       ` rsbecker
  2022-05-09 19:41                       ` Junio C Hamano
  1 sibling, 2 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-09 18:48 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

On Mon, May 9, 2022 at 9:54 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> It is not just "not likely", but such an attack, with a potential
> victim not futzing with SUDO_UID environment themselves, would not
> work at all.  The value of SUDO_UID and the original uid of the
> potential victim by definition should fit in the uid_t type.  So if
> you, the aspiring cracker, have UID 1000, nobody else on the system
> has UID that is congruent modulo uid_t and wrap-around attack does
> not exist.  As long as the type we use to read SUDO_UID string into
> a variable is not narrower than uid_t, there.

correct; would adding a less elegant "static_assert" (obviously not
available in C99) in the code an alternative?, or you are suggesting
that documenting that constrain should be done in a comment and hope
it doesn't get ignored in the future, or maybe gets instead made less
likely to fail by some additional work, like the one I suggested to do
later by moving this code around and using intmax_t instead?

not that there are 2 different scenarios which we might seem to be
flip flopping about :

1) victim gets attacked by tricking them into using the wrong SUDO_UID

unlikely to be useful as you pointed out, and totally useless if we
still restrict this code to run only as root

2) attacker using this code to elevate privileges themselves

only useful if this code is not run as root.

My concern again, is not with this code AS-IS but how it might be used
in the future.

> Of course you can tell any user who runs "sudo" to set SUDO_UID to
> 1000 + 64k and cause wrap-around, but then you can tell them to set
> SUDO_UID to 1000 without relying on wrap-around and have the same
> effect.  So, let's stop worrying about this bogus scenario.

bogus only if we are still only running this code as root, of course.

> As to the "we can break compilation with -Wsign-compare on a system
> with signed uid_t", I agree that is true if we have

Apologies for not documenting it until now, but I had
-Wtautological-constant-out-of-range-compare in mind instead, but your
are correct either one would work and the point was that (without
having to add even an "static assert") we were able to find them and
warn them that they need to patch.

>         env_id <= (uid_t) -1

If that was not enough, that simple code ALSO disabled the code in
that case to make sure they MUST patch locally if they need to make it
work, or come back to us to figure out a portable way to accommodate
them in the future, with all the information about their system we
currently lack.

> there.  But I am not sure if that is the most effective way to smoke
> out platforms where this code has trouble working correctly.  Also,
> I would think that a system with signed uid_t is a possibility, but
> a user with a negative UID?

It doesn't need to be a real user (specially if someone independently
decides to remove the restriction that keeps this code available only
to root).

The fact that it was negative but was treated as a positive number on
our code just makes the wraparound we decided to ignore before more
likely to work without triggering alarms somewhere else and because we
decided to ignore an unlikely to be useful positive wraparound, we
also would fall from the negative wraparound here that would had
protected this code from both if that humble line of code wouldn't had
been removed.

In your hypothetical system where uid_t is 16-bit (hence 15-bit for
valid non-negative ids if uid_t is signed, since no sane system would
have negative ones), either 65536 or -65536 would become 0, as well as
at least the other 2^16 possibilities that a larger long would
provide.

If the size difference is even smaller (ex: uid_t is signed int), so
the type we are using to parse those values is only 1 bit wider it
will still be a concern IMHO.

I know people that wrote code to check if "/" was writable as means to
make sure they were "root", because that is what any sane system would
do, and then came Windows and even in 2022 anyone can write to "/"
there and create subdirectories.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* RE: [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-09 18:48                     ` Carlo Arenas
@ 2022-05-09 19:16                       ` rsbecker
  2022-05-09 19:41                       ` Junio C Hamano
  1 sibling, 0 replies; 161+ messages in thread
From: rsbecker @ 2022-05-09 19:16 UTC (permalink / raw)
  To: 'Carlo Arenas', 'Junio C Hamano'
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	'Guy Maurel', 'SZEDER Gábor'

On May 9, 2022 2:49 PM, Carlo Arenas wrote:
>On Mon, May 9, 2022 at 9:54 AM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> It is not just "not likely", but such an attack, with a potential
>> victim not futzing with SUDO_UID environment themselves, would not
>> work at all.  The value of SUDO_UID and the original uid of the
>> potential victim by definition should fit in the uid_t type.  So if
>> you, the aspiring cracker, have UID 1000, nobody else on the system
>> has UID that is congruent modulo uid_t and wrap-around attack does not
>> exist.  As long as the type we use to read SUDO_UID string into a
>> variable is not narrower than uid_t, there.
>
>correct; would adding a less elegant "static_assert" (obviously not available in C99)
>in the code an alternative?, or you are suggesting that documenting that constrain
>should be done in a comment and hope it doesn't get ignored in the future, or
>maybe gets instead made less likely to fail by some additional work, like the one I
>suggested to do later by moving this code around and using intmax_t instead?
>
>not that there are 2 different scenarios which we might seem to be flip flopping
>about :
>
>1) victim gets attacked by tricking them into using the wrong SUDO_UID
>
>unlikely to be useful as you pointed out, and totally useless if we still restrict this
>code to run only as root
>
>2) attacker using this code to elevate privileges themselves
>
>only useful if this code is not run as root.
>
>My concern again, is not with this code AS-IS but how it might be used in the
>future.
>
>> Of course you can tell any user who runs "sudo" to set SUDO_UID to
>> 1000 + 64k and cause wrap-around, but then you can tell them to set
>> SUDO_UID to 1000 without relying on wrap-around and have the same
>> effect.  So, let's stop worrying about this bogus scenario.
>
>bogus only if we are still only running this code as root, of course.
>
>> As to the "we can break compilation with -Wsign-compare on a system
>> with signed uid_t", I agree that is true if we have
>
>Apologies for not documenting it until now, but I had -Wtautological-constant-out-
>of-range-compare in mind instead, but your are correct either one would work
>and the point was that (without having to add even an "static assert") we were
>able to find them and warn them that they need to patch.
>
>>         env_id <= (uid_t) -1
>
>If that was not enough, that simple code ALSO disabled the code in that case to
>make sure they MUST patch locally if they need to make it work, or come back to
>us to figure out a portable way to accommodate them in the future, with all the
>information about their system we currently lack.
>
>> there.  But I am not sure if that is the most effective way to smoke
>> out platforms where this code has trouble working correctly.  Also, I
>> would think that a system with signed uid_t is a possibility, but a
>> user with a negative UID?
>
>It doesn't need to be a real user (specially if someone independently decides to
>remove the restriction that keeps this code available only to root).
>
>The fact that it was negative but was treated as a positive number on our code just
>makes the wraparound we decided to ignore before more likely to work without
>triggering alarms somewhere else and because we decided to ignore an unlikely to
>be useful positive wraparound, we also would fall from the negative wraparound
>here that would had protected this code from both if that humble line of code
>wouldn't had been removed.
>
>In your hypothetical system where uid_t is 16-bit (hence 15-bit for valid non-
>negative ids if uid_t is signed, since no sane system would have negative ones),
>either 65536 or -65536 would become 0, as well as at least the other 2^16
>possibilities that a larger long would provide.
>
>If the size difference is even smaller (ex: uid_t is signed int), so the type we are
>using to parse those values is only 1 bit wider it will still be a concern IMHO.

Just to be pedantic uid_t is signed int is not always smaller. It is 32-bit on NonStop. uid_t signed short or signed char would be smaller. I have wondered why uid_t was not defined as unsigned int or unsigned long (although unsigned long is 64 bits on the x86 wide model NonStop) because they cannot be negative. Making assumptions about size or sign when doing this check is not correct IMHO. It should be a direct comparison of env_id != ROOT_UID, where you know what ROOT_UID is on the specific platform. I do not like the <= concept for user id comparison because it is making assumptions. Structurally, on NonStop, (uid & 0x0000FF00) == 0x0000FF00 can be used to check whether the user is in the root group but that is coincidental and subject to change without notice. No one worth their salt does that comparison on platform, rather they compare getgid() == 255 to do that test.

>
>I know people that wrote code to check if "/" was writable as means to make sure
>they were "root", because that is what any sane system would do, and then came
>Windows and even in 2022 anyone can write to "/"
>there and create subdirectories.

On NonStop: / is often writable by non-root users in the root group. Non-root users in the root group sometimes have repositories. In cygwin, / is owned by the user who installed cygwin or the OS rather than root. It is a relatively random number, and definitely not 0. It is also possible that a dedicated VM in Linux can be spun up for sandbox testing allowing anyone to write anywhere. Even if you run git as Administrator on Windows 10+, it will not have the userId of 0 in cygwin git. I do not think the / writable assumption is portable.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-09 18:48                     ` Carlo Arenas
  2022-05-09 19:16                       ` rsbecker
@ 2022-05-09 19:41                       ` Junio C Hamano
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-09 19:41 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

Carlo Arenas <carenas@gmail.com> writes:

> not that there are 2 different scenarios which we might seem to be
> flip flopping about :
>
> 1) victim gets attacked by tricking them into using the wrong SUDO_UID
>
> unlikely to be useful as you pointed out, and totally useless if we
> still restrict this code to run only as root

I think we established that we are not interested in this long ago.

> 2) attacker using this code to elevate privileges themselves
>
> only useful if this code is not run as root.

An attacker who is not root would not make the "what does SUDO_UID
say about the user we came from?", and "git" will not be setuid
binary, so I agree the code is not useful for such an attack,
either.

> My concern again, is not with this code AS-IS but how it might be used
> in the future.


I'd rather not such a "concern" to block us for too long.


>> Of course you can tell any user who runs "sudo" to set SUDO_UID to
>> 1000 + 64k and cause wrap-around, but then you can tell them to set
>> SUDO_UID to 1000 without relying on wrap-around and have the same
>> effect.  So, let's stop worrying about this bogus scenario.
>
> bogus only if we are still only running this code as root, of course.

And this code is only run to learn what SUDO_UID says, which is what
we check when we notice geteuid() says we are root.

>> As to the "we can break compilation with -Wsign-compare on a system
>> with signed uid_t", I agree that is true if we have
>
> Apologies for not documenting it until now, but I had
> -Wtautological-constant-out-of-range-compare in mind instead, but your
> are correct either one would work and the point was that (without
> having to add even an "static assert") we were able to find them and
> warn them that they need to patch.
>
>>         env_id <= (uid_t) -1
>
> If that was not enough, that simple code ALSO disabled the code in
> that case to make sure they MUST patch locally if they need to make it
> work, or come back to us to figure out a portable way to accommodate
> them in the future, with all the information about their system we
> currently lack.

Without a comment to say that, nobody would be able to figure out
that we are waiting for them to speak up.

	/* the code does not work on a system with signed uid */
	intmax_t tmp = (uid_t) -1;
	if (tmp < 0)
		BUG("report to git@vger that you have a signed uid_t");

or better yet, a compile-time equivalent, perhaps.

>> there.  But I am not sure if that is the most effective way to smoke
>> out platforms where this code has trouble working correctly.  Also,
>> I would think that a system with signed uid_t is a possibility, but
>> a user with a negative UID?
>
> It doesn't need to be a real user (specially if someone independently
> decides to remove the restriction that keeps this code available only
> to root).

When they break, they can keep both halves.  Is it our concern?

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v4 0/3] fix `sudo make install` regression in maint
  2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
                               ` (2 preceding siblings ...)
  2022-05-07 16:35             ` [RFC PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
@ 2022-05-10 14:17             ` Phillip Wood
  2022-05-10 15:47               ` Carlo Arenas
  2022-05-10 17:46             ` [PATCH " Carlo Marcelo Arenas Belón
  4 siblings, 1 reply; 161+ messages in thread
From: Phillip Wood @ 2022-05-10 14:17 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón, git
  Cc: gitster, bagasdotme, Johannes.Schindelin

Hi Carlo

On 07/05/2022 17:35, Carlo Marcelo Arenas Belón wrote:
> A reroll for cb/path-owner-check-with-sudo with most of the suggestions
> included but planned originally as an RFC, because I am frankly not sure
> that I read and addressed all of it, but also because after seeing how
> my only chance to get an official Reviewed-by: Junio vanished I also
> realized that I wasn't been clear enough and careful enough from the
> beginning to explain the code correctly and therefore had maybe wasted
> more of the review quota this change should require.
> 
> Important changes (eventhough most are not affecting the logic)
> * Document hopefully better which environments are supported and what
>    to do if you want to test it in one that is not (thanks to dscho)
> * Removed the arguably premature optimization to try to keep the sudo
>    cache warm which was actually buggy, as it was also not needed.
>    The CI does passwordless sudo unconditionally and even when running
>    it locally it will go so fast that is shouldn't be an issue (thanks
>    to Philip)
> * No longer using the ugly and controversial variable name so now it
>    will need GIT_TEST_ENABLE_SUDO to be used to enable it on CI (which
>    is not done in this series, but a run with it enabled on top of
>    seen is available[1])
> * Stop the arguing about what is or not a regression worth fixing and
>    instead document it as one through a test, which would be easy to
>    fix in a follow up since the code was already provided by Junio

I've had a read of the patches and I agree with Junio's comments on the 
second patch. I'd really like us to avoid sudo in the tests if we can as 
it causes a lot of problems. Just to let you know I'm going to be off 
the list for the next couple of weeks, so I wont be looking at these 
patches in that time.

Best Wishes

Phillip

> Lastly I am little concerned by the fact this is going to maint but
> has a "weather balloon" of sorts, which might not be wise, since it
> might prevent people that might be affected from upgrading if they
> have a -Werror policy.
> 
> The effect is minor though, as worst case, if someone has a system
> with a signed uid_t then this "feature" wouldn't work for them and
> nothing has changed but I think it is worth to consider the alternatives
> which are (in my own order of preference)
> 
> * Revert the change to use unsigned long and strtoul()
> 
>    This will mean that people running in a 32bit system with an uid bigger
>    than INT_MAX wouldn't be able to use the feature
> 
> * Move the code out (which is indeed an artificial restriction) so that
>    we can use intmax_t and strtoimax() instead and a cast to compare the
>    uid_t.
> 
>    This avoids all issues and restrictions but means more code changes
> 
> * Throw away the custom function and expand the API ones to be used
>    instead as dscho suggested.
> 
>    Even more code changes, but maybe less risk as we will be building
>    on top of battle tested code.
> 
> [1] https://github.com/carenas/git/actions/runs/2286452160
> 
> Carlo Marcelo Arenas Belón (3):
>    t: regression git needs safe.directory when using sudo
>    git-compat-util: avoid failing dir ownership checks if running
>      privileged
>    t0034: add negative tests and allow git init to mostly work under sudo
> 
>   Documentation/config/safe.txt  |  10 ++++
>   git-compat-util.h              |  49 +++++++++++++++-
>   t/lib-sudo.sh                  |  12 ++++
>   t/t0034-root-safe-directory.sh | 103 +++++++++++++++++++++++++++++++++
>   4 files changed, 173 insertions(+), 1 deletion(-)
>   create mode 100644 t/lib-sudo.sh
>   create mode 100755 t/t0034-root-safe-directory.sh
> 

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [RFC PATCH v4 0/3] fix `sudo make install` regression in maint
  2022-05-10 14:17             ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Phillip Wood
@ 2022-05-10 15:47               ` Carlo Arenas
  0 siblings, 0 replies; 161+ messages in thread
From: Carlo Arenas @ 2022-05-10 15:47 UTC (permalink / raw)
  To: phillip.wood; +Cc: git, gitster, bagasdotme, Johannes.Schindelin

On Tue, May 10, 2022 at 7:17 AM Phillip Wood <phillip.wood123@gmail.com> wrote:
> On 07/05/2022 17:35, Carlo Marcelo Arenas Belón wrote:
> > A reroll for cb/path-owner-check-with-sudo with most of the suggestions
> > included but planned originally as an RFC, because I am frankly not sure
> > that I read and addressed all of it, but also because after seeing how
> > my only chance to get an official Reviewed-by: Junio vanished I also
> > realized that I wasn't clear enough and careful enough from the
> > beginning to explain the code correctly and therefore had maybe wasted
> > more of the review quota this change should require.
> >
> > Important changes (eventhough most are not affecting the logic)
> > * Document hopefully better which environments are supported and what
> >    to do if you want to test it in one that is not (thanks to dscho)
> > * Removed the arguably premature optimization to try to keep the sudo
> >    cache warm which was actually buggy, as it was also not needed.
> >    The CI does passwordless sudo unconditionally and even when running
> >    it locally it will go so fast that is shouldn't be an issue (thanks
> >    to Phillip)
> > * No longer using the ugly and controversial variable name so now it
> >    will need GIT_TEST_ENABLE_SUDO to be used to enable it on CI (which
> >    is not done in this series, but a run with it enabled on top of
> >    seen is available[1])
> > * Stop the arguing about what is or not a regression worth fixing and
> >    instead document it as one through a test, which would be easy to
> >    fix in a follow up since the code was already provided by Junio
>
> I've had a read of the patches and I agree with Junio's comments on the
> second patch.

Not sure which comments you are referring to, but
I'd implemented Junio's suggestion and removed the "too clever" (uid_t)-1 in v4.

> I'd really like us to avoid sudo in the tests if we can as
> it causes a lot of problems.

Even if it might not seem like it, I agree with you both (and dscho)
too, and if I could come out with a way to do so that would be still
secure, I would do it in a pinch, but I can't (at least until now) and
I don't want for that to hold up this fix so will be publishing soon a
v4 that still uses sudo in the tests, I am afraid.

> Just to let you know I'm going to be off
> the list for the next couple of weeks, so I wont be looking at these
> patches in that time.

Thanks for all your help reviewing them and more importantly improving
them, enjoy your time off.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v4 0/3] fix `sudo make install` regression in maint
  2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
                               ` (3 preceding siblings ...)
  2022-05-10 14:17             ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Phillip Wood
@ 2022-05-10 17:46             ` Carlo Marcelo Arenas Belón
  2022-05-10 17:46               ` [PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
                                 ` (3 more replies)
  4 siblings, 4 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-10 17:46 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón

A reroll for cb/path-owner-check-with-sudo with all the suggestions by
Junio since RFC v4 and that only include a small code change in patch 2
to get rid of the maligned and probably buggy `(uid_t)-1` which is not
needed and making sure the code would most likely work (ad discussed)
even when uid_t is signed, sudo behaves and git is running as root.

Range-diff provided below to make easier to spot all the other changes
in documentation and commit messages as well.

Carlo Marcelo Arenas Belón (3):
  t: regression git needs safe.directory when using sudo
  git-compat-util: avoid failing dir ownership checks if running
    privileged
  t0034: add negative tests and allow git init to mostly work under sudo

 Documentation/config/safe.txt  |  10 ++++
 git-compat-util.h              |  54 ++++++++++++++++-
 t/lib-sudo.sh                  |  12 ++++
 t/t0034-root-safe-directory.sh | 103 +++++++++++++++++++++++++++++++++
 4 files changed, 178 insertions(+), 1 deletion(-)
 create mode 100644 t/lib-sudo.sh
 create mode 100755 t/t0034-root-safe-directory.sh

--
1:  4d078785cb ! 1:  8296d93ec0 t: regression git needs safe.directory when using sudo
    @@ Commit message
     
         The test file also uses at least one initial "setup" test that creates
         a parallel execution directory, while ignoring the repository created
    -    by the test framework, and special care should be taken when invoking
    -    commands through sudo, since the environment is otherwise independent
    -    from what the test framework expects.  Indeed `git status` was used
    -    as a proxy because it doesn't even require commits in the repository
    -    to work.
    +    by the test framework.
    +
    +    Special care should be taken when invoking commands through sudo, since
    +    the environment is otherwise independent from what the test framework
    +    setup and might have changed the values for HOME, SHELL and dropped
    +    several relevant environment variables for your test.  Indeed `git status`
    +    was used as a proxy because it doesn't even require commits in the
    +    repository to work and usually doesn't require much from the environment
    +    to run, but a future patch will add calls to `git init` and that will
    +    fail to honor the default branch name, unless that setting is NOT
    +    provided through an environment variable (which means even a CI run
    +    could fail that test if enabled incorrectly).
     
         A new SUDO prerequisite is provided that does some sanity checking
         to make sure the sudo command that will be used allows for passwordless
    @@ Commit message
         execution path.  This matches what is provided by the macOS agents that
         are used as part of GitHub actions and probably nowhere else.
     
    -    Most of those characteristics make this test mostly suitable only for
    +    Most of those characteristics make this test mostly only suitable for
         CI, but it might be executed locally if special care is taken to provide
    -    for some of them in the local configuration and maybe making use of the
    +    for all of them in the local configuration and maybe making use of the
         sudo credential cache by first invoking sudo, entering your password if
         needed, and then invoking the test with:
     
           $ GIT_TEST_ALLOW_SUDO=YES ./t0034-root-safe-directory.sh
     
         If it fails to run, then it means your local setup wouldn't work for the
    -    test and things that might help is to comment out sudo's secure_path config
    -    and make sure your account has similar privileges than what the CI
    -    provides (for example an entry in /etc/sudoers for the user marta like)
    +    test because of the configuration sudo has or other system settings, and
    +    things that might help are to comment out sudo's secure_path config, and
    +    make sure that the account you are using has no restrictions on the
    +    commands it can run through sudo, just like is provided for the user in
    +    the CI.
    +
    +    For example (assuming a username of marta for you) something probably
    +    similar to the following entry in your /etc/sudoers (or equivalent) file:
     
           marta ALL=(ALL:ALL) NOPASSWD: ALL
     
         Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
         Helped-by: Phillip Wood <phillip.wood123@gmail.com>
         Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>
     
      ## t/t0034-root-safe-directory.sh (new) ##
     @@
2:  073b73eb26 ! 2:  e3efd6a178 git-compat-util: avoid failing dir ownership checks if running privileged
    @@ Commit message
         privileges didn't track the original id in a sudo compatible way.
     
         Because of compatibility with sudo, the code assumes that uid_t is an
    -    unsigned integer type, but adds additional logic to protect itself
    -    against possibly malicious ids outside the expected range and ignore
    -    them.
    -
    -    A warning should be generated if uid_t is signed and the code would
    -    need to be locally patched to work correctly, but this is also a
    -    weather balloon of sorts so we will then now which systems those are
    -    and whether we should accommodate for their portability in our codebase.
    +    unsigned integer type (which is not required by the standard) but is used
    +    that way in their codebase to generate SUDO_UID.  In systems where uid_t
    +    is signed, sudo might be also patched to NOT be unsigned and that might
    +    be able to trigger an edge case and a bug (as described in the code), but
    +    it is considered unlikely to be triggered, and even when it does the code
    +    would just safely fail, so there is no attempt either to detect it or
    +    prevent it in the code, which might need to be changed in the future.
     
         Reported-by: Guy Maurel <guy.j@maurel.de>
         Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
    @@ Commit message
         Helped-by: Phillip Wood <phillip.wood123@gmail.com>
         Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
         Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>
     
      ## Documentation/config/safe.txt ##
     @@ Documentation/config/safe.txt: directory was listed in the `safe.directory` list. If `safe.directory=*`
    @@ Documentation/config/safe.txt: directory was listed in the `safe.directory` list
     +obviously do so with the uid of the user that is running git itself,
     +but if git is running as root, in a platform that provides sudo and is
     +not Windows, it will check first if it might have been started through
    -+it, and if that is the case, will instead use the uid of the user that
    -+did invoke that instead.
    ++it, and if that is the case, will use the uid of the user that invoked
    ++sudo instead.
     +If that is not what you would prefer and want git to only trust
     +repositories that are owned by root instead, then you should remove
     +the `SUDO_UID` variable from root's environment.
    @@ git-compat-util.h: static inline int git_offset_1st_component(const char *path)
     +#endif
     +
     +/*
    -+ * this helper function overrides a ROOT_UID with the one provided by
    ++ * This helper function overrides a ROOT_UID with the one provided by
     + * an environment variable, do not use unless the original user is
    -+ * root
    -+ * WARNING: this function assumes uid_t is unsigned, if you got here
    -+ *          because of a warning or a bug will need a patch and would
    -+ *          be nice if you let us know
    ++ * root or could be used as means to escalate to root privileges.
    ++ *
    ++ * 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, but need to make sure sudo never generates a
    ++ * value higher than what can be represented by your uid_t type or a
    ++ * negative value or it will trigger wraparound bugs.
    ++ *
    ++ * If that happens the uid used might be incorrect and then trigger
    ++ * an access error from the filesystem itself.
    ++ *
    ++ * In the unlikely scenario this happened to you, and that is how you
    ++ * got to this message, we would like to know about it by letting us
    ++ * now with an email to git@vger.kernel.org indicating which platform,
    ++ * you are running on and which version of sudo you used to see if we
    ++ * can provide you a patch that would prevent this issue in the future.
     + */
     +static inline void extract_id_from_env(const char *env, uid_t *id)
     +{
     +	const char *real_uid = getenv(env);
     +
    -+	/* discard any empty values */
    ++	/* 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 below */
     +		env_id = strtoul(real_uid, &endptr, 10);
    -+		/*
    -+		 * env_id could underflow/overflow in the previous call
    -+		 * and if it will still fit in a long it will not report
    -+		 * it as error with ERANGE, instead silently using an
    -+		 * equivalent positive number that might be bogus.
    -+		 * if uid_t is narrower than long, it might not fit,
    -+		 * hence why we  need to check it against the maximum
    -+		 * possible uid_t value before accepting it.
    -+		 */
    -+		if (!*endptr && !errno && env_id <= (uid_t)-1)
    ++		if (!*endptr && !errno)
     +			*id = env_id;
     +	}
     +}
3:  e772e4d128 ! 3:  1b8003c599 t0034: add negative tests and allow git init to mostly work under sudo
    @@ Commit message
     
         Note that the specific test that documents that after the previous
         changes, it is no longer possible for root (if obtained through sudo)
    -    to NOT add an exception or need a "workaround" to be able to run git
    +    to NOT add an exception or NOT need a "workaround" to be able to run git
         commands in a repository owned by thyself, is marked as a regression
         and is expected to be fixed with a future change, which hasn't been
         provided yet and that is not part of this series.
     
    +    As described in the comments from the test itself, at least one of the
    +    documented workarounds could fail if sudo doesn't allow root to call sudo
    +    or if root is not in sudoers, but that is very unlikely, and more
    +    importantly actually not what is provided by the currently supported sudo
    +    configuration this test already relies on and therefore adding a way to
    +    validate it works in the prerequisite has been punted for now.
    +
         Helped-by: Junio C Hamano <gitster@pobox.com>
         Helped-by: Phillip Wood <phillip.wood123@gmail.com>
         Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
    -    Signed-off-by: Junio C Hamano <gitster@pobox.com>
     
      ## t/lib-sudo.sh (new) ##
     @@
    @@ t/t0034-root-safe-directory.sh: test_expect_success SUDO 'sudo git status as ori
     +'
     +
     +test_expect_success SUDO 'can access with sudo using a workaround' '
    -+	# run sudo twice; would fail is root is not in the sudoers
    ++	# run sudo twice; would fail if root is not in sudoers
     +	(
     +		cd root/p &&
     +		sudo sudo git status
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v4 1/3] t: regression git needs safe.directory when using sudo
  2022-05-10 17:46             ` [PATCH " Carlo Marcelo Arenas Belón
@ 2022-05-10 17:46               ` Carlo Marcelo Arenas Belón
  2022-05-10 22:10                 ` Junio C Hamano
  2022-05-10 17:46               ` [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
                                 ` (2 subsequent siblings)
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-10 17:46 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón, SZEDER Gábor

Originally reported after release of v2.35.2 (and other maint branches)
for CVE-2022-24765 and blocking otherwise harmless commands that were
done using sudo in a repository that was owned by the user.

Add a new test script with very basic support to allow running git
commands through sudo, so a reproduction could be implemented and that
uses only `git status` as a proxy of the issue reported.

Note that because of the way sudo interacts with the system, a much
more complete integration with the test framework will require a lot
more work and that was therefore intentionally punted for now.

The current implementation requires the execution of a special cleanup
function which should always be kept as the last "test" or otherwise
the standard cleanup functions will fail because they can't remove
the root owned directories that are used.  This also means that if
failures are found while running, the specifics of the failure might
not be kept for further debugging and if the test was interrupted, it
will be necessary to clean the working directory manually before
restarting by running:

  $ sudo rm -rf trash\ directory.t0034-root-safe-directory/

The test file also uses at least one initial "setup" test that creates
a parallel execution directory, while ignoring the repository created
by the test framework.

Special care should be taken when invoking commands through sudo, since
the environment is otherwise independent from what the test framework
setup and might have changed the values for HOME, SHELL and dropped
several relevant environment variables for your test.  Indeed `git status`
was used as a proxy because it doesn't even require commits in the
repository to work and usually doesn't require much from the environment
to run, but a future patch will add calls to `git init` and that will
fail to honor the default branch name, unless that setting is NOT
provided through an environment variable (which means even a CI run
could fail that test if enabled incorrectly).

A new SUDO prerequisite is provided that does some sanity checking
to make sure the sudo command that will be used allows for passwordless
execution as root without restrictions and doesn't mess with git's
execution path.  This matches what is provided by the macOS agents that
are used as part of GitHub actions and probably nowhere else.

Most of those characteristics make this test mostly only suitable for
CI, but it might be executed locally if special care is taken to provide
for all of them in the local configuration and maybe making use of the
sudo credential cache by first invoking sudo, entering your password if
needed, and then invoking the test with:

  $ GIT_TEST_ALLOW_SUDO=YES ./t0034-root-safe-directory.sh

If it fails to run, then it means your local setup wouldn't work for the
test because of the configuration sudo has or other system settings, and
things that might help are to comment out sudo's secure_path config, and
make sure that the account you are using has no restrictions on the
commands it can run through sudo, just like is provided for the user in
the CI.

For example (assuming a username of marta for you) something probably
similar to the following entry in your /etc/sudoers (or equivalent) file:

  marta	ALL=(ALL:ALL) NOPASSWD: ALL

Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/t0034-root-safe-directory.sh | 45 ++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 0000000000..2e4492a66d
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.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
+
+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
+'
+
+test_expect_success SUDO 'setup' '
+	sudo rm -rf root &&
+	mkdir -p root/r &&
+	sudo chown root root &&
+	(
+		cd root/r &&
+		git init
+	)
+'
+
+test_expect_failure SUDO 'sudo git status as original owner' '
+	(
+		cd root/r &&
+		git status &&
+		sudo git status
+	)
+'
+
+# this MUST be always the last test
+test_expect_success SUDO 'cleanup' '
+	sudo rm -rf root
+'
+
+test_done
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-10 17:46             ` [PATCH " Carlo Marcelo Arenas Belón
  2022-05-10 17:46               ` [PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-10 17:46               ` Carlo Marcelo Arenas Belón
  2022-05-10 22:57                 ` Junio C Hamano
  2022-05-10 17:46               ` [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
  2022-05-13  1:00               ` [PATCH v5 0/4] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-10 17:46 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón, Guy Maurel, SZEDER Gábor,
	Randall Becker

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that user was
root (because git was invoked through sudo or a compatible tool) and the
original uid that repository trusted for its config was no longer known,
therefore failing the following otherwise safe call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using that instead.

This assumes the environment the user is running on after going
privileged can't be tampered with, and also adds code to restrict that
the new behavior only applies if running as root, therefore keeping the
most common case, which runs unprivileged, from changing, but because of
that, it will miss cases where sudo (or an equivalent) was used to change
to another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Because of compatibility with sudo, the code assumes that uid_t is an
unsigned integer type (which is not required by the standard) but is used
that way in their codebase to generate SUDO_UID.  In systems where uid_t
is signed, sudo might be also patched to NOT be unsigned and that might
be able to trigger an edge case and a bug (as described in the code), but
it is considered unlikely to be triggered, and even when it does the code
would just safely fail, so there is no attempt either to detect it or
prevent it in the code, which might need to be changed in the future.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 Documentation/config/safe.txt  | 10 +++++++
 git-compat-util.h              | 54 +++++++++++++++++++++++++++++++++-
 t/t0034-root-safe-directory.sh |  2 +-
 3 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 6d764fe0cc..dba9d5b2d1 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -26,3 +26,13 @@ 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.
++
+When git tries to check for ownership of git repositories, it will
+obviously do so with the uid of the user that is running git itself,
+but if git is running as root, in a platform that provides sudo and is
+not Windows, it will check first if it might have been started through
+it, and if that is the case, will use the uid of the user that invoked
+sudo instead.
+If that is not what you would prefer and want git to only trust
+repositories that are owned by root instead, then you should remove
+the `SUDO_UID` variable from root's environment.
diff --git a/git-compat-util.h b/git-compat-util.h
index 63ba89dd31..754cd90d43 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -393,12 +393,64 @@ 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
+
+/*
+ * This helper function overrides a ROOT_UID with the one provided by
+ * an environment variable, do not use unless the original user is
+ * root or could be used as means to escalate to root privileges.
+ *
+ * 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, but need to make sure sudo never generates a
+ * value higher than what can be represented by your uid_t type or a
+ * negative value or it will trigger wraparound bugs.
+ *
+ * If that happens the uid used might be incorrect and then trigger
+ * an access error from the filesystem itself.
+ *
+ * In the unlikely scenario this happened to you, and that is how you
+ * got to this message, we would like to know about it by letting us
+ * now with an email to git@vger.kernel.org indicating which platform,
+ * you are running on and which version of sudo you used to see if we
+ * can provide you a patch that would prevent this issue 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 below */
+		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/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index 2e4492a66d..ecd9dca6b3 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -29,7 +29,7 @@ test_expect_success SUDO 'setup' '
 	)
 '
 
-test_expect_failure SUDO 'sudo git status as original owner' '
+test_expect_success SUDO 'sudo git status as original owner' '
 	(
 		cd root/r &&
 		git status &&
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-10 17:46             ` [PATCH " Carlo Marcelo Arenas Belón
  2022-05-10 17:46               ` [PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
  2022-05-10 17:46               ` [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-10 17:46               ` Carlo Marcelo Arenas Belón
  2022-05-10 23:11                 ` Junio C Hamano
  2022-05-13  1:00               ` [PATCH v5 0/4] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-10 17:46 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Carlo Marcelo Arenas Belón

Add a support library that provides one function that can be used
to run a "scriplet" of commands through sudo and that helps invoking
sudo in the slightly awkward way that is required to ensure it doesn't
block the call (if shell was allowed as tested in the prerequisite)
and it doesn't run the command through a different shell than the one
we intended.

Add additional negative tests as suggested by Junio and that use a
new workspace that is owned by root.

Note that the specific test that documents that after the previous
changes, it is no longer possible for root (if obtained through sudo)
to NOT add an exception or NOT need a "workaround" to be able to run git
commands in a repository owned by thyself, is marked as a regression
and is expected to be fixed with a future change, which hasn't been
provided yet and that is not part of this series.

As described in the comments from the test itself, at least one of the
documented workarounds could fail if sudo doesn't allow root to call sudo
or if root is not in sudoers, but that is very unlikely, and more
importantly actually not what is provided by the currently supported sudo
configuration this test already relies on and therefore adding a way to
validate it works in the prerequisite has been punted for now.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 t/lib-sudo.sh                  | 12 +++++++
 t/t0034-root-safe-directory.sh | 58 ++++++++++++++++++++++++++++++++++
 2 files changed, 70 insertions(+)
 create mode 100644 t/lib-sudo.sh

diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
new file mode 100644
index 0000000000..d8a88fb9db
--- /dev/null
+++ b/t/lib-sudo.sh
@@ -0,0 +1,12 @@
+# 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"
+	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
index ecd9dca6b3..54f687d787 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -3,6 +3,7 @@
 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
@@ -10,6 +11,12 @@ then
 	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 &&
@@ -19,6 +26,12 @@ test_lazy_prereq SUDO '
 	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 &&
@@ -37,6 +50,51 @@ test_expect_success SUDO 'sudo git status as original owner' '
 	)
 '
 
+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_failure SUDO 'can access with sudo if root' '
+	(
+		cd root/p &&
+		sudo git status
+	)
+'
+
+test_expect_success SUDO 'can access with sudo using a workaround' '
+	# run sudo twice; would fail if root is not in sudoers
+	(
+		cd root/p &&
+		sudo sudo git status
+	) &&
+	# provide explicit GIT_DIR
+	(
+		cd root/p &&
+		run_with_sudo <<-END
+			GIT_DIR=.git &&
+			GIT_WORK_TREE=. &&
+			export GIT_DIR GIT_WORK_TREE &&
+			git status
+		END
+	) &&
+	# discard 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
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 1/3] t: regression git needs safe.directory when using sudo
  2022-05-10 17:46               ` [PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-10 22:10                 ` Junio C Hamano
  2022-05-10 23:11                   ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-10 22:10 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin, SZEDER Gábor

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> Note that because of the way sudo interacts with the system, a much
> more complete integration with the test framework will require a lot
> more work and that was therefore intentionally punted for now.
>
> The current implementation requires ...
> ...
> If it fails to run, then it means your local setup wouldn't work for the
> test because of the configuration sudo has or other system settings, and
> things that might help are to comment out sudo's secure_path config, and
> make sure that the account you are using has no restrictions on the
> commands it can run through sudo, just like is provided for the user in
> the CI.
>
> For example (assuming a username of marta for you) something probably
> similar to the following entry in your /etc/sudoers (or equivalent) file:
>
>   marta	ALL=(ALL:ALL) NOPASSWD: ALL
>
> Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
> Helped-by: Phillip Wood <phillip.wood123@gmail.com>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>

Very well written.

> +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
> +'

I vaguely recall mentions of older dash that lack "command -v" made
earlier, but implementations of dash I have handy seem to know it.
I am personally fine with this as this script has a very narrow and
limited audience in mind.

> +test_expect_success SUDO 'setup' '
> +	sudo rm -rf root &&
> +	mkdir -p root/r &&
> +	sudo chown root root &&
> +	(
> +		cd root/r &&
> +		git init
> +	)
> +'

So, "root/" is owned by root, "root/r" is owned by the tester.

> +test_expect_failure SUDO 'sudo git status as original owner' '
> +	(
> +		cd root/r &&
> +		git status &&

The tester runs "git status" in "root/r" owned by the tester and it
should succeed.

> +		sudo git status

We want the tester to be able to do the same while temporarily
becoming 'root' with "sudo", but we know it fails right now.

> +	)
> +'

Mental note.  We do not need root to be owned by 'root' with the
tests we see here.  Perhaps we would add some that requires it in
later patches.  We'll see.

> +# this MUST be always the last test
> +test_expect_success SUDO 'cleanup' '
> +	sudo rm -rf root
> +'
> +
> +test_done

So far, looking good.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-10 17:46               ` [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-10 22:57                 ` Junio C Hamano
  2022-05-11  7:34                   ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-10 22:57 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> This assumes the environment the user is running on after going
> privileged can't be tampered with, and also adds code to restrict that
> the new behavior only applies if running as root, therefore keeping the
> most common case, which runs unprivileged, from changing, but because of
> that, it will miss cases where sudo (or an equivalent) was used to change
> to another unprivileged user or where the equivalent tool used to raise
> privileges didn't track the original id in a sudo compatible way.

Well written.

> Because of compatibility with sudo, the code assumes that uid_t is an
> unsigned integer type (which is not required by the standard) but is used
> that way in their codebase to generate SUDO_UID.

Heh, that is a good point.

> ++
> +When git tries to check for ownership of git repositories, it will
> +obviously do so with the uid of the user that is running git itself,

We do not need "obviously" here, but this has overlap with the
beginning part of the safe.directory explanation, so I would
probably suggest rewriting it altogether.

> +but if git is running as root, in a platform that provides sudo and is
> +not Windows, it will check first if it might have been started through

Does Windows provide sudo that leaves the original user in SUDO_UID
(I doubt it)?  If not, then "on a platform that provides sudo, it will"
would be sufficient.

> +it, and if that is the case, will use the uid of the user that invoked
> +sudo instead.

  
  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', however, instead of allowing accesses to
  repositories owned by 'root', it checks the SUDO_UID environment
  variable and if it is set, allows access to repositories owned by
  the uid recorded as its value.  This is to make it easy to perform
  a common sequence "make && sudo make install".  A process running
  under 'sudo' runs as 'root' but the 'sudo' command exports the
  environment variable to record who the original user was.

> +If that is not what you would prefer and want git to only trust
> +repositories that are owned by root instead, then you should remove
> +the `SUDO_UID` variable from root's environment.

s/should/can/.  But otherwise this is excellent.

> diff --git a/git-compat-util.h b/git-compat-util.h
> index 63ba89dd31..754cd90d43 100644
> --- a/git-compat-util.h
> +++ b/git-compat-util.h
> @@ -393,12 +393,64 @@ 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
> +
> +/*
> + * This helper function overrides a ROOT_UID with the one provided by
> + * an environment variable, do not use unless the original user is
> + * root or could be used as means to escalate to root privileges.

I do not understand the "or could be used ..." at all.  If the
original user obtained from geteuid() is not root, then no matter
what we do to *id here in this function, we won't let you gain the
root privilege.  The system won't let us give you the root privilege
because we (Git) are running as a normal user.

Or do you mean

	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. 

Then I can sort-of understand it, but (2) is a too obvious thing to
say.

> + * 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, but need to make sure sudo never generates a
> + * value higher than what can be represented by your uid_t type or a
> + * negative value or it will trigger wraparound bugs.

"sudo" generating a value higher than what uid_t can represent in
SUDO_UID *is* a bug that we shouldn't have to worry about.
Otherwise "sudo" as a tool would be unusable by folks with higher
UID on their system.

In their implementation of "sudo", they must have done getuid(),
stored the value in uid_t and formatted it into a string.  If they
lost precision there by wrapping around or truncating, we can do
nothing about it, but the thing is, we cannot even tell.

> + * If that happens the uid used might be incorrect and then trigger
> + * an access error from the filesystem itself.

If uid we are extracting is incorrect, Git will fail to refuse
access, the access is done as 'root', and filesystem level safety
will not trigger.  The end result is that I run "sudo git describe"
in your repository and instead of getting refused, because our "sudo"
was broken and SUDO_UID had your numeric uid, I execute "git" as root
in your repository.

IOW, this change is meant to make it slightly convenient to allow
access to one's own stuff, but it got a bit more convenient by
allowing me access to my own stuff plus yours ;-)

> + * In the unlikely scenario this happened to you, and that is how you
> + * got to this message, we would like to know about it by letting us
> + * now with an email to git@vger.kernel.org indicating which platform,
> + * you are running on and which version of sudo you used to see if we
> + * can provide you a patch that would prevent this issue in the future.

Nice.  What message does the reporter see?

> + */
> +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 below */

What "bug" are we referring to?

> +		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/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
> index 2e4492a66d..ecd9dca6b3 100755
> --- a/t/t0034-root-safe-directory.sh
> +++ b/t/t0034-root-safe-directory.sh
> @@ -29,7 +29,7 @@ test_expect_success SUDO 'setup' '
>  	)
>  '
>  
> -test_expect_failure SUDO 'sudo git status as original owner' '
> +test_expect_success SUDO 'sudo git status as original owner' '
>  	(
>  		cd root/r &&
>  		git status &&

OK.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-10 17:46               ` [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
@ 2022-05-10 23:11                 ` Junio C Hamano
  2022-05-10 23:25                   ` Junio C Hamano
  2022-05-11 14:04                   ` Carlo Arenas
  0 siblings, 2 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-10 23:11 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> Note that the specific test that documents that after the previous
> changes, it is no longer possible for root (if obtained through sudo)
> to NOT add an exception or NOT need a "workaround" to be able to run git
> commands in a repository owned by thyself, is marked as a regression
> and is expected to be fixed with a future change, which hasn't been
> provided yet and that is not part of this series.

Do you mean "you can easily unset SUDO_UID to access root-owned
repositories as root"?  Ahh, no, "after tentatively becoming root,
you can access your own (via SUDO_UID) and root-owned repositories"
is what you meant, I think.  I think that is reasonable to stop the
current round before adding the support for it.

> As described in the comments from the test itself, at least one of the
> documented workarounds could fail if sudo doesn't allow root to call sudo
> or if root is not in sudoers, but that is very unlikely, and more
> importantly actually not what is provided by the currently supported sudo
> configuration this test already relies on and therefore adding a way to
> validate it works in the prerequisite has been punted for now.

OK.

> diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
> new file mode 100644
> index 0000000000..d8a88fb9db
> --- /dev/null
> +++ b/t/lib-sudo.sh
> @@ -0,0 +1,12 @@
> +# 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"
> +	sudo "$TEST_SHELL_PATH" -c "\"$RUN\""

This is not wrong per-se, but I think

	sudo "$RUN"

would be sufficient, wouldn't it? 

> @@ -19,6 +26,12 @@ test_lazy_prereq SUDO '
>  	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

Quite sensible and understandable.

> @@ -37,6 +50,51 @@ test_expect_success SUDO 'sudo git status as original owner' '
>  	)
>  '
>  
> +test_expect_success SUDO 'setup root owned repository' '
> +	sudo mkdir -p root/p &&
> +	sudo git init root/p
> +'

OK, so 

	root/ is owned by 'root',
	root/r is owned by the tester, and
	root/p is owned by 'root'.

> +test_expect_success 'cannot access if owned by root' '
> +	(
> +		cd root/p &&
> +		test_must_fail git status
> +	)
> +'

OK.

Mark this point as [A] for future reference.

> +test_expect_failure SUDO 'can access with sudo if root' '
> +	(
> +		cd root/p &&
> +		sudo git status
> +	)
> +'

OK.


> +test_expect_success SUDO 'can access with sudo using a workaround' '
> +	# run sudo twice; would fail if root is not in sudoers

It probably is a good idea to check "you can run nested sudo" before
setting SUDO prerequisite.  Then we do not have to say "would fail"
here.

> +	(
> +		cd root/p &&
> +		sudo sudo git status
> +	) &&
> +	# provide explicit GIT_DIR
> +	(
> +		cd root/p &&
> +		run_with_sudo <<-END
> +			GIT_DIR=.git &&
> +			GIT_WORK_TREE=. &&
> +			export GIT_DIR GIT_WORK_TREE &&
> +			git status
> +		END
> +	) &&

OK.  We probably want to highlight that this "by default you can
only access your own repository" is *not* a security measure that
protects the repositories---it is a security measure to protect
you from potentially malicious repositories by unknowingly stepping
into them.  To do so, let's go back to point [A] and add a similar
"I as a tester can still access repository owned by root, as long as
I express that I want to access it explicitly" test after it. i.e.

	(
		cd root/p &&
		GIT_DIR=.git GIT_WORK_TREE=. git status
	)

> +	# discard SUDO_UID
> +	(
> +		cd root/p &&
> +		run_with_sudo <<-END
> +			unset SUDO_UID &&
> +			git status
> +		END
> +	)
> +'

OK.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 1/3] t: regression git needs safe.directory when using sudo
  2022-05-10 22:10                 ` Junio C Hamano
@ 2022-05-10 23:11                   ` Carlo Arenas
  2022-05-10 23:44                     ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-10 23:11 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin, SZEDER Gábor

On Tue, May 10, 2022 at 3:10 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
>
> > +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
> > +'
>
> I vaguely recall mentions of older dash that lack "command -v" made
> earlier, but implementations of dash I have handy seem to know it.
> I am personally fine with this as this script has a very narrow and
> limited audience in mind.

I did check that, but think the report was mistaken.
Debian, Ubuntu, NetBSD and OpenBSD would fail the same way here, but
it is not because of the use of dash, as much as sudo NOT being
configured to default to `-s` mode.

dscho was right to point out that I should had usen type instead, but
that wouldn't work because of the mismatch of shells and therefore the
mismatch of outputs, so I went with command instead as an extra clever
way to make sure both the shell inside and outside were most likely
the same, even if some sudo somewhere decides in the name of security
not to respect its own "-s mode" and force a "safer" shell.

I have a real fix for this which will be released later as part of
that "better integration with the framework", which basically makes
sure all invocations through sudo are done through the test shell
(just like that ugly function that gets added in patch 3), but it
requires changing write_shell and therefore not something that is
worth doing now.

> +test_expect_success SUDO 'setup' '
> > +     sudo rm -rf root &&
> > +     mkdir -p root/r &&
> > +     sudo chown root root &&
> > +     (
> > +             cd root/r &&
> > +             git init
> > +     )
> > +'
>
> So, "root/" is owned by root, "root/r" is owned by the tester.

It doesn't need to be root, but needs to be different than "tester",
and since I know root is different and I validated in the prerequisite
that I can sudo to it, that is what is used here.

> > +test_expect_failure SUDO 'sudo git status as original owner' '
> > +     (
> > +             cd root/r &&
> > +             git status &&
>
> The tester runs "git status" in "root/r" owned by the tester and it
> should succeed.
>
> > +             sudo git status
>
> We want the tester to be able to do the same while temporarily
> becoming 'root' with "sudo", but we know it fails right now.
>
> > +     )
> > +'
>
> Mental note.  We do not need root to be owned by 'root' with the
> tests we see here.  Perhaps we would add some that requires it in
> later patches.  We'll see.

I am not good with subtle messages in a foreign language, but is this
a way to imply that I shouldn't need to chown and instead use the
GIT_TEST_PRETEND feature more?

frankly I might had overused sudo, but it is because every extra
invocation refreshes the cache, and all tests depend on SUDO anyway,
so I wanted to make sure they were also more easily reconizable for
the real thing.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-10 23:11                 ` Junio C Hamano
@ 2022-05-10 23:25                   ` Junio C Hamano
  2022-05-11 14:04                   ` Carlo Arenas
  1 sibling, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-10 23:25 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin

Junio C Hamano <gitster@pobox.com> writes:

> OK, so 
>
> 	root/ is owned by 'root',
> 	root/r is owned by the tester, and
> 	root/p is owned by 'root'.

One thing I forgot.  In these three patches, I didn't see anything
that required root/ to be owned by 'root'.  So perhaps we can lose
the "chown root root" in the earlier test.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 1/3] t: regression git needs safe.directory when using sudo
  2022-05-10 23:11                   ` Carlo Arenas
@ 2022-05-10 23:44                     ` Junio C Hamano
  2022-05-11  0:56                       ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-10 23:44 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin, SZEDER Gábor

Carlo Arenas <carenas@gmail.com> writes:

>> > +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
>> > +'
>>
>> I vaguely recall mentions of older dash that lack "command -v" made
>> earlier, but implementations of dash I have handy seem to know it.
>> I am personally fine with this as this script has a very narrow and
>> limited audience in mind.
>
> I did check that, but think the report was mistaken.
> Debian, Ubuntu, NetBSD and OpenBSD would fail the same way here, but
> it is not because of the use of dash, as much as sudo NOT being
> configured to default to `-s` mode.

OK.

> dscho was right to point out that I should had usen type instead, but
> that wouldn't work because of the mismatch of shells and therefore the
> mismatch of outputs, so I went with command instead as an extra clever
> way to make sure both the shell inside and outside were most likely
> the same, even if some sudo somewhere decides in the name of security
> not to respect its own "-s mode" and force a "safer" shell.

In this particular case, "command -v" is the right thing to use, as
you care where the command is found on the $PATH and "type --path"
is *NOT* portable.

>> > +     sudo chown root root &&
>> > +     (
>> > +             cd root/r &&
>> > +             git init
>> > +     )
>> > +'
>>
>> So, "root/" is owned by root, "root/r" is owned by the tester.
>
> It doesn't need to be root, but needs to be different than "tester",

To make sure you do not misunderstand, I know the ownership of
root/r and root/p matter---tester and root must be different.  And
we use these two as sample repositories owned by two different
users.

What I am pointing out here is the root/ directory itself.  I do not
think its ownership does not matter anywhere in these new tests (not
just this patch, but after applying all three patches).

>> > +test_expect_failure SUDO 'sudo git status as original owner' '
>> > +     (
>> > +             cd root/r &&
>> > +             git status &&
>>
>> The tester runs "git status" in "root/r" owned by the tester and it
>> should succeed.
>>
>> > +             sudo git status
>>
>> We want the tester to be able to do the same while temporarily
>> becoming 'root' with "sudo", but we know it fails right now.
>>
>> > +     )
>> > +'
>>
>> Mental note.  We do not need root to be owned by 'root' with the
>> tests we see here.  Perhaps we would add some that requires it in
>> later patches.  We'll see.
>
> I am not good with subtle messages in a foreign language, but is this
> a way to imply that I shouldn't need to chown and instead use the
> GIT_TEST_PRETEND feature more?

No.  I just said I made a mental note of the fact that the ownership
of root/ directory (not root/r which is the other directory this
test script creates in this step) does not matter at least in patch
1/3, but I cannot say, by that observation alone, that chown we saw
earlier should be removed, because patches 2/3 and 3/3 might start
requiring root/ to be owned by 'root'.  Hence "I made a mental note
here.  We do not have anything that requires the above chown in this
patch, but we might see something that makes it a good idea to keep
the chown in later patches".

I do not think GIT_TEST_PRETEND needs to be involved at all.  It's
just root/ can be left owned by the tester, because we only care
about the owners of root/p and root/r in these three patches.

Thanks.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 1/3] t: regression git needs safe.directory when using sudo
  2022-05-10 23:44                     ` Junio C Hamano
@ 2022-05-11  0:56                       ` Carlo Arenas
  2022-05-11  1:11                         ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-11  0:56 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin, SZEDER Gábor

On Tue, May 10, 2022 at 4:44 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Arenas <carenas@gmail.com> writes:
> >>
> >> Mental note.  We do not need root to be owned by 'root' with the
> >> tests we see here.  Perhaps we would add some that requires it in
> >> later patches.  We'll see.
> >
> > I am not good with subtle messages in a foreign language, but is this
> > a way to imply that I shouldn't need to chown and instead use the
> > GIT_TEST_PRETEND feature more?
>
> No.  I just said I made a mental note of the fact that the ownership
> of root/ directory (not root/r which is the other directory this
> test script creates in this step) does not matter at least in patch
> 1/3, but I cannot say, by that observation alone, that chown we saw
> earlier should be removed, because patches 2/3 and 3/3 might start
> requiring root/ to be owned by 'root'.  Hence "I made a mental note
> here.  We do not have anything that requires the above chown in this
> patch, but we might see something that makes it a good idea to keep
> the chown in later patches".

got it; we actually DO (or at least I intended to, but didn't because
of a bug and complicating the tests unnecessarily).but then as usual I
didn't document it well, apologize for that.

`root` is called that way because it was meant to be a ceiling of
sorts and used as a safetynet (like GIT_CEILING in the test suite) to
block the tests in this file for going up one more level and using the
default git repository from the suite by mistake.  That is also why it
is owned by root.

of course later I find out that for it to really stop the walking it
needed a .git on its own and to create one I needed to do it as root
which I didn't even attempt to do until patch 3 (at that time too, I
was thinking maybe I could get patch 1 and 2 ready first, so my name
wouldn't be on every git update and one of the reasons why nobody can
get anything merged into next)

one option would be to consolidate its use with the root repository
that gets created in patch 3, which I could have done originally
instead of just using the ones you provided almost AS-IS, or as you
pointed out we could remove the safety net since it is not needed and
it is buggy anyway.

will remove the chown in v5 otherwise but I think the plan above
shouldn't be that intrusive and might be better.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 1/3] t: regression git needs safe.directory when using sudo
  2022-05-11  0:56                       ` Carlo Arenas
@ 2022-05-11  1:11                         ` Junio C Hamano
  0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-11  1:11 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin, SZEDER Gábor

Carlo Arenas <carenas@gmail.com> writes:

> one option would be to consolidate its use with the root repository
> that gets created in patch 3, which I could have done originally
> instead of just using the ones you provided almost AS-IS, or as you
> pointed out we could remove the safety net since it is not needed and
> it is buggy anyway.

I agree that there is no point to chown the directory, especially if
it does not offer any additional safety of any sort.  And quite
honstly, a test that is designed to be run ONLY in a very controlled
CI environment, it does not need one that complicates the tests.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-10 22:57                 ` Junio C Hamano
@ 2022-05-11  7:34                   ` Carlo Arenas
  2022-05-11 14:58                     ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-11  7:34 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

On Tue, May 10, 2022 at 3:57 PM Junio C Hamano <gitster@pobox.com> wrote:
> > Because of compatibility with sudo, the code assumes that uid_t is an
> > unsigned integer type (which is not required by the standard) but is used
> > that way in their codebase to generate SUDO_UID.
>
> Heh, that is a good point.

except it is an implementation detail we can't control, and that we
have no way to test for (at least yet and definitely in these series)

just because sudo upstream does this, doesn't mean the sudo that was
built in that specific system does as well, and because strto*l will
silently convert a value from one sign to another it might even "do
the right thing" (tm) even when a signed uid_t exist and a negative
one is valid.

but since we are using a larger integer type to hold those values we
are especially at risk of misbehaving when uid_t is signed because we
didn't want to use atoi (and really couldn't safely).

some systems provide UID_T_MAX as means to figure out (both if it is
signed or not and to truncate safely), and I presume there are others
where you can get that value at runtime with something like getconf(),
but as you pointed out all that is moot if we still run as root so has
been punted from this series and all that is left are these documented
issues, which I meant to get to and fix ASAP (but with changes that
will be more intrusive and wouldn't be safe for maint), but that I
also want to leave clearly visible as bus factor protection.

> > +When git tries to check for ownership of git repositories, it will
> > +obviously do so with the uid of the user that is running git itself,
>
> We do not need "obviously" here, but this has overlap with the
> beginning part of the safe.directory explanation, so I would
> probably suggest rewriting it altogether.

obviously

> > +but if git is running as root, in a platform that provides sudo and is
> > +not Windows, it will check first if it might have been started through
>
> Does Windows provide sudo that leaves the original user in SUDO_UID
> (I doubt it)?  If not, then "on a platform that provides sudo, it will"
> would be sufficient.

At least my windows box does not, but dscho's somehow had and so that
line was added at his request[1] after he wasted so much time trying
to get this to work and realized the function where SUDO_UID logic
resides doesn't even exist in a Windows build.

While I think it is probably ok to go, something that would save
someone else that trouble is probably worth having one way or another,
especially since he wasn't the only one while reviewing[2] this code,
that got it wrong, either.

> > +If that is not what you would prefer and want git to only trust
> > +repositories that are owned by root instead, then you should remove
> > +the `SUDO_UID` variable from root's environment.
>
> s/should/can/.  But otherwise this is excellent.

It weakens the message I wanted to apply to this as being a way to
signal intention, and also being kind of the "official" way to do so
for the future, but I agree it might be premature and it is obviously
not that relevant once the next thing we might add is your code that
fixes the "regression" and allows both root and SUDO_UID to work for
this code.

> > diff --git a/git-compat-util.h b/git-compat-util.h
> > index 63ba89dd31..754cd90d43 100644
> > --- a/git-compat-util.h
> > +++ b/git-compat-util.h
> > @@ -393,12 +393,64 @@ 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
> > +
> > +/*
> > + * This helper function overrides a ROOT_UID with the one provided by
> > + * an environment variable, do not use unless the original user is
> > + * root or could be used as means to escalate to root privileges.
>
> I do not understand the "or could be used ..." at all.  If the
> original user obtained from geteuid() is not root, then no matter
> what we do to *id here in this function, we won't let you gain the
> root privilege.  The system won't let us give you the root privilege
> because we (Git) are running as a normal user.

It is just there to explain the risks of using this function (which is
very visible) somewhere else, as well as to explain why it is only
meant to be used in a process running as root.

> Or do you mean
>
>         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.
>
> Then I can sort-of understand it, but (2) is a too obvious thing to
> say.

As usual I like your wording of it better, and yes I agree it is
obvious but since this function is very visible (which is not
necessary) and it is going to sit there in several maint releases, it
might not be that all unwarranted IMHO.

> > + * 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, but need to make sure sudo never generates a
> > + * value higher than what can be represented by your uid_t type or a
> > + * negative value or it will trigger wraparound bugs.
>
> "sudo" generating a value higher than what uid_t can represent in
> SUDO_UID *is* a bug that we shouldn't have to worry about.
> Otherwise "sudo" as a tool would be unusable by folks with higher
> UID on their system.

not necessarily; that system might had decided to use negative uids
instead of changing to unsigned and everything will still work, but
you are correct that in that case sudo printing the uid_t with "%u" is
a bug in that sudo and should be fixed there, but the irony is that if
fixed, would then cause a bug on our side since we assume uid was
always positive and was printed with "%u".

this whole message is there just to try to explain the portability
issue that having signed uid_t might trigger and that would be
unlikely to happen in real life as an alternative to try to do
something about it (like I did in previous versions), or plainly
refusing to run like you proposed (at least NONSTOP seems to have
signed uid_t), specially considering this is in a maint track,

I was thinking it might be something better done (in the future) and
with all the portability bits hidden away in a proper compat file
instead of here.

> In their implementation of "sudo", they must have done getuid(),
> stored the value in uid_t and formatted it into a string.  If they
> lost precision there by wrapping around or truncating, we can do
> nothing about it, but the thing is, we cannot even tell.

exactly, but we can warn the users that the problems they might have
with this feature might be because of that, and the information they
will provide could guide us into implementing a more portable version
of this code in the future.

and meanwhile, they might be able to keep a local patch which would be
really simple to write on their side.

> > + * If that happens the uid used might be incorrect and then trigger
> > + * an access error from the filesystem itself.
>
> If uid we are extracting is incorrect, Git will fail to refuse
> access, the access is done as 'root', and filesystem level safety
> will not trigger.

My bad, I didn't mean that the filesystem will trigger the error, but
that we might fail to match the uid_t we got from the filesystem with
the one we parsed from uid_t and therefore fail to allow access.

Hopefully that mismatch would be easy to spot as well because of a
compiler warning (like -Wsign-compare), or we might get lucky and
might even work (ex: sizeof(long) == sizeof(uid_t))

> The end result is that I run "sudo git describe"
> in your repository and instead of getting refused, because our "sudo"
> was broken and SUDO_UID had your numeric uid, I execute "git" as root
> in your repository.

That shouldn't happen unless the overflow was big enough to trigger an
ERANGE though, which also means uid_t would be much larger than the
expected uint32_t it seems to have almost everywhere I looked.

> IOW, this change is meant to make it slightly convenient to allow
> access to one's own stuff, but it got a bit more convenient by
> allowing me access to my own stuff plus yours ;-)

Well, you said before that since we are running as root you can
already have access to mine so it shouldn't matter ;)

> > + * In the unlikely scenario this happened to you, and that is how you
> > + * got to this message, we would like to know about it by letting us
> > + * now with an email to git@vger.kernel.org indicating which platform,
> > + * you are running on and which version of sudo you used to see if we
> > + * can provide you a patch that would prevent this issue in the future.
>
> Nice.  What message does the reporter see?

when using `sudo git status` a rejection of access to the directory they own

> > + */
> > +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 below */
>
> What "bug" are we referring to?

All of them, the ones we decided to ignore because they are irrelevant
when running as root, and the ones that are documented in the long
paragraph above.

Carlo

[1] https://lore.kernel.org/git/nycvar.QRO.7.76.6.2205051545370.355@tvgsbejvaqbjf.bet/
[2] https://lore.kernel.org/git/CAPUEspitAQrEjMpUyw8e2pyT1MT+h_dO5wSU0wWDWTqSye5TwA@mail.gmail.com/

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-10 23:11                 ` Junio C Hamano
  2022-05-10 23:25                   ` Junio C Hamano
@ 2022-05-11 14:04                   ` Carlo Arenas
  2022-05-11 15:29                     ` Junio C Hamano
  1 sibling, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-11 14:04 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin

On Tue, May 10, 2022 at 4:11 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:
>
> > Note that the specific test that documents that after the previous
> > changes, it is no longer possible for root (if obtained through sudo)
> > to NOT add an exception or NOT need a "workaround" to be able to run git
> > commands in a repository owned by thyself, is marked as a regression
> > and is expected to be fixed with a future change, which hasn't been
> > provided yet and that is not part of this series.
>
> Do you mean "you can easily unset SUDO_UID to access root-owned
> repositories as root"?  Ahh, no, "after tentatively becoming root,
> you can access your own (via SUDO_UID) and root-owned repositories"
> is what you meant, I think.  I think that is reasonable to stop the
> current round before adding the support for it.

I thought so too, but now I am not sure anymore; it would seem this is
a "regression" worth fixing by some (especially since there is little
appetite for behaviour changes since the last CVE that had a "fixup"
on top) and the code is available to do so, so will add it as an
"optional" patch on top and then we can decide.

> > --- /dev/null
> > +++ b/t/lib-sudo.sh
> > @@ -0,0 +1,12 @@
> > +# 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"
> > +     sudo "$TEST_SHELL_PATH" -c "\"$RUN\""
>
> This is not wrong per-se, but I think
>
>         sudo "$RUN"
>
> would be sufficient, wouldn't it?

only because currently we rely in a sudo that defaults to "-s" and
SHELL should be TEST_SHELL_PATH, but that wasn't explicitly tested by
the prerequisite, and so this just makes sure we ALWAYS use the right
shell, even if sudo might not want to normally.

BTW, sudo IS very opinionated, and just like it can ignore PATH when
it thinks that is the most secure option, can also ignore the #! line
in a shell script and use a more secure SHELL for the same reason, or
even not run ANY shell script, so by doing it this convoluted and
ackward way (as explained in the commit message) we ensure it works,
and works the right way, and is indeed how I was planning to "fix" the
'can we use it also when sudo doesn't default to "-s"', which is
obviously not part of this series, but a future one that should also
improve coverage for this test both in CI and for people brave enough
to try to run it locally.

I guess I will remove it in v5 and which seems better again as an RFC
to make sure we can iron out all remaining controversial things, but
thanks again for your thorough review.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-11  7:34                   ` Carlo Arenas
@ 2022-05-11 14:58                     ` Junio C Hamano
  0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-11 14:58 UTC (permalink / raw)
  To: Carlo Arenas
  Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin,
	Guy Maurel, SZEDER Gábor, Randall Becker

Carlo Arenas <carenas@gmail.com> writes:

>> Does Windows provide sudo that leaves the original user in SUDO_UID
>> (I doubt it)?  If not, then "on a platform that provides sudo, it will"
>> would be sufficient.
>
> At least my windows box does not, but dscho's somehow had and so that
> line was added at his request[1] after he wasted so much time trying
> to get this to work and realized the function where SUDO_UID logic
> resides doesn't even exist in a Windows build.

Ahh, of course.  

I forgot that this patch sent into is_path_owned_by_current_uid(),
as a UID as a value of some integer type is not a thing on Windows.
OK, so it does need to be as you wrote to mean "not on Windows, and
with 'sudo' that uses 'SUDO_UID' to record from whom 'root' came".

OK.

>> > + * In the unlikely scenario this happened to you, and that is how you
>> > + * got to this message, we would like to know about it by letting us
>> > + * now with an email to git@vger.kernel.org indicating which platform,
>> > + * you are running on and which version of sudo you used to see if we
>> > + * can provide you a patch that would prevent this issue in the future.
>>
>> Nice.  What message does the reporter see?
>
> when using `sudo git status` a rejection of access to the directory they own

It may have been obvious to who wrote the above comment, but it was
not, at least to me.

>> > + */
>> > +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 below */
>>
>> What "bug" are we referring to?
>
> All of them, the ones we decided to ignore because they are irrelevant
> when running as root, and the ones that are documented in the long
> paragraph above.

Likewise.  It did not click "a bug below" referred to "your sudo is
behaving in a way different from what we expected".

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-11 14:04                   ` Carlo Arenas
@ 2022-05-11 15:29                     ` Junio C Hamano
  0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-11 15:29 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: git, bagasdotme, phillip.wood123, Johannes.Schindelin

Carlo Arenas <carenas@gmail.com> writes:

>> > --- /dev/null
>> > +++ b/t/lib-sudo.sh
>> > @@ -0,0 +1,12 @@
>> > +# 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"
>> > +     sudo "$TEST_SHELL_PATH" -c "\"$RUN\""
>>
>> This is not wrong per-se, but I think
>>
>>         sudo "$RUN"
>>
>> would be sufficient, wouldn't it?
>
> only because currently we rely in a sudo that defaults to "-s" and
> SHELL should be TEST_SHELL_PATH, but that wasn't explicitly tested by
> the prerequisite, and so this just makes sure we ALWAYS use the right
> shell, even if sudo might not want to normally.

Ah, OK.  So giving "$TEST_SHELL_PATH" to write_script and then
running the resulting script explicitly with "$TEST_SHELL_PATH" is
belt-and-suspenders kind of defensiveness.  This was puzzling
without explanation, but starts making sense when explained.

Thanks.


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v5 0/4] fix `sudo make install` regression in maint
  2022-05-10 17:46             ` [PATCH " Carlo Marcelo Arenas Belón
                                 ` (2 preceding siblings ...)
  2022-05-10 17:46               ` [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
@ 2022-05-13  1:00               ` Carlo Marcelo Arenas Belón
  2022-05-13  1:00                 ` [PATCH v5 1/4] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
                                   ` (3 more replies)
  3 siblings, 4 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-13  1:00 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, johannes.Schindelin,
	Carlo Marcelo Arenas Belón

A reroll for cb/path-owner-check-with-sudo which should hopefully cover
for all feedback plus an extra (optional) patch to implement a fix for
the "regression" that this introduces

A range-diff against v4 provided below to easy review but the only code
changes come from the 4th patch which might be ironically the less
controversial even if not strictly needed.

The tests are not enabled by default, but a run of this on top of seen
that had an extra patch to enable them shows no failures[1].  Either way
they are only expected to run in the macOS agents because of the sudo
configuration that is needed, and which will require additional changes
to improve coverage.

Carlo Marcelo Arenas Belón (4):
  t: regression git needs safe.directory when using sudo
  git-compat-util: avoid failing dir ownership checks if running
    privileged
  t0034: add negative tests and allow git init to mostly work under sudo
  git-compat-util: allow root to access both SUDO_UID and root owned

 Documentation/config/safe.txt  | 14 +++++
 git-compat-util.h              | 58 ++++++++++++++++++++-
 t/lib-sudo.sh                  | 15 ++++++
 t/t0034-root-safe-directory.sh | 93 ++++++++++++++++++++++++++++++++++
 4 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 t/lib-sudo.sh
 create mode 100755 t/t0034-root-safe-directory.sh

[1] https://github.com/carenas/git/actions/runs/2316684806
-- 
1:  0096bb0968f ! 1:  2f17307e1a9 t: regression git needs safe.directory when using sudo
    @@ Commit message
           $ sudo rm -rf trash\ directory.t0034-root-safe-directory/
     
         The test file also uses at least one initial "setup" test that creates
    -    a parallel execution directory, while ignoring the repository created
    -    by the test framework.
    +    a parallel execution directory under the "root" sub directory, which
    +    should be used as top level directory for all repositories that are
    +    used in this test file.  Unlike all other tests the repository provided
    +    by the test framework should go unused.
     
         Special care should be taken when invoking commands through sudo, since
         the environment is otherwise independent from what the test framework
    @@ t/t0034-root-safe-directory.sh (new)
     +test_expect_success SUDO 'setup' '
     +	sudo rm -rf root &&
     +	mkdir -p root/r &&
    -+	sudo chown root root &&
     +	(
     +		cd root/r &&
     +		git init
2:  7ba248dace4 ! 2:  ccaa3f3c71d git-compat-util: avoid failing dir ownership checks if running privileged
    @@ Commit message
         that way in their codebase to generate SUDO_UID.  In systems where uid_t
         is signed, sudo might be also patched to NOT be unsigned and that might
         be able to trigger an edge case and a bug (as described in the code), but
    -    it is considered unlikely to be triggered, and even when it does the code
    -    would just safely fail, so there is no attempt either to detect it or
    -    prevent it in the code, which might need to be changed in the future.
    +    it is considered unlikely to happen and even if it does, the code would
    +    just mostly fail safely, so there was no attempt either to detect it or
    +    prevent it by the code, which is something that might change in the future,
    +    based on expected user feedback.
     
         Reported-by: Guy Maurel <guy.j@maurel.de>
         Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
    @@ Documentation/config/safe.txt: directory was listed in the `safe.directory` list
      initialize your list with an empty value before listing the repositories
      that you deem safe.
     ++
    -+When git tries to check for ownership of git repositories, it will
    -+obviously do so with the uid of the user that is running git itself,
    -+but if git is running as root, in a platform that provides sudo and is
    -+not Windows, it will check first if it might have been started through
    -+it, and if that is the case, will use the uid of the user that invoked
    -+sudo instead.
    ++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 should remove
    -+the `SUDO_UID` variable from root's environment.
    ++repositories that are owned by root instead, then you must remove
    ++the `SUDO_UID` variable from root's environment before invoking git.
     
      ## git-compat-util.h ##
     @@ git-compat-util.h: static inline int git_offset_1st_component(const char *path)
    @@ git-compat-util.h: static inline int git_offset_1st_component(const char *path)
     +#endif
     +
     +/*
    -+ * This helper function overrides a ROOT_UID with the one provided by
    -+ * an environment variable, do not use unless the original user is
    -+ * root or could be used as means to escalate to root privileges.
    ++ * 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, but need to make sure sudo never generates a
    -+ * value higher than what can be represented by your uid_t type or a
    -+ * negative value or it will trigger wraparound bugs.
    -+ *
    -+ * If that happens the uid used might be incorrect and then trigger
    -+ * an access error from the filesystem itself.
    -+ *
    ++ * 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 by letting us
    -+ * now with an email to git@vger.kernel.org indicating which platform,
    -+ * you are running on and which version of sudo you used to see if we
    -+ * can provide you a patch that would prevent this issue in the future.
    ++ * 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)
     +{
    @@ git-compat-util.h: static inline int git_offset_1st_component(const char *path)
     +		unsigned long env_id;
     +
     +		errno = 0;
    -+		/* silent overflow errors could trigger a bug below */
    ++		/* silent overflow errors could trigger a bug here */
     +		env_id = strtoul(real_uid, &endptr, 10);
     +		if (!*endptr && !errno)
     +			*id = env_id;
3:  1ed06acc93c ! 3:  2a9c0c20e08 t0034: add negative tests and allow git init to mostly work under sudo
    @@ Commit message
         Add additional negative tests as suggested by Junio and that use a
         new workspace that is owned by root.
     
    -    Note that the specific test that documents that after the previous
    -    changes, it is no longer possible for root (if obtained through sudo)
    -    to NOT add an exception or NOT need a "workaround" to be able to run git
    -    commands in a repository owned by thyself, is marked as a regression
    -    and is expected to be fixed with a future change, which hasn't been
    -    provided yet and that is not part of this series.
    +    Document a regression that was introduced by previous commits where
    +    root won't be able anymore to access directories they own unless
    +    SUDO_UID is removed from their environment.
     
    -    As described in the comments from the test itself, at least one of the
    -    documented workarounds could fail if sudo doesn't allow root to call sudo
    -    or if root is not in sudoers, but that is very unlikely, and more
    -    importantly actually not what is provided by the currently supported sudo
    -    configuration this test already relies on and therefore adding a way to
    -    validate it works in the prerequisite has been punted for now.
    +    The tests document additional ways that this new restriction could
    +    be worked around and the documentation explains why it might be instead
    +    considered a feature, but a "fix" is planned for a future change.
     
         Helped-by: Junio C Hamano <gitster@pobox.com>
         Helped-by: Phillip Wood <phillip.wood123@gmail.com>
    @@ t/lib-sudo.sh (new)
     +	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"
    @@ t/t0034-root-safe-directory.sh: test_expect_success SUDO 'sudo git status as ori
     +	)
     +'
     +
    -+test_expect_failure SUDO 'can access with sudo if root' '
    ++test_expect_success 'can access if addressed explicitly' '
     +	(
     +		cd root/p &&
    -+		sudo git status
    ++		GIT_DIR=.git GIT_WORK_TREE=. git status
     +	)
     +'
     +
    -+test_expect_success SUDO 'can access with sudo using a workaround' '
    -+	# run sudo twice; would fail if root is not in sudoers
    ++test_expect_failure SUDO 'can access with sudo if root' '
     +	(
     +		cd root/p &&
    -+		sudo sudo git status
    -+	) &&
    -+	# provide explicit GIT_DIR
    ++		sudo git status
    ++	)
    ++'
    ++
    ++test_expect_success SUDO 'can access with sudo if root by removing SUDO_UID' '
     +	(
     +		cd root/p &&
     +		run_with_sudo <<-END
    -+			GIT_DIR=.git &&
    -+			GIT_WORK_TREE=. &&
    -+			export GIT_DIR GIT_WORK_TREE &&
    ++			unset SUDO_UID &&
     +			git status
     +		END
    -+	) &&
    -+	# discard SUDO_UID
    ++	)
    ++'
    ++
    ++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 &&
    -+		run_with_sudo <<-END
    -+			unset SUDO_UID &&
    -+			git status
    -+		END
    ++		sudo sudo git status
     +	)
     +'
     +
-:  ----------- > 4:  8486777a417 git-compat-util: allow root to access both SUDO_UID and root owned
--
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v5 1/4] t: regression git needs safe.directory when using sudo
  2022-05-13  1:00               ` [PATCH v5 0/4] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
@ 2022-05-13  1:00                 ` Carlo Marcelo Arenas Belón
  2022-05-13  1:00                 ` [PATCH v5 2/4] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
                                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-13  1:00 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, johannes.Schindelin,
	Carlo Marcelo Arenas Belón, SZEDER Gábor, Phillip Wood

Originally reported after release of v2.35.2 (and other maint branches)
for CVE-2022-24765 and blocking otherwise harmless commands that were
done using sudo in a repository that was owned by the user.

Add a new test script with very basic support to allow running git
commands through sudo, so a reproduction could be implemented and that
uses only `git status` as a proxy of the issue reported.

Note that because of the way sudo interacts with the system, a much
more complete integration with the test framework will require a lot
more work and that was therefore intentionally punted for now.

The current implementation requires the execution of a special cleanup
function which should always be kept as the last "test" or otherwise
the standard cleanup functions will fail because they can't remove
the root owned directories that are used.  This also means that if
failures are found while running, the specifics of the failure might
not be kept for further debugging and if the test was interrupted, it
will be necessary to clean the working directory manually before
restarting by running:

  $ sudo rm -rf trash\ directory.t0034-root-safe-directory/

The test file also uses at least one initial "setup" test that creates
a parallel execution directory under the "root" sub directory, which
should be used as top level directory for all repositories that are
used in this test file.  Unlike all other tests the repository provided
by the test framework should go unused.

Special care should be taken when invoking commands through sudo, since
the environment is otherwise independent from what the test framework
setup and might have changed the values for HOME, SHELL and dropped
several relevant environment variables for your test.  Indeed `git status`
was used as a proxy because it doesn't even require commits in the
repository to work and usually doesn't require much from the environment
to run, but a future patch will add calls to `git init` and that will
fail to honor the default branch name, unless that setting is NOT
provided through an environment variable (which means even a CI run
could fail that test if enabled incorrectly).

A new SUDO prerequisite is provided that does some sanity checking
to make sure the sudo command that will be used allows for passwordless
execution as root without restrictions and doesn't mess with git's
execution path.  This matches what is provided by the macOS agents that
are used as part of GitHub actions and probably nowhere else.

Most of those characteristics make this test mostly only suitable for
CI, but it might be executed locally if special care is taken to provide
for all of them in the local configuration and maybe making use of the
sudo credential cache by first invoking sudo, entering your password if
needed, and then invoking the test with:

  $ GIT_TEST_ALLOW_SUDO=YES ./t0034-root-safe-directory.sh

If it fails to run, then it means your local setup wouldn't work for the
test because of the configuration sudo has or other system settings, and
things that might help are to comment out sudo's secure_path config, and
make sure that the account you are using has no restrictions on the
commands it can run through sudo, just like is provided for the user in
the CI.

For example (assuming a username of marta for you) something probably
similar to the following entry in your /etc/sudoers (or equivalent) file:

  marta	ALL=(ALL:ALL) NOPASSWD: ALL

Reported-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/t0034-root-safe-directory.sh | 44 ++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100755 t/t0034-root-safe-directory.sh

diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
new file mode 100755
index 00000000000..f6a5d63ff41
--- /dev/null
+++ b/t/t0034-root-safe-directory.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks while running as root'
+
+. ./test-lib.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
+
+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
+'
+
+test_expect_success SUDO 'setup' '
+	sudo rm -rf root &&
+	mkdir -p root/r &&
+	(
+		cd root/r &&
+		git init
+	)
+'
+
+test_expect_failure SUDO 'sudo git status as original owner' '
+	(
+		cd root/r &&
+		git status &&
+		sudo git status
+	)
+'
+
+# this MUST be always the last test
+test_expect_success SUDO 'cleanup' '
+	sudo rm -rf root
+'
+
+test_done
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v5 2/4] git-compat-util: avoid failing dir ownership checks if running privileged
  2022-05-13  1:00               ` [PATCH v5 0/4] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-13  1:00                 ` [PATCH v5 1/4] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
@ 2022-05-13  1:00                 ` Carlo Marcelo Arenas Belón
  2022-05-13  1:00                 ` [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
  2022-05-13  1:00                 ` [PATCH v5 4/4] git-compat-util: allow root to access both SUDO_UID and root owned Carlo Marcelo Arenas Belón
  3 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-13  1:00 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, johannes.Schindelin,
	Carlo Marcelo Arenas Belón, Guy Maurel, SZEDER Gábor,
	Randall Becker, Phillip Wood, Johannes Schindelin

bdc77d1d685 (Add a function to determine whether a path is owned by the
current user, 2022-03-02) checks for the effective uid of the running
process using geteuid() but didn't account for cases where that user was
root (because git was invoked through sudo or a compatible tool) and the
original uid that repository trusted for its config was no longer known,
therefore failing the following otherwise safe call:

  guy@renard ~/Software/uncrustify $ sudo git describe --always --dirty
  [sudo] password for guy:
  fatal: unsafe repository ('/home/guy/Software/uncrustify' is owned by someone else)

Attempt to detect those cases by using the environment variables that
those tools create to keep track of the original user id, and do the
ownership check using that instead.

This assumes the environment the user is running on after going
privileged can't be tampered with, and also adds code to restrict that
the new behavior only applies if running as root, therefore keeping the
most common case, which runs unprivileged, from changing, but because of
that, it will miss cases where sudo (or an equivalent) was used to change
to another unprivileged user or where the equivalent tool used to raise
privileges didn't track the original id in a sudo compatible way.

Because of compatibility with sudo, the code assumes that uid_t is an
unsigned integer type (which is not required by the standard) but is used
that way in their codebase to generate SUDO_UID.  In systems where uid_t
is signed, sudo might be also patched to NOT be unsigned and that might
be able to trigger an edge case and a bug (as described in the code), but
it is considered unlikely to happen and even if it does, the code would
just mostly fail safely, so there was no attempt either to detect it or
prevent it by the code, which is something that might change in the future,
based on expected user feedback.

Reported-by: Guy Maurel <guy.j@maurel.de>
Helped-by: SZEDER Gábor <szeder.dev@gmail.com>
Helped-by: Randall Becker <rsbecker@nexbridge.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Suggested-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/config/safe.txt  | 13 +++++++++
 git-compat-util.h              | 53 +++++++++++++++++++++++++++++++++-
 t/t0034-root-safe-directory.sh |  2 +-
 3 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index 6d764fe0ccf..c6ebd1674dd 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 63ba89dd31d..e7cbfa65c9a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -393,12 +393,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/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index f6a5d63ff41..6b8ea5357f6 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -28,7 +28,7 @@ test_expect_success SUDO 'setup' '
 	)
 '
 
-test_expect_failure SUDO 'sudo git status as original owner' '
+test_expect_success SUDO 'sudo git status as original owner' '
 	(
 		cd root/r &&
 		git status &&
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-13  1:00               ` [PATCH v5 0/4] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
  2022-05-13  1:00                 ` [PATCH v5 1/4] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
  2022-05-13  1:00                 ` [PATCH v5 2/4] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
@ 2022-05-13  1:00                 ` Carlo Marcelo Arenas Belón
  2022-05-13  1:20                   ` Junio C Hamano
  2022-05-13  1:00                 ` [PATCH v5 4/4] git-compat-util: allow root to access both SUDO_UID and root owned Carlo Marcelo Arenas Belón
  3 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-13  1:00 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, johannes.Schindelin,
	Carlo Marcelo Arenas Belón, Phillip Wood

Add a support library that provides one function that can be used
to run a "scriplet" of commands through sudo and that helps invoking
sudo in the slightly awkward way that is required to ensure it doesn't
block the call (if shell was allowed as tested in the prerequisite)
and it doesn't run the command through a different shell than the one
we intended.

Add additional negative tests as suggested by Junio and that use a
new workspace that is owned by root.

Document a regression that was introduced by previous commits where
root won't be able anymore to access directories they own unless
SUDO_UID is removed from their environment.

The tests document additional ways that this new restriction could
be worked around and the documentation explains why it might be instead
considered a feature, but a "fix" is planned for a future change.

Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 t/lib-sudo.sh                  | 15 ++++++++
 t/t0034-root-safe-directory.sh | 62 ++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 t/lib-sudo.sh

diff --git a/t/lib-sudo.sh b/t/lib-sudo.sh
new file mode 100644
index 00000000000..b4d7788f4e5
--- /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
index 6b8ea5357f6..a621f1ea5eb 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -3,6 +3,7 @@
 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
@@ -10,6 +11,12 @@ then
 	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 &&
@@ -19,6 +26,12 @@ test_lazy_prereq SUDO '
 	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 &&
@@ -36,6 +49,55 @@ test_expect_success SUDO 'sudo git status as original owner' '
 	)
 '
 
+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
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* [PATCH v5 4/4] git-compat-util: allow root to access both SUDO_UID and root owned
  2022-05-13  1:00               ` [PATCH v5 0/4] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
                                   ` (2 preceding siblings ...)
  2022-05-13  1:00                 ` [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
@ 2022-05-13  1:00                 ` Carlo Marcelo Arenas Belón
  3 siblings, 0 replies; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-13  1:00 UTC (permalink / raw)
  To: git
  Cc: gitster, bagasdotme, johannes.Schindelin,
	Carlo Marcelo Arenas Belón

Previous changes introduced a regression which will prevent root for
accessing repositories owned by thyself if using sudo because SUDO_UID
takes precedence.

Loosen that restriction by allowing root to access repositories owned
by both uid by default and without having to add a safe.directory
exception.

A previous workaround that was documented in the tests is no longer
needed so it has been removed together with its specially crafted
prerequisite.

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 Documentation/config/safe.txt  |  5 +++--
 git-compat-util.h              | 15 ++++++++++-----
 t/t0034-root-safe-directory.sh | 15 +--------------
 3 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/Documentation/config/safe.txt b/Documentation/config/safe.txt
index c6ebd1674dd..3128b132713 100644
--- a/Documentation/config/safe.txt
+++ b/Documentation/config/safe.txt
@@ -31,11 +31,12 @@ 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.
+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 must remove
+repositories that are owned by root instead, then you can 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 e7cbfa65c9a..0a5a4ee7a9a 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -420,9 +420,10 @@ static inline int git_offset_1st_component(const char *path)
  * 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)
+static inline int id_from_env_matches(const char *env, uid_t id)
 {
 	const char *real_uid = getenv(env);
+	int matches = 0;
 
 	/* discard anything empty to avoid a more complex check below */
 	if (real_uid && *real_uid) {
@@ -432,9 +433,10 @@ static inline void extract_id_from_env(const char *env, uid_t *id)
 		errno = 0;
 		/* silent overflow errors could trigger a bug here */
 		env_id = strtoul(real_uid, &endptr, 10);
-		if (!*endptr && !errno)
-			*id = env_id;
+		if (!*endptr && !errno && (uid_t)env_id == id)
+			matches = 1;
 	}
+	return matches;
 }
 
 static inline int is_path_owned_by_current_uid(const char *path)
@@ -446,10 +448,13 @@ static inline int is_path_owned_by_current_uid(const char *path)
 		return 0;
 
 	euid = geteuid();
+	if (st.st_uid == euid)
+		return 1;
+
 	if (euid == ROOT_UID)
-		extract_id_from_env("SUDO_UID", &euid);
+		return id_from_env_matches("SUDO_UID", st.st_uid);
 
-	return st.st_uid == euid;
+	return 0;
 }
 
 #define is_path_owned_by_current_user is_path_owned_by_current_uid
diff --git a/t/t0034-root-safe-directory.sh b/t/t0034-root-safe-directory.sh
index a621f1ea5eb..ff311761289 100755
--- a/t/t0034-root-safe-directory.sh
+++ b/t/t0034-root-safe-directory.sh
@@ -68,7 +68,7 @@ test_expect_success 'can access if addressed explicitly' '
 	)
 '
 
-test_expect_failure SUDO 'can access with sudo if root' '
+test_expect_success SUDO 'can access with sudo if root' '
 	(
 		cd root/p &&
 		sudo git status
@@ -85,19 +85,6 @@ test_expect_success SUDO 'can access with sudo if root by removing SUDO_UID' '
 	)
 '
 
-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
-- 
2.36.1.371.g0fb0ef0c8d


^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-13  1:00                 ` [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
@ 2022-05-13  1:20                   ` Junio C Hamano
  2022-05-14 14:36                     ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-13  1:20 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, johannes.Schindelin, Phillip Wood

Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> Add a support library that provides one function that can be used
> to run a "scriplet" of commands through sudo and that helps invoking
> sudo in the slightly awkward way that is required to ensure it doesn't
> block the call (if shell was allowed as tested in the prerequisite)
> and it doesn't run the command through a different shell than the one
> we intended.
>
> Add additional negative tests as suggested by Junio and that use a
> new workspace that is owned by root.
>
> Document a regression that was introduced by previous commits where
> root won't be able anymore to access directories they own unless
> SUDO_UID is removed from their environment.
>
> The tests document additional ways that this new restriction could
> be worked around and the documentation explains why it might be instead
> considered a feature, but a "fix" is planned for a future change.
>
> Helped-by: Junio C Hamano <gitster@pobox.com>
> Helped-by: Phillip Wood <phillip.wood123@gmail.com>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  t/lib-sudo.sh                  | 15 ++++++++
>  t/t0034-root-safe-directory.sh | 62 ++++++++++++++++++++++++++++++++++
>  2 files changed, 77 insertions(+)
>  create mode 100644 t/lib-sudo.sh

Heh.  I am a bit surprised that double sudo would become a separate
prerequisite, instead of a new part of SUDO prerequisite.  After all
we expect from SUDO prerequisite quite a lot (e.g. most sane
installatios facing end-users will futz with $PATH, but we require
not to do so to satisfy the SUDO prereq) and it is already very
narrowly targetted to a throw-away CI environment whose sudo
basically lets us do anything.

But that is not a serious enough "thing" to trigger a reroll.

This step looks good to me (others I'll comment later).

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-13  1:20                   ` Junio C Hamano
@ 2022-05-14 14:36                     ` Carlo Arenas
  2022-05-15 16:54                       ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-14 14:36 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, bagasdotme, johannes.Schindelin, Phillip Wood

On Thu, May 12, 2022 at 6:20 PM Junio C Hamano <gitster@pobox.com> wrote:
>
> Heh.  I am a bit surprised that double sudo would become a separate
> prerequisite,

It is because it goes away in the optional patch 4, since it won't be
needed anymore after that.
Also, it shared the same test with the other 2 workarounds which moved
into their own (one as you suggested is more of a statement of policy
that says, git trusts the developer and doesn't check if --git-dir or
--worktree (or equivalent means to explicitly identify those parts of
your repository) are used.

and the other, was always "special" since it was documented as an
indicator of what to do which could be considered a least privilege
marker as well.

without the optional patch that brings it back, root MUST indicate
through its use of that (or other "workarounds") that he really meant
to access a directory owned by root, and will instead defalt (when
appropriate) to use the id of the user that invoked sudo, which has
(normally) less privilege.

> of a new part of SUDO prerequisite.  After all
> we expect from SUDO prerequisite quite a lot (e.g. most sane
> installatios facing end-users will futz with $PATH, but we require
> not to do so to satisfy the SUDO prereq) and it is already very
> narrowly targetted to a throw-away CI environment whose sudo
> basically lets us do anything.

just because I didn't want this to become a bigger change that it was already
indeed I'd been "cutting" it since the very beginning, by first
dropping DOAS support and then avoiding moving things around so it
could be easy to backport.

I think I can provide a version of it that might be able to work with
less restrictions that it currently has, but that would get us into
the "test framework integration" that was specifically punted as well.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-14 14:36                     ` Carlo Arenas
@ 2022-05-15 16:54                       ` Junio C Hamano
  2022-05-15 19:21                         ` Carlo Arenas
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-15 16:54 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: git, bagasdotme, johannes.Schindelin, Phillip Wood

Carlo Arenas <carenas@gmail.com> writes:

> On Thu, May 12, 2022 at 6:20 PM Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Heh.  I am a bit surprised that double sudo would become a separate
>> prerequisite,
>
> It is because it goes away in the optional patch 4, since it won't be
> needed anymore after that.

Hmph, it may not be needed, but it should still work, in which case
it probably is still worth testing, even with the optional patch #4.

No?

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-15 16:54                       ` Junio C Hamano
@ 2022-05-15 19:21                         ` Carlo Arenas
  2022-05-16  5:27                           ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Arenas @ 2022-05-15 19:21 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, bagasdotme, johannes.Schindelin, Phillip Wood

On Sun, May 15, 2022 at 9:54 AM Junio C Hamano <gitster@pobox.com> wrote:
>
> Carlo Arenas <carenas@gmail.com> writes:
>
> > On Thu, May 12, 2022 at 6:20 PM Junio C Hamano <gitster@pobox.com> wrote:
> >>
> >> Heh.  I am a bit surprised that double sudo would become a separate
> >> prerequisite,
> >
> > It is because it goes away in the optional patch 4, since it won't be
> > needed anymore after that.
>
> Hmph, it may not be needed, but it should still work, in which case
> it probably is still worth testing, even with the optional patch #4.

Just because it works, it doesn't mean we have to test it.

IMHO tests are better reserved for things that might not work or that
should work, so this gets in the same bucket than "triple sudo" would
go, and interestingly enough we could add that test without having to
add a new prerequisite too!

> No?

Your call, my assumption was (since this patch is part of this series,
albeit optional), that the "double sudo" need will be short lived and
therefore better not to have this test to begin with, or remove it as
soon as the need is gone, which in practice would be the time between
when this series (without the optional patch) is released, and the
time that optional path gets released (maybe as part of another
series, since it might be dropped from this one).

alternatively we can make it not optional, and then the test will
NEVER be needed.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-15 19:21                         ` Carlo Arenas
@ 2022-05-16  5:27                           ` Junio C Hamano
  2022-05-16 13:07                             ` Carlo Marcelo Arenas Belón
  0 siblings, 1 reply; 161+ messages in thread
From: Junio C Hamano @ 2022-05-16  5:27 UTC (permalink / raw)
  To: Carlo Arenas; +Cc: git, bagasdotme, johannes.Schindelin, Phillip Wood

Carlo Arenas <carenas@gmail.com> writes:

>> Hmph, it may not be needed, but it should still work, in which case
>> it probably is still worth testing, even with the optional patch #4.
>
> Just because it works, it doesn't mean we have to test it.

Yes.  It all depends on the answer to this question: Is it
reasonably expected that any half-way intelligent Git user would not
be surprised to learn that "sudo sudo git status" would be a way to
work on a repository that is owned by root as root?  Given that
"sudo git status" is a good way to work on a repository that is
owned by you as root, perhaps the answer is yes, but I am not
a representative sample ;-)

If the answer is yes, then we would want to make sure it will
continue to work by having a test to protect it from future
breakage.  If not, and "sudo sudo git" (or worse "sudo sudo sudo
git") is something that would be imagined by the most wicked mind
and no sane person would imagine it would be a way to achieve
something useful, no, it does not have to be protected from any
future breakage.

So...

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-16  5:27                           ` Junio C Hamano
@ 2022-05-16 13:07                             ` Carlo Marcelo Arenas Belón
  2022-05-16 16:25                               ` Junio C Hamano
  0 siblings, 1 reply; 161+ messages in thread
From: Carlo Marcelo Arenas Belón @ 2022-05-16 13:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, bagasdotme, johannes.Schindelin, Phillip Wood

On Sun, May 15, 2022 at 10:27:04PM -0700, Junio C Hamano wrote:
> Carlo Arenas <carenas@gmail.com> writes:
> 
> >> Hmph, it may not be needed, but it should still work, in which case
> >> it probably is still worth testing, even with the optional patch #4.
> >
> > Just because it works, it doesn't mean we have to test it.
> 
> Yes.  It all depends on the answer to this question

Not quite, after all this is part of the "git" testsuite and therefore will
only apply if it would be testing git's functionality, and in this case it
does not.

More details below.

> Is it
> reasonably expected that any half-way intelligent Git user would not
> be surprised to learn that "sudo sudo git status" would be a way to
> work on a repository that is owned by root as root?  Given that
> "sudo git status" is a good way to work on a repository that is
> owned by you as root, perhaps the answer is yes, but I am not
> a representative sample ;-)
> 
> If the answer is yes, then we would want to make sure it will
> continue to work by having a test to protect it from future
> breakage.  If not, and "sudo sudo git" (or worse "sudo sudo sudo
> git") is something that would be imagined by the most wicked mind
> and no sane person would imagine it would be a way to achieve
> something useful, no, it does not have to be protected from any
> future breakage.

The answer is "yes", but it is because of a misunderstanding (which has
nothing to do with intelligence, but just experience with sudo and the type
of environment where it runs).

* sudo does NOT respect SUDO_UID, indeed is one of those few *NIX tools
  that doesn't even respect EUID but insist on only trusting the real id.
* once you run something through sudo, it creates an environment for you
  that is based on its security policy and not even the invoking user can
  change some of the parametersr it uses to do that, only "root" can.
* that means that once you invoke the first sudo, then the second runs as
  root and ignores the SUDO_UID the first sudo creates, so by the time git
  gets to run, it will only see the SUDO_UID that the one that invoked it
  creates, and since that sudo was running as root it MUST be the same than
  a root owned file/directory would use, hence why it works for that root
  owned repository and would fail in one that is owned by the original user.

there is no new functionality or code path difference inside git between the
first and second invocation of sudo, the only relevant difference is that
the starting environment from the two last processes in that triple chain
have different values for the SUDO_UID environment variable.

Carlo

^ permalink raw reply	[flat|nested] 161+ messages in thread

* Re: [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo
  2022-05-16 13:07                             ` Carlo Marcelo Arenas Belón
@ 2022-05-16 16:25                               ` Junio C Hamano
  0 siblings, 0 replies; 161+ messages in thread
From: Junio C Hamano @ 2022-05-16 16:25 UTC (permalink / raw)
  To: Carlo Marcelo Arenas Belón
  Cc: git, bagasdotme, johannes.Schindelin, Phillip Wood

Carlo Marcelo Arenas Belón <carenas@gmail.com> writes:

> On Sun, May 15, 2022 at 10:27:04PM -0700, Junio C Hamano wrote:
>> Carlo Arenas <carenas@gmail.com> writes:
>> 
>> >> Hmph, it may not be needed, but it should still work, in which case
>> >> it probably is still worth testing, even with the optional patch #4.
>> >
>> > Just because it works, it doesn't mean we have to test it.
>> 
>> Yes.  It all depends on the answer to this question
>
> Not quite, after all this is part of the "git" testsuite and therefore will
> only apply if it would be testing git's functionality, and in this case it
> does not.

It is immaterial if the way how "sudo sudo git" behaves is "git's
functionality" or not, because what we care about is what the end
user sees as a whole and it does not matter all that much to them
where the observed behaviour comes from.

The rule is simple.  If we care about the behaviour to stay with us
over time, we ensure it with a test.  If we are certain that no
users will depend on such a behaviour and are willing to break them
(i.e. users who depend on how "sudo sudo git" behaves, which is an
empty set) when we need to update the code, then we don't.

And if that changes with and without the optional patch #4, it makes
it more important to have test (if we care, that is).  Later we may
find what patch #4 does is detrimental to user experience and decide
to tweak it out (not necessarily with a revert of #4, but doing an
equivalent of reverting of it only in the code part and not tests).

In any case, as I said in the beginning, this was merely "a bit
surprised" and "not a serious enough thing to trigger a reroll", so
I will stop wasting our time on this thread.

Thanks.

^ permalink raw reply	[flat|nested] 161+ messages in thread

end of thread, other threads:[~2022-05-16 16:26 UTC | newest]

Thread overview: 161+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-04-26 18:31 [RFC PATCH] git-compat-util: avoid failing dir ownership checks if running priviledged Carlo Marcelo Arenas Belón
2022-04-26 19:48 ` Derrick Stolee
2022-04-26 19:56   ` Junio C Hamano
2022-04-26 20:10     ` rsbecker
2022-04-26 20:45       ` Carlo Arenas
2022-04-26 21:10         ` Junio C Hamano
2022-04-26 20:12     ` Carlo Arenas
2022-04-26 20:26   ` Carlo Arenas
2022-04-29 16:16   ` Derrick Stolee
2022-04-27  0:05 ` [PATCH] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
2022-04-27  9:33   ` Phillip Wood
2022-04-27 12:30     ` Phillip Wood
2022-04-27 14:15       ` rsbecker
2022-04-27 15:58       ` Carlo Arenas
2022-04-27 16:14         ` Phillip Wood
2022-04-27 18:54           ` Junio C Hamano
2022-04-27 20:59             ` Carlo Arenas
2022-04-27 21:09               ` rsbecker
2022-04-27 21:25               ` Junio C Hamano
2022-04-28 17:56             ` Phillip Wood
2022-04-27 15:38     ` Carlo Arenas
2022-04-27 15:50       ` rsbecker
2022-04-27 16:19       ` Junio C Hamano
2022-04-27 16:45         ` Carlo Arenas
2022-04-27 17:22         ` Phillip Wood
2022-04-27 17:49           ` rsbecker
2022-04-27 17:54             ` Carlo Arenas
2022-04-27 18:05               ` rsbecker
2022-04-27 18:11                 ` Carlo Arenas
2022-04-27 18:16                   ` rsbecker
2022-04-27 16:31       ` Phillip Wood
2022-04-27 16:54         ` Carlo Arenas
2022-04-27 17:28           ` Phillip Wood
2022-04-27 17:49             ` Carlo Arenas
2022-04-27 22:26   ` [RFC PATCH v2] " Carlo Marcelo Arenas Belón
2022-04-27 22:33     ` Junio C Hamano
2022-04-28  3:35     ` [PATCH 0/2] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
2022-04-28  3:35       ` [PATCH 1/2] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
2022-04-28  5:17         ` Junio C Hamano
2022-04-28  5:58           ` Carlo Arenas
2022-04-28  6:41             ` Junio C Hamano
2022-04-28  3:35       ` [PATCH 2/2] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
2022-04-28  5:34         ` Junio C Hamano
2022-04-28  4:57       ` [PATCH 0/2] fix `sudo make install` regression in maint Junio C Hamano
2022-04-28 10:58       ` [PATCH v2 0/3] " Carlo Marcelo Arenas Belón
2022-04-28 10:58         ` [PATCH v2 1/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
2022-04-28 18:02           ` Phillip Wood
2022-04-28 18:57             ` Carlo Arenas
2022-04-28 10:58         ` [PATCH v2 2/3] Documentation: explain how safe.directory works when running under sudo Carlo Marcelo Arenas Belón
2022-04-30  6:17           ` Bagas Sanjaya
2022-04-30  6:39             ` Junio C Hamano
2022-04-30 14:15             ` Carlo Marcelo Arenas Belón
2022-04-28 10:58         ` [PATCH v2 3/3] t: add tests for safe.directory when running with sudo Carlo Marcelo Arenas Belón
2022-04-28 16:55           ` Junio C Hamano
2022-04-28 18:08             ` Phillip Wood
2022-04-28 18:12               ` Junio C Hamano
2022-05-06 17:50                 ` Carlo Arenas
2022-05-06 21:43                   ` Junio C Hamano
2022-05-06 22:57                     ` Carlo Arenas
2022-05-06 23:55                       ` Junio C Hamano
2022-05-07 11:57                         ` Carlo Marcelo Arenas Belón
2022-04-28 19:53             ` rsbecker
2022-04-28 20:22               ` Carlo Arenas
2022-04-28 20:43                 ` rsbecker
2022-04-28 20:51                   ` Junio C Hamano
2022-04-28 20:56                   ` Carlo Arenas
2022-04-28 21:55                     ` rsbecker
2022-04-28 22:21                       ` Junio C Hamano
2022-04-28 22:45                         ` rsbecker
2022-04-28 20:46                 ` Junio C Hamano
2022-04-28 20:32               ` Junio C Hamano
2022-04-28 20:40                 ` rsbecker
2022-04-28 20:48                 ` Carlo Arenas
2022-04-28 21:02             ` Carlo Arenas
2022-04-28 21:07               ` Junio C Hamano
2022-04-29  1:24                 ` Carlo Marcelo Arenas Belón
2022-04-29 18:50                   ` Junio C Hamano
2022-04-29 20:05                     ` Carlo Marcelo Arenas Belón
2022-05-02 18:39         ` [RFC PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
2022-05-02 18:39           ` [RFC PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
2022-05-02 21:35             ` Junio C Hamano
2022-05-02 23:07               ` Carlo Arenas
2022-05-02 18:39           ` [RFC PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
2022-05-02 18:39           ` [RFC PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
2022-05-02 22:10             ` Junio C Hamano
2022-05-03  0:00               ` Carlo Arenas
2022-05-03  6:54         ` [PATCH v3 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
2022-05-03  6:54           ` [PATCH v3 1/3] t: document regression git safe.directory when using sudo Carlo Marcelo Arenas Belón
2022-05-03 14:03             ` Phillip Wood
2022-05-03 15:56               ` Carlo Marcelo Arenas Belón
2022-05-04 11:15                 ` Phillip Wood
2022-05-04 13:02                   ` Carlo Arenas
2022-05-04 14:11                     ` Phillip Wood
2022-05-05 13:44             ` Johannes Schindelin
2022-05-05 14:34               ` Phillip Wood
2022-05-05 15:50               ` Junio C Hamano
2022-05-05 18:33               ` Junio C Hamano
2022-05-05 19:39                 ` Junio C Hamano
2022-05-06 21:03                   ` Carlo Arenas
2022-05-09  8:21                 ` Phillip Wood
2022-05-09 14:51                   ` Carlo Arenas
2022-05-09 15:18                     ` Phillip Wood
2022-05-09 16:01                   ` Junio C Hamano
2022-05-09 16:21                     ` Carlo Arenas
2022-05-06 17:39               ` Carlo Arenas
2022-05-03  6:54           ` [PATCH v3 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
2022-05-05 14:01             ` Johannes Schindelin
2022-05-05 14:32               ` Phillip Wood
2022-05-06 19:15                 ` Carlo Arenas
2022-05-06 20:00                   ` Junio C Hamano
2022-05-06 20:22                     ` Carlo Arenas
2022-05-06 20:59                       ` Junio C Hamano
2022-05-06 21:40                         ` Carlo Arenas
2022-05-06 21:07                       ` rsbecker
2022-05-05 16:09               ` Junio C Hamano
2022-05-06 20:02               ` Carlo Arenas
2022-05-03  6:54           ` [PATCH v3 3/3] t0034: enhance framework to allow testing more commands under sudo Carlo Marcelo Arenas Belón
2022-05-03 14:12             ` Phillip Wood
2022-05-03 15:27               ` Junio C Hamano
2022-05-06 16:54               ` Carlo Arenas
2022-05-07 16:35           ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
2022-05-07 16:35             ` [RFC PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
2022-05-07 16:35             ` [RFC PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
2022-05-07 17:34               ` Junio C Hamano
2022-05-07 18:56                 ` Carlo Marcelo Arenas Belón
2022-05-09 16:54                   ` Junio C Hamano
2022-05-09 17:36                     ` rsbecker
2022-05-09 18:48                     ` Carlo Arenas
2022-05-09 19:16                       ` rsbecker
2022-05-09 19:41                       ` Junio C Hamano
2022-05-07 16:35             ` [RFC PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
2022-05-10 14:17             ` [RFC PATCH v4 0/3] fix `sudo make install` regression in maint Phillip Wood
2022-05-10 15:47               ` Carlo Arenas
2022-05-10 17:46             ` [PATCH " Carlo Marcelo Arenas Belón
2022-05-10 17:46               ` [PATCH v4 1/3] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
2022-05-10 22:10                 ` Junio C Hamano
2022-05-10 23:11                   ` Carlo Arenas
2022-05-10 23:44                     ` Junio C Hamano
2022-05-11  0:56                       ` Carlo Arenas
2022-05-11  1:11                         ` Junio C Hamano
2022-05-10 17:46               ` [PATCH v4 2/3] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
2022-05-10 22:57                 ` Junio C Hamano
2022-05-11  7:34                   ` Carlo Arenas
2022-05-11 14:58                     ` Junio C Hamano
2022-05-10 17:46               ` [PATCH v4 3/3] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
2022-05-10 23:11                 ` Junio C Hamano
2022-05-10 23:25                   ` Junio C Hamano
2022-05-11 14:04                   ` Carlo Arenas
2022-05-11 15:29                     ` Junio C Hamano
2022-05-13  1:00               ` [PATCH v5 0/4] fix `sudo make install` regression in maint Carlo Marcelo Arenas Belón
2022-05-13  1:00                 ` [PATCH v5 1/4] t: regression git needs safe.directory when using sudo Carlo Marcelo Arenas Belón
2022-05-13  1:00                 ` [PATCH v5 2/4] git-compat-util: avoid failing dir ownership checks if running privileged Carlo Marcelo Arenas Belón
2022-05-13  1:00                 ` [PATCH v5 3/4] t0034: add negative tests and allow git init to mostly work under sudo Carlo Marcelo Arenas Belón
2022-05-13  1:20                   ` Junio C Hamano
2022-05-14 14:36                     ` Carlo Arenas
2022-05-15 16:54                       ` Junio C Hamano
2022-05-15 19:21                         ` Carlo Arenas
2022-05-16  5:27                           ` Junio C Hamano
2022-05-16 13:07                             ` Carlo Marcelo Arenas Belón
2022-05-16 16:25                               ` Junio C Hamano
2022-05-13  1:00                 ` [PATCH v5 4/4] git-compat-util: allow root to access both SUDO_UID and root owned Carlo Marcelo Arenas Belón

Code repositories for project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).