]> git.ipfire.org Git - thirdparty/git.git/commitdiff
is_ntfs_dotgit(): only verify the leading segment
authorJohannes Schindelin <johannes.schindelin@gmx.de>
Mon, 23 Sep 2019 06:58:11 +0000 (08:58 +0200)
committerJohannes Schindelin <johannes.schindelin@gmx.de>
Thu, 5 Dec 2019 14:36:50 +0000 (15:36 +0100)
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 <johannes.schindelin@gmx.de>
fsck.c
path.c
read-cache.c

diff --git a/fsck.c b/fsck.c
index b1579c7e2821a99729af85b1382f7ea5c3824b8f..d80a96f4bef050e37c0f224aa9bbf783012e6523 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -551,7 +551,7 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
 
        while (desc.size) {
                unsigned mode;
-               const char *name;
+               const char *name, *backslash;
                const struct object_id *oid;
 
                oid = tree_entry_extract(&desc, &name, &mode);
@@ -565,6 +565,15 @@ static int fsck_tree(struct tree *item, struct fsck_options *options)
                               is_hfs_dotgit(name) ||
                               is_ntfs_dotgit(name));
                has_zero_pad |= *(char *)desc.buffer == '0';
+
+               if ((backslash = strchr(name, '\\'))) {
+                       while (backslash) {
+                               backslash++;
+                               has_dotgit |= is_ntfs_dotgit(backslash);
+                               backslash = strchr(backslash, '\\');
+                       }
+               }
+
                if (update_tree_entry_gently(&desc)) {
                        retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
                        break;
diff --git a/path.c b/path.c
index 22bd0b6f52a5d9d34a971e4db866b34730716561..f62a37d5f5122b660ae695685f5f87a4c9e5e255 100644 (file)
--- a/path.c
+++ b/path.c
@@ -1342,10 +1342,7 @@ int is_ntfs_dotgit(const char *name)
                        if (only_spaces_and_periods(name, len, 5) &&
                                        !strncasecmp(name, "git~1", 5))
                                return 1;
-                       if (name[len] != '\\')
-                               return 0;
-                       name += len + 1;
-                       len = -1;
+                       return 0;
                }
 }
 
index 5b57b369e86b88c992d3158eca43c50a0b01d4c9..bde1e70c5142f905c3f98514ec19370412348572 100644 (file)
@@ -874,7 +874,15 @@ inside:
                        if ((c == '.' && !verify_dotfile(path, mode)) ||
                            is_dir_sep(c) || c == '\0')
                                return 0;
+               } else if (c == '\\' && protect_ntfs) {
+                       if (is_ntfs_dotgit(path))
+                               return 0;
+                       if (S_ISLNK(mode)) {
+                               if (is_ntfs_dotgitmodules(path))
+                                       return 0;
+                       }
                }
+
                c = *path++;
        }
 }