Skip to content

Commit 7c9fbda

Browse files
committed
Sync with 2.18.2
* maint-2.18: (33 commits) Git 2.18.2 Git 2.17.3 Git 2.16.6 test-drop-caches: use `has_dos_drive_prefix()` Git 2.15.4 Git 2.14.6 mingw: handle `subst`-ed "DOS drives" mingw: refuse to access paths with trailing spaces or periods mingw: refuse to access paths with illegal characters unpack-trees: let merged_entry() pass through do_add_entry()'s errors quote-stress-test: offer to test quoting arguments for MSYS2 sh t6130/t9350: prepare for stringent Win32 path validation quote-stress-test: allow skipping some trials quote-stress-test: accept arguments to test via the command-line tests: add a helper to stress test argument quoting mingw: fix quoting of arguments Disallow dubiously-nested submodule git directories protect_ntfs: turn on NTFS protection by default path: also guard `.gitmodules` against NTFS Alternate Data Streams is_ntfs_dotgit(): speed it up ...
2 parents 98cdfbb + 9877106 commit 7c9fbda

39 files changed

+906
-82
lines changed

Documentation/RelNotes/2.14.6.txt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
Git v2.14.6 Release Notes
2+
=========================
3+
4+
This release addresses the security issues CVE-2019-1348,
5+
CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
6+
CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387.
7+
8+
Fixes since v2.14.5
9+
-------------------
10+
11+
* CVE-2019-1348:
12+
The --export-marks option of git fast-import is exposed also via
13+
the in-stream command feature export-marks=... and it allows
14+
overwriting arbitrary paths.
15+
16+
* CVE-2019-1349:
17+
When submodules are cloned recursively, under certain circumstances
18+
Git could be fooled into using the same Git directory twice. We now
19+
require the directory to be empty.
20+
21+
* CVE-2019-1350:
22+
Incorrect quoting of command-line arguments allowed remote code
23+
execution during a recursive clone in conjunction with SSH URLs.
24+
25+
* CVE-2019-1351:
26+
While the only permitted drive letters for physical drives on
27+
Windows are letters of the US-English alphabet, this restriction
28+
does not apply to virtual drives assigned via subst <letter>:
29+
<path>. Git mistook such paths for relative paths, allowing writing
30+
outside of the worktree while cloning.
31+
32+
* CVE-2019-1352:
33+
Git was unaware of NTFS Alternate Data Streams, allowing files
34+
inside the .git/ directory to be overwritten during a clone.
35+
36+
* CVE-2019-1353:
37+
When running Git in the Windows Subsystem for Linux (also known as
38+
"WSL") while accessing a working directory on a regular Windows
39+
drive, none of the NTFS protections were active.
40+
41+
* CVE-2019-1354:
42+
Filenames on Linux/Unix can contain backslashes. On Windows,
43+
backslashes are directory separators. Git did not use to refuse to
44+
write out tracked files with such filenames.
45+
46+
* CVE-2019-1387:
47+
Recursive clones are currently affected by a vulnerability that is
48+
caused by too-lax validation of submodule names, allowing very
49+
targeted attacks via remote code execution in recursive clones.
50+
51+
Credit for finding these vulnerabilities goes to Microsoft Security
52+
Response Center, in particular to Nicolas Joly. The `fast-import`
53+
fixes were provided by Jeff King, the other fixes by Johannes
54+
Schindelin with help from Garima Singh.

Documentation/RelNotes/2.15.4.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Git v2.15.4 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.14.6 to address
5+
the security issues CVE-2019-1348, CVE-2019-1349, CVE-2019-1350,
6+
CVE-2019-1351, CVE-2019-1352, CVE-2019-1353, CVE-2019-1354, and
7+
CVE-2019-1387; see the release notes for that version for details.
8+
9+
In conjunction with a vulnerability that was fixed in v2.20.2,
10+
`.gitmodules` is no longer allowed to contain entries of the form
11+
`submodule.<name>.update=!command`.

Documentation/RelNotes/2.16.6.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Git v2.16.6 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.14.6 and in
5+
v2.15.4 addressing the security issues CVE-2019-1348, CVE-2019-1349,
6+
CVE-2019-1350, CVE-2019-1351, CVE-2019-1352, CVE-2019-1353,
7+
CVE-2019-1354, and CVE-2019-1387; see the release notes for those
8+
versions for details.

Documentation/RelNotes/2.17.3.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Git v2.17.3 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.14.6 and in
5+
v2.15.4 addressing the security issues CVE-2019-1348, CVE-2019-1349,
6+
CVE-2019-1350, CVE-2019-1351, CVE-2019-1352, CVE-2019-1353,
7+
CVE-2019-1354, and CVE-2019-1387; see the release notes for those
8+
versions for details.
9+
10+
In addition, `git fsck` was taught to identify `.gitmodules` entries
11+
of the form `submodule.<name>.update=!command`, which have been
12+
disallowed in v2.15.4.

Documentation/RelNotes/2.18.2.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Git v2.18.2 Release Notes
2+
=========================
3+
4+
This release merges up the fixes that appear in v2.14.6, v2.15.4
5+
and in v2.17.3, addressing the security issues CVE-2019-1348,
6+
CVE-2019-1349, CVE-2019-1350, CVE-2019-1351, CVE-2019-1352,
7+
CVE-2019-1353, CVE-2019-1354, and CVE-2019-1387; see the release notes
8+
for those versions for details.

Documentation/git-fast-import.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ OPTIONS
5050
memory used by fast-import during this run. Showing this output
5151
is currently the default, but can be disabled with --quiet.
5252

53+
--allow-unsafe-features::
54+
Many command-line options can be provided as part of the
55+
fast-import stream itself by using the `feature` or `option`
56+
commands. However, some of these options are unsafe (e.g.,
57+
allowing fast-import to access the filesystem outside of the
58+
repository). These options are disabled by default, but can be
59+
allowed by providing this option on the command line. This
60+
currently impacts only the `export-marks`, `import-marks`, and
61+
`import-marks-if-exists` feature commands.
62+
+
63+
Only enable this option if you trust the program generating the
64+
fast-import stream! This option is enabled automatically for
65+
remote-helpers that use the `import` capability, as they are
66+
already trusted to run their own code.
67+
5368
Options for Frontends
5469
~~~~~~~~~~~~~~~~~~~~~
5570

Documentation/gitmodules.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@ submodule.<name>.update::
4444
submodule init` to initialize the configuration variable of
4545
the same name. Allowed values here are 'checkout', 'rebase',
4646
'merge' or 'none'. See description of 'update' command in
47-
linkgit:git-submodule[1] for their meaning. Note that the
48-
'!command' form is intentionally ignored here for security
49-
reasons.
47+
linkgit:git-submodule[1] for their meaning. For security
48+
reasons, the '!command' form is not accepted here.
5049

5150
submodule.<name>.branch::
5251
A remote branch name for tracking updates in the upstream submodule.

builtin/clone.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,7 @@ static int checkout(int submodule_progress)
767767

768768
if (!err && (option_recurse_submodules.nr > 0)) {
769769
struct argv_array args = ARGV_ARRAY_INIT;
770-
argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
770+
argv_array_pushl(&args, "submodule", "update", "--require-init", "--recursive", NULL);
771771

772772
if (option_shallow_submodules == 1)
773773
argv_array_push(&args, "--depth=1");

builtin/submodule--helper.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "diffcore.h"
1919
#include "diff.h"
2020
#include "object-store.h"
21+
#include "dir.h"
2122

2223
#define OPT_QUIET (1 << 0)
2324
#define OPT_CACHED (1 << 1)
@@ -1346,7 +1347,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
13461347
char *p, *path = NULL, *sm_gitdir;
13471348
struct strbuf sb = STRBUF_INIT;
13481349
struct string_list reference = STRING_LIST_INIT_NODUP;
1349-
int dissociate = 0;
1350+
int dissociate = 0, require_init = 0;
13501351
char *sm_alternate = NULL, *error_strategy = NULL;
13511352

13521353
struct option module_clone_options[] = {
@@ -1373,6 +1374,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
13731374
OPT__QUIET(&quiet, "Suppress output for cloning a submodule"),
13741375
OPT_BOOL(0, "progress", &progress,
13751376
N_("force cloning progress")),
1377+
OPT_BOOL(0, "require-init", &require_init,
1378+
N_("disallow cloning into non-empty directory")),
13761379
OPT_END()
13771380
};
13781381

@@ -1400,6 +1403,10 @@ static int module_clone(int argc, const char **argv, const char *prefix)
14001403
} else
14011404
path = xstrdup(path);
14021405

1406+
if (validate_submodule_git_dir(sm_gitdir, name) < 0)
1407+
die(_("refusing to create/use '%s' in another submodule's "
1408+
"git dir"), sm_gitdir);
1409+
14031410
if (!file_exists(sm_gitdir)) {
14041411
if (safe_create_leading_directories_const(sm_gitdir) < 0)
14051412
die(_("could not create directory '%s'"), sm_gitdir);
@@ -1411,6 +1418,8 @@ static int module_clone(int argc, const char **argv, const char *prefix)
14111418
die(_("clone of '%s' into submodule path '%s' failed"),
14121419
url, path);
14131420
} else {
1421+
if (require_init && !access(path, X_OK) && !is_empty_dir(path))
1422+
die(_("directory not empty: '%s'"), path);
14141423
if (safe_create_leading_directories_const(path) < 0)
14151424
die(_("could not create directory '%s'"), path);
14161425
strbuf_addf(&sb, "%s/index", sm_gitdir);
@@ -1459,6 +1468,7 @@ struct submodule_update_clone {
14591468
int recommend_shallow;
14601469
struct string_list references;
14611470
int dissociate;
1471+
unsigned require_init;
14621472
const char *depth;
14631473
const char *recursive_prefix;
14641474
const char *prefix;
@@ -1474,7 +1484,7 @@ struct submodule_update_clone {
14741484
int failed_clones_nr, failed_clones_alloc;
14751485
};
14761486
#define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
1477-
SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
1487+
SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, 0, \
14781488
NULL, NULL, NULL, \
14791489
STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
14801490

@@ -1593,6 +1603,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
15931603
argv_array_pushl(&child->args, "--prefix", suc->prefix, NULL);
15941604
if (suc->recommend_shallow && sub->recommend_shallow == 1)
15951605
argv_array_push(&child->args, "--depth=1");
1606+
if (suc->require_init)
1607+
argv_array_push(&child->args, "--require-init");
15961608
argv_array_pushl(&child->args, "--path", sub->path, NULL);
15971609
argv_array_pushl(&child->args, "--name", sub->name, NULL);
15981610
argv_array_pushl(&child->args, "--url", url, NULL);
@@ -1748,6 +1760,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
17481760
OPT__QUIET(&suc.quiet, N_("don't print cloning progress")),
17491761
OPT_BOOL(0, "progress", &suc.progress,
17501762
N_("force cloning progress")),
1763+
OPT_BOOL(0, "require-init", &suc.require_init,
1764+
N_("disallow cloning into non-empty directory")),
17511765
OPT_END()
17521766
};
17531767

compat/mingw.c

Lines changed: 96 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,12 @@ int mingw_mkdir(const char *path, int mode)
333333
{
334334
int ret;
335335
wchar_t wpath[MAX_PATH];
336+
337+
if (!is_valid_win32_path(path)) {
338+
errno = EINVAL;
339+
return -1;
340+
}
341+
336342
if (xutftowcs_path(wpath, path) < 0)
337343
return -1;
338344
ret = _wmkdir(wpath);
@@ -406,14 +412,19 @@ int mingw_open (const char *filename, int oflags, ...)
406412
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
407413
va_list args;
408414
unsigned mode;
409-
int fd;
415+
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
410416
wchar_t wfilename[MAX_PATH];
411417
open_fn_t open_fn;
412418

413419
va_start(args, oflags);
414420
mode = va_arg(args, int);
415421
va_end(args);
416422

423+
if (!is_valid_win32_path(filename)) {
424+
errno = create ? EINVAL : ENOENT;
425+
return -1;
426+
}
427+
417428
if (filename && !strcmp(filename, "/dev/null"))
418429
filename = "nul";
419430

@@ -480,6 +491,11 @@ FILE *mingw_fopen (const char *filename, const char *otype)
480491
int hide = needs_hiding(filename);
481492
FILE *file;
482493
wchar_t wfilename[MAX_PATH], wotype[4];
494+
if (!is_valid_win32_path(filename)) {
495+
int create = otype && strchr(otype, 'w');
496+
errno = create ? EINVAL : ENOENT;
497+
return NULL;
498+
}
483499
if (filename && !strcmp(filename, "/dev/null"))
484500
filename = "nul";
485501
if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -502,6 +518,11 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
502518
int hide = needs_hiding(filename);
503519
FILE *file;
504520
wchar_t wfilename[MAX_PATH], wotype[4];
521+
if (!is_valid_win32_path(filename)) {
522+
int create = otype && strchr(otype, 'w');
523+
errno = create ? EINVAL : ENOENT;
524+
return NULL;
525+
}
505526
if (filename && !strcmp(filename, "/dev/null"))
506527
filename = "nul";
507528
if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -950,7 +971,7 @@ static const char *quote_arg(const char *arg)
950971
p++;
951972
len++;
952973
}
953-
if (*p == '"')
974+
if (*p == '"' || !*p)
954975
n += count*2 + 1;
955976
continue;
956977
}
@@ -972,16 +993,19 @@ static const char *quote_arg(const char *arg)
972993
count++;
973994
*d++ = *arg++;
974995
}
975-
if (*arg == '"') {
996+
if (*arg == '"' || !*arg) {
976997
while (count-- > 0)
977998
*d++ = '\\';
999+
/* don't escape the surrounding end quote */
1000+
if (!*arg)
1001+
break;
9781002
*d++ = '\\';
9791003
}
9801004
}
9811005
*d++ = *arg++;
9821006
}
9831007
*d++ = '"';
984-
*d++ = 0;
1008+
*d++ = '\0';
9851009
return q;
9861010
}
9871011

@@ -2043,6 +2067,30 @@ pid_t waitpid(pid_t pid, int *status, int options)
20432067
return -1;
20442068
}
20452069

2070+
int mingw_has_dos_drive_prefix(const char *path)
2071+
{
2072+
int i;
2073+
2074+
/*
2075+
* Does it start with an ASCII letter (i.e. highest bit not set),
2076+
* followed by a colon?
2077+
*/
2078+
if (!(0x80 & (unsigned char)*path))
2079+
return *path && path[1] == ':' ? 2 : 0;
2080+
2081+
/*
2082+
* While drive letters must be letters of the English alphabet, it is
2083+
* possible to assign virtually _any_ Unicode character via `subst` as
2084+
* a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
2085+
* like this:
2086+
*
2087+
* subst ֍: %USERPROFILE%\Desktop
2088+
*/
2089+
for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
2090+
; /* skip first UTF-8 character */
2091+
return path[i] == ':' ? i + 1 : 0;
2092+
}
2093+
20462094
int mingw_skip_dos_drive_prefix(char **path)
20472095
{
20482096
int ret = has_dos_drive_prefix(*path);
@@ -2184,6 +2232,50 @@ static void setup_windows_environment(void)
21842232
setenv("TERM", "cygwin", 1);
21852233
}
21862234

2235+
int is_valid_win32_path(const char *path)
2236+
{
2237+
int preceding_space_or_period = 0, i = 0, periods = 0;
2238+
2239+
if (!protect_ntfs)
2240+
return 1;
2241+
2242+
skip_dos_drive_prefix((char **)&path);
2243+
2244+
for (;;) {
2245+
char c = *(path++);
2246+
switch (c) {
2247+
case '\0':
2248+
case '/': case '\\':
2249+
/* cannot end in ` ` or `.`, except for `.` and `..` */
2250+
if (preceding_space_or_period &&
2251+
(i != periods || periods > 2))
2252+
return 0;
2253+
if (!c)
2254+
return 1;
2255+
2256+
i = periods = preceding_space_or_period = 0;
2257+
continue;
2258+
case '.':
2259+
periods++;
2260+
/* fallthru */
2261+
case ' ':
2262+
preceding_space_or_period = 1;
2263+
i++;
2264+
continue;
2265+
case ':': /* DOS drive prefix was already skipped */
2266+
case '<': case '>': case '"': case '|': case '?': case '*':
2267+
/* illegal character */
2268+
return 0;
2269+
default:
2270+
if (c > '\0' && c < '\x20')
2271+
/* illegal character */
2272+
return 0;
2273+
}
2274+
preceding_space_or_period = 0;
2275+
i++;
2276+
}
2277+
}
2278+
21872279
/*
21882280
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
21892281
* mingw startup code, see init.c in mingw runtime).

0 commit comments

Comments
 (0)