Skip to content

Commit 288a74b

Browse files
committed
is_ntfs_dotgit(): only verify the leading segment
The config setting `core.protectNTFS` is specifically designed to work not only on Windows, but anywhere, to allow for repositories hosted on, say, Linux servers to be protected against NTFS-specific attack vectors. As a consequence, `is_ntfs_dotgit()` manually splits backslash-separated paths (but does not do the same for paths separated by forward slashes), under the assumption that the backslash might not be a valid directory separator on the _current_ Operating System. However, the two callers, `verify_path()` and `fsck_tree()`, are supposed to feed only individual path segments to the `is_ntfs_dotgit()` function. This causes a lot of duplicate scanning (and very inefficient scanning, too, as the inner loop of `is_ntfs_dotgit()` was optimized for readability rather than for speed. Let's simplify the design of `is_ntfs_dotgit()` by putting the burden of splitting the paths by backslashes as directory separators on the callers of said function. Consequently, the `verify_path()` function, which already splits the path by directory separators, now treats backslashes as directory separators _explicitly_ when `core.protectNTFS` is turned on, even on platforms where the backslash is _not_ a directory separator. Note that we have to repeat some code in `verify_path()`: if the backslash is not a directory separator on the current Operating System, we want to allow file names like `\`, but we _do_ want to disallow paths that are clearly intended to cause harm when the repository is cloned on Windows. The `fsck_tree()` function (the other caller of `is_ntfs_dotgit()`) now needs to look for backslashes in tree entries' names specifically when `core.protectNTFS` is turned on. While it would be tempting to completely disallow backslashes in that case (much like `fsck` reports names containing forward slashes as "full paths"), this would be overzealous: when `core.protectNTFS` is turned on in a non-Windows setup, backslashes are perfectly valid characters in file names while we _still_ want to disallow tree entries that are clearly designed to exploit NTFS-specific behavior. This simplification will make subsequent changes easier to implement, such as turning `core.protectNTFS` on by default (not only on Windows) or protecting against attack vectors involving NTFS Alternate Data Streams. Incidentally, this change allows for catching malicious repositories that contain tree entries of the form `dir\.gitmodules` already on the server side rather than only on the client side (and previously only on Windows): in contrast to `is_ntfs_dotgit()`, the `is_ntfs_dotgitmodules()` function already expects the caller to split the paths by directory separators. Signed-off-by: Johannes Schindelin <[email protected]>
1 parent a62f9d1 commit 288a74b

File tree

3 files changed

+19
-5
lines changed

3 files changed

+19
-5
lines changed

fsck.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
551551

552552
while (desc.size) {
553553
unsigned mode;
554-
const char *name;
554+
const char *name, *backslash;
555555
const struct object_id *oid;
556556

557557
oid = tree_entry_extract(&desc, &name, &mode);
@@ -565,6 +565,15 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
565565
is_hfs_dotgit(name) ||
566566
is_ntfs_dotgit(name));
567567
has_zero_pad |= *(char *)desc.buffer == '0';
568+
569+
if ((backslash = strchr(name, '\\'))) {
570+
while (backslash) {
571+
backslash++;
572+
has_dotgit |= is_ntfs_dotgit(backslash);
573+
backslash = strchr(backslash, '\\');
574+
}
575+
}
576+
568577
if (update_tree_entry_gently(&desc)) {
569578
retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
570579
break;

path.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,10 +1342,7 @@ int is_ntfs_dotgit(const char *name)
13421342
if (only_spaces_and_periods(name, len, 5) &&
13431343
!strncasecmp(name, "git~1", 5))
13441344
return 1;
1345-
if (name[len] != '\\')
1346-
return 0;
1347-
name += len + 1;
1348-
len = -1;
1345+
return 0;
13491346
}
13501347
}
13511348

read-cache.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,15 @@ int verify_path(const char *path, unsigned mode)
874874
if ((c == '.' && !verify_dotfile(path, mode)) ||
875875
is_dir_sep(c) || c == '\0')
876876
return 0;
877+
} else if (c == '\\' && protect_ntfs) {
878+
if (is_ntfs_dotgit(path))
879+
return 0;
880+
if (S_ISLNK(mode)) {
881+
if (is_ntfs_dotgitmodules(path))
882+
return 0;
883+
}
877884
}
885+
878886
c = *path++;
879887
}
880888
}

0 commit comments

Comments
 (0)