Skip to content

Commit 3b0bf27

Browse files
carenasdscho
authored andcommitted
setup: tighten ownership checks post CVE-2022-24765
8959555 (setup_git_directory(): add an owner check for the top-level directory, 2022-03-02), adds a function to check for ownership of repositories using a directory that is representative of it, and ways to add exempt a specific repository from said check if needed, but that check didn't account for owership of the gitdir, or (when used) the gitfile that points to that gitdir. An attacker could create a git repository in a directory that they can write into but that is owned by the victim to work around the fix that was introduced with CVE-2022-24765 to potentially run code as the victim. An example that could result in privilege escalation to root in *NIX would be to set a repository in a shared tmp directory by doing (for example): $ git -C /tmp init To avoid that, extend the ensure_valid_ownership function to be able to check for all three paths. This will have the side effect of tripling the number of stat() calls when a repository is detected, but the effect is expected to be likely minimal, as it is done only once during the directory walk in which Git looks for a repository. Additionally make sure to resolve the gitfile (if one was used) to find the relevant gitdir for checking. While at it change the message printed on failure so it is clear we are referring to the repository by its worktree (or gitdir if it is bare) and not to a specific directory. Helped-by: Junio C Hamano <[email protected]> Helped-by: Johannes Schindelin <[email protected]> Signed-off-by: Carlo Marcelo Arenas Belón <[email protected]>
1 parent b779214 commit 3b0bf27

File tree

1 file changed

+60
-11
lines changed

1 file changed

+60
-11
lines changed

setup.c

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,14 +1054,32 @@ static int safe_directory_cb(const char *key, const char *value, void *d)
10541054
return 0;
10551055
}
10561056

1057-
static int ensure_valid_ownership(const char *path)
1057+
/*
1058+
* Check if a repository is safe, by verifying the ownership of the
1059+
* worktree (if any), the git directory, and the gitfile (if any).
1060+
*
1061+
* Exemptions for known-safe repositories can be added via `safe.directory`
1062+
* config settings; for non-bare repositories, their worktree needs to be
1063+
* added, for bare ones their git directory.
1064+
*/
1065+
static int ensure_valid_ownership(const char *gitfile,
1066+
const char *worktree, const char *gitdir)
10581067
{
1059-
struct safe_directory_data data = { .path = path };
1068+
struct safe_directory_data data = {
1069+
.path = worktree ? worktree : gitdir
1070+
};
10601071

10611072
if (!git_env_bool("GIT_TEST_ASSUME_DIFFERENT_OWNER", 0) &&
1062-
is_path_owned_by_current_user(path))
1073+
(!gitfile || is_path_owned_by_current_user(gitfile)) &&
1074+
(!worktree || is_path_owned_by_current_user(worktree)) &&
1075+
(!gitdir || is_path_owned_by_current_user(gitdir)))
10631076
return 1;
10641077

1078+
/*
1079+
* data.path is the "path" that identifies the repository and it is
1080+
* constant regardless of what failed above. data.is_safe should be
1081+
* initialized to false, and might be changed by the callback.
1082+
*/
10651083
read_very_early_config(safe_directory_cb, &data);
10661084

10671085
return data.is_safe;
@@ -1149,6 +1167,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
11491167
current_device = get_device_or_die(dir->buf, NULL, 0);
11501168
for (;;) {
11511169
int offset = dir->len, error_code = 0;
1170+
char *gitdir_path = NULL;
1171+
char *gitfile = NULL;
11521172

11531173
if (offset > min_offset)
11541174
strbuf_addch(dir, '/');
@@ -1159,21 +1179,50 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
11591179
if (die_on_error ||
11601180
error_code == READ_GITFILE_ERR_NOT_A_FILE) {
11611181
/* NEEDSWORK: fail if .git is not file nor dir */
1162-
if (is_git_directory(dir->buf))
1182+
if (is_git_directory(dir->buf)) {
11631183
gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
1184+
gitdir_path = xstrdup(dir->buf);
1185+
}
11641186
} else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
11651187
return GIT_DIR_INVALID_GITFILE;
1166-
}
1188+
} else
1189+
gitfile = xstrdup(dir->buf);
1190+
/*
1191+
* Earlier, we tentatively added DEFAULT_GIT_DIR_ENVIRONMENT
1192+
* to check that directory for a repository.
1193+
* Now trim that tentative addition away, because we want to
1194+
* focus on the real directory we are in.
1195+
*/
11671196
strbuf_setlen(dir, offset);
11681197
if (gitdirenv) {
1169-
if (!ensure_valid_ownership(dir->buf))
1170-
return GIT_DIR_INVALID_OWNERSHIP;
1171-
strbuf_addstr(gitdir, gitdirenv);
1172-
return GIT_DIR_DISCOVERED;
1198+
enum discovery_result ret;
1199+
1200+
if (ensure_valid_ownership(gitfile,
1201+
dir->buf,
1202+
(gitdir_path ? gitdir_path : gitdirenv))) {
1203+
strbuf_addstr(gitdir, gitdirenv);
1204+
ret = GIT_DIR_DISCOVERED;
1205+
} else
1206+
ret = GIT_DIR_INVALID_OWNERSHIP;
1207+
1208+
/*
1209+
* Earlier, during discovery, we might have allocated
1210+
* string copies for gitdir_path or gitfile so make
1211+
* sure we don't leak by freeing them now, before
1212+
* leaving the loop and function.
1213+
*
1214+
* Note: gitdirenv will be non-NULL whenever these are
1215+
* allocated, therefore we need not take care of releasing
1216+
* them outside of this conditional block.
1217+
*/
1218+
free(gitdir_path);
1219+
free(gitfile);
1220+
1221+
return ret;
11731222
}
11741223

11751224
if (is_git_directory(dir->buf)) {
1176-
if (!ensure_valid_ownership(dir->buf))
1225+
if (!ensure_valid_ownership(NULL, NULL, dir->buf))
11771226
return GIT_DIR_INVALID_OWNERSHIP;
11781227
strbuf_addstr(gitdir, ".");
11791228
return GIT_DIR_BARE;
@@ -1306,7 +1355,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
13061355
struct strbuf quoted = STRBUF_INIT;
13071356

13081357
sq_quote_buf_pretty(&quoted, dir.buf);
1309-
die(_("unsafe repository ('%s' is owned by someone else)\n"
1358+
die(_("detected dubious ownership in repository at '%s'\n"
13101359
"To add an exception for this directory, call:\n"
13111360
"\n"
13121361
"\tgit config --global --add safe.directory %s"),

0 commit comments

Comments
 (0)