Skip to content

Commit 7f6c1ce

Browse files
authored
Merge pull request libgit2#4660 from libgit2/cmn/submodule-traversal
Fixes for CVE 2018-11235
2 parents d050acf + 491722e commit 7f6c1ce

File tree

18 files changed

+793
-239
lines changed

18 files changed

+793
-239
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ v0.27 + 1
66
* The line-ending filtering logic - when checking out files - has been
77
updated to match newer git (>= git 2.9) for proper interoperability.
88

9+
* Submodules with names which attempt to perform path traversal now have their
10+
configuration ignored. Such names were blindly appended to the
11+
`$GIT_DIR/modules` and a malicious name could lead to an attacker writing to
12+
an arbitrary location. This matches git's handling of CVE-2018-11235.
13+
914
### API additions
1015

1116
### API removals
@@ -14,6 +19,10 @@ v0.27 + 1
1419

1520
* The default checkout strategy changed from `DRY_RUN` to `SAFE` (#4531).
1621

22+
* Adding a symlink as .gitmodules into the index from the workdir or checking
23+
out such files is not allowed as this can make a Git implementation write
24+
outside of the repository and bypass the fsck checks for CVE-2018-11235.
25+
1726
v0.27
1827
---------
1928

src/checkout.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,14 +1276,14 @@ static int checkout_verify_paths(
12761276
unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS;
12771277

12781278
if (action & CHECKOUT_ACTION__REMOVE) {
1279-
if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
1279+
if (!git_path_isvalid(repo, delta->old_file.path, delta->old_file.mode, flags)) {
12801280
giterr_set(GITERR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path);
12811281
return -1;
12821282
}
12831283
}
12841284

12851285
if (action & ~CHECKOUT_ACTION__REMOVE) {
1286-
if (!git_path_isvalid(repo, delta->new_file.path, flags)) {
1286+
if (!git_path_isvalid(repo, delta->new_file.path, delta->new_file.mode, flags)) {
12871287
giterr_set(GITERR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path);
12881288
return -1;
12891289
}

src/index.c

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -884,20 +884,24 @@ static int index_entry_create(
884884
git_index_entry **out,
885885
git_repository *repo,
886886
const char *path,
887+
struct stat *st,
887888
bool from_workdir)
888889
{
889890
size_t pathlen = strlen(path), alloclen;
890891
struct entry_internal *entry;
891892
unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS;
893+
uint16_t mode = 0;
892894

893895
/* always reject placing `.git` in the index and directory traversal.
894896
* when requested, disallow platform-specific filenames and upgrade to
895897
* the platform-specific `.git` tests (eg, `git~1`, etc).
896898
*/
897899
if (from_workdir)
898900
path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS;
901+
if (st)
902+
mode = st->st_mode;
899903

900-
if (!git_path_isvalid(repo, path, path_valid_flags)) {
904+
if (!git_path_isvalid(repo, path, mode, path_valid_flags)) {
901905
giterr_set(GITERR_INDEX, "invalid path: '%s'", path);
902906
return -1;
903907
}
@@ -922,15 +926,35 @@ static int index_entry_init(
922926
{
923927
int error = 0;
924928
git_index_entry *entry = NULL;
929+
git_buf path = GIT_BUF_INIT;
925930
struct stat st;
926931
git_oid oid;
932+
git_repository *repo;
927933

928934
if (INDEX_OWNER(index) == NULL)
929935
return create_index_error(-1,
930936
"could not initialize index entry. "
931937
"Index is not backed up by an existing repository.");
932938

933-
if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0)
939+
/*
940+
* FIXME: this is duplicated with the work in
941+
* git_blob__create_from_paths. It should accept an optional stat
942+
* structure so we can pass in the one we have to do here.
943+
*/
944+
repo = INDEX_OWNER(index);
945+
if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
946+
return GIT_EBAREREPO;
947+
948+
if (git_buf_joinpath(&path, git_repository_workdir(repo), rel_path) < 0)
949+
return -1;
950+
951+
error = git_path_lstat(path.ptr, &st);
952+
git_buf_free(&path);
953+
954+
if (error < 0)
955+
return error;
956+
957+
if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, &st, true) < 0)
934958
return -1;
935959

936960
/* write the blob to disk and get the oid and stat info */
@@ -1016,7 +1040,7 @@ static int index_entry_dup(
10161040
git_index *index,
10171041
const git_index_entry *src)
10181042
{
1019-
if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0)
1043+
if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0)
10201044
return -1;
10211045

10221046
index_entry_cpy(*out, src);
@@ -1038,7 +1062,7 @@ static int index_entry_dup_nocache(
10381062
git_index *index,
10391063
const git_index_entry *src)
10401064
{
1041-
if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0)
1065+
if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0)
10421066
return -1;
10431067

10441068
index_entry_cpy_nocache(*out, src);
@@ -1461,9 +1485,6 @@ static int add_repo_as_submodule(git_index_entry **out, git_index *index, const
14611485
struct stat st;
14621486
int error;
14631487

1464-
if (index_entry_create(&entry, INDEX_OWNER(index), path, true) < 0)
1465-
return -1;
1466-
14671488
if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0)
14681489
return error;
14691490

@@ -1472,6 +1493,9 @@ static int add_repo_as_submodule(git_index_entry **out, git_index *index, const
14721493
return -1;
14731494
}
14741495

1496+
if (index_entry_create(&entry, INDEX_OWNER(index), path, &st, true) < 0)
1497+
return -1;
1498+
14751499
git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
14761500

14771501
if ((error = git_repository_open(&sub, abspath.ptr)) < 0)
@@ -2965,7 +2989,7 @@ static int read_tree_cb(
29652989
if (git_buf_joinpath(&path, root, tentry->filename) < 0)
29662990
return -1;
29672991

2968-
if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, false) < 0)
2992+
if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, NULL, false) < 0)
29692993
return -1;
29702994

29712995
entry->mode = tentry->attr;

0 commit comments

Comments
 (0)