From: Stefan Sperling Subject: hide private gotd repositories from anonymous users To: gameoftrees@openbsd.org Date: Mon, 6 May 2024 16:25:47 +0200 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=$?