"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
hide private gotd repositories from anonymous users
To:
gameoftrees@openbsd.org
Date:
Mon, 6 May 2024 16:25:47 +0200

Download raw body.

Thread
When mixing public and private repositories in the same gotd instance,
anonymous users can guess names of private repositories and use gotd as
an oracle about their existence by running: got clone -l $url/foo.git

If foo.git does not exist then gotd returns "no git repository found".
However, if foo.git does exist gotd returns "foo: Permission denied".

This patch prevents leaking the names of private repositories.
An explicit permission error will now be returned only if the connecting
user matches a permit or deny rule for the requested repository.

ok?

-----------------------------------------------
 don't leak the existence of gotd repositories to unrelated user accounts
 
 In particular, this prevents anonymous user accounts from discovering
 the existence of other private repositories served by gotd by correctly
 guessing the name of a private repository. They still wouldn't have read
 or write access but in some cases even knowledge about the existence of
 a particular repository could be cause for concern.
 
 M  gotd/auth.c                              |  12+  2-
 M  regress/gotd/Makefile                    |   1+  0-
 M  regress/gotd/repo_read_access_denied.sh  |   9+  2-
 M  regress/gotd/repo_write_readonly.sh      |  10+  2-

4 files changed, 32 insertions(+), 6 deletions(-)

diff dab6d6fcbcba4b0f05c2297301e384ea58e26a33 acdcedd90a93d49613793c2668cc0eff4eb531c9
commit - dab6d6fcbcba4b0f05c2297301e384ea58e26a33
commit + acdcedd90a93d49613793c2668cc0eff4eb531c9
blob - 46bcfeef428d4ba1125f8851a58966b8bf62c96d
blob + 66b5696fc0fe4bca89de497a5be81b41c9d65b52
--- gotd/auth.c
+++ gotd/auth.c
@@ -136,6 +136,7 @@ auth_check(char **username, struct gotd_access_rule_li
 	struct passwd *pw;
 	gid_t groups[NGROUPS_MAX];
 	int ngroups = NGROUPS_MAX;
+	int matched_user = 0;
 
 	*username = NULL;
 
@@ -159,14 +160,23 @@ auth_check(char **username, struct gotd_access_rule_li
 		    euid, egid))
 			continue;
 
+		matched_user = 1;
 		access = rule->access;
 		if (rule->access == GOTD_ACCESS_PERMITTED &&
 		    (rule->authorization & required_auth) != required_auth)
 			access = GOTD_ACCESS_DENIED;
 	}
 
-	if (access == GOTD_ACCESS_DENIED)
-		return got_error_set_errno(EACCES, repo_name);
+	if (access == GOTD_ACCESS_DENIED) {
+		/*
+		 * If a user has no explicit read or write access then
+		 * do not leak the existence of a repository to them.
+		 */
+		if (!matched_user)
+			return got_error(GOT_ERR_NOT_GIT_REPO);
+		else
+			return got_error_set_errno(EACCES, repo_name);
+	}
 
 	if (access == GOTD_ACCESS_PERMITTED)
 		return NULL;
blob - 4482c735241818f4fa534feaaf319e44c6d23018
blob + 8d1fea32686e4a7c678467f1b9cdccf2ec79fb66
--- regress/gotd/Makefile
+++ regress/gotd/Makefile
@@ -55,6 +55,7 @@ GOTD_TEST_ENV=GOTD_TEST_ROOT=$(GOTD_TEST_ROOT) \
 	GOTD_SOCK=$(GOTD_SOCK) \
 	GOTD_DEVUSER=$(GOTD_DEVUSER) \
 	GOTD_USER=$(GOTD_USER) \
+	GOTD_CONF=$(PWD)/gotd.conf \
 	GOTD_TEST_SMTP_PORT=$(GOTD_TEST_SMTP_PORT) \
 	GOTD_TEST_HTTP_PORT=$(GOTD_TEST_HTTP_PORT) \
 	HOME=$(GOTD_TEST_USER_HOME) \
blob - f1eaa263ded2de0987d31ec852c1c021a68df9cc
blob + 734479f1d4bb418a9468c905aed529752d696062
--- regress/gotd/repo_read_access_denied.sh
+++ regress/gotd/repo_read_access_denied.sh
@@ -33,8 +33,15 @@ test_clone_basic_access_denied() {
 	grep ^got-fetch-pack: $testroot/stderr.raw > $testroot/stderr
 
 	# Verify that the clone operation failed.
-	echo 'got-fetch-pack: test-repo: Permission denied' \
-		> $testroot/stderr.expected
+	# The error returned will differ depending on whether read access
+	# is denied explicitly for GOTD_DEVUSER.
+	if grep -q "permit.*${GOTD_DEVUSER}$" $GOTD_CONF; then
+		echo 'got-fetch-pack: test-repo: Permission denied' \
+			> $testroot/stderr.expected
+	else
+		echo 'got-fetch-pack: no git repository found' \
+			> $testroot/stderr.expected
+	fi
 	cmp -s $testroot/stderr.expected $testroot/stderr
 	ret=$?
 	if [ $ret -ne 0 ]; then
blob - 9bfba2cfd281497c7e9b811178f4b534d191cc7e
blob + c8b3337da837dcd5501b30cd8f9fc57f52366777
--- regress/gotd/repo_write_readonly.sh
+++ regress/gotd/repo_write_readonly.sh
@@ -58,8 +58,16 @@ EOF
 		return 1
 	fi
 
-	echo "got-send-pack: test-repo: Permission denied" \
-		> $testroot/stderr.expected
+	# Verify that the send operation failed.
+	# The error returned will differ depending on whether read access
+	# is denied explicitly for GOTD_DEVUSER.
+	if grep -q "permit.*${GOTD_DEVUSER}$" $GOTD_CONF; then
+		echo "got-send-pack: test-repo: Permission denied" \
+			> $testroot/stderr.expected
+	else
+		echo 'got-send-pack: no git repository found' \
+			> $testroot/stderr.expected
+	fi
 	grep '^got-send-pack:' $testroot/stderr > $testroot/stderr.filtered
 	cmp -s $testroot/stderr.expected $testroot/stderr.filtered
 	ret=$?