2327204ad0
cgit cannot serve patches with stable hashes, so store these patches in-tree. cgit community discussion about this problem: https://lists.zx2c4.com/pipermail/cgit/2017-February/003470.html We pull the patches in-tree rather than strip cgit footers with fetchpatch because per https://github.com/NixOS/nixpkgs/pull/61471#issuecomment-493218587 dependencies of fetchpatch cannot use fetchpatch. Verification that the only difference between the live page, the patch committed here, and the version cached under the old hash at tarballs.nixos.org is the cgit version footer: $ curl -s -L http://tarballs.nixos.org/sha256/"$(nix-hash --type sha256 --to-base16 0iw0lk0yhnhvfjzal48ij6zdr92mgb84jq7fwryy1hdhi47hhq64)" > Allow_input_files_to_be_missing_for_ed-style_patches.patch $ diff -U0 --label cgit-live <( curl -s -L https://git.savannah.gnu.org/cgit/patch.git/patch/?id=b5a91a01e5d0897facdd0f49d64b76b0f02b43e1 ) Allow_input_files_to_be_missing_for_ed-style_patches.patch --- cgit-live +++ Allow_input_files_to_be_missing_for_ed-style_patches.patch 2020-01-29 17:22:00.077312937 -0800 @@ -32 +32 @@ -cgit v1.2.1 +cgit v1.0-41-gc330 $ curl -s -L http://tarballs.nixos.org/sha256/"$(nix-hash --type sha256 --to-base16 1bpy16n3hm5nv9xkrn6c4wglzsdzj3ss1biq16w9kfv48p4hx2vg)" > CVE-2018-1000156.patch $ diff -U0 --label cgit-live <( curl -s -L https://git.savannah.gnu.org/cgit/patch.git/patch/?id=123eaff0d5d1aebe128295959435b9ca5909c26d ) CVE-2018-1000156.patch --- cgit-live +++ CVE-2018-1000156.patch 2020-01-29 17:23:41.021116969 -0800 @@ -210 +210 @@ -cgit v1.2.1 +cgit v1.0-41-gc330
212 lines
5.7 KiB
Diff
212 lines
5.7 KiB
Diff
From 123eaff0d5d1aebe128295959435b9ca5909c26d Mon Sep 17 00:00:00 2001
|
|
From: Andreas Gruenbacher <agruen@gnu.org>
|
|
Date: Fri, 6 Apr 2018 12:14:49 +0200
|
|
Subject: Fix arbitrary command execution in ed-style patches
|
|
(CVE-2018-1000156)
|
|
|
|
* src/pch.c (do_ed_script): Write ed script to a temporary file instead
|
|
of piping it to ed: this will cause ed to abort on invalid commands
|
|
instead of rejecting them and carrying on.
|
|
* tests/ed-style: New test case.
|
|
* tests/Makefile.am (TESTS): Add test case.
|
|
---
|
|
src/pch.c | 91 ++++++++++++++++++++++++++++++++++++++++---------------
|
|
tests/Makefile.am | 1 +
|
|
tests/ed-style | 41 +++++++++++++++++++++++++
|
|
3 files changed, 108 insertions(+), 25 deletions(-)
|
|
create mode 100644 tests/ed-style
|
|
|
|
diff --git a/src/pch.c b/src/pch.c
|
|
index 0c5cc26..4fd5a05 100644
|
|
--- a/src/pch.c
|
|
+++ b/src/pch.c
|
|
@@ -33,6 +33,7 @@
|
|
# include <io.h>
|
|
#endif
|
|
#include <safe.h>
|
|
+#include <sys/wait.h>
|
|
|
|
#define INITHUNKMAX 125 /* initial dynamic allocation size */
|
|
|
|
@@ -2389,24 +2390,28 @@ do_ed_script (char const *inname, char const *outname,
|
|
static char const editor_program[] = EDITOR_PROGRAM;
|
|
|
|
file_offset beginning_of_this_line;
|
|
- FILE *pipefp = 0;
|
|
size_t chars_read;
|
|
+ FILE *tmpfp = 0;
|
|
+ char const *tmpname;
|
|
+ int tmpfd;
|
|
+ pid_t pid;
|
|
+
|
|
+ if (! dry_run && ! skip_rest_of_patch)
|
|
+ {
|
|
+ /* Write ed script to a temporary file. This causes ed to abort on
|
|
+ invalid commands such as when line numbers or ranges exceed the
|
|
+ number of available lines. When ed reads from a pipe, it rejects
|
|
+ invalid commands and treats the next line as a new command, which
|
|
+ can lead to arbitrary command execution. */
|
|
+
|
|
+ tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0);
|
|
+ if (tmpfd == -1)
|
|
+ pfatal ("Can't create temporary file %s", quotearg (tmpname));
|
|
+ tmpfp = fdopen (tmpfd, "w+b");
|
|
+ if (! tmpfp)
|
|
+ pfatal ("Can't open stream for file %s", quotearg (tmpname));
|
|
+ }
|
|
|
|
- if (! dry_run && ! skip_rest_of_patch) {
|
|
- int exclusive = *outname_needs_removal ? 0 : O_EXCL;
|
|
- if (inerrno != ENOENT)
|
|
- {
|
|
- *outname_needs_removal = true;
|
|
- copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
|
|
- }
|
|
- sprintf (buf, "%s %s%s", editor_program,
|
|
- verbosity == VERBOSE ? "" : "- ",
|
|
- outname);
|
|
- fflush (stdout);
|
|
- pipefp = popen(buf, binary_transput ? "wb" : "w");
|
|
- if (!pipefp)
|
|
- pfatal ("Can't open pipe to %s", quotearg (buf));
|
|
- }
|
|
for (;;) {
|
|
char ed_command_letter;
|
|
beginning_of_this_line = file_tell (pfp);
|
|
@@ -2417,14 +2422,14 @@ do_ed_script (char const *inname, char const *outname,
|
|
}
|
|
ed_command_letter = get_ed_command_letter (buf);
|
|
if (ed_command_letter) {
|
|
- if (pipefp)
|
|
- if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
|
|
+ if (tmpfp)
|
|
+ if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
|
|
write_fatal ();
|
|
if (ed_command_letter != 'd' && ed_command_letter != 's') {
|
|
p_pass_comments_through = true;
|
|
while ((chars_read = get_line ()) != 0) {
|
|
- if (pipefp)
|
|
- if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
|
|
+ if (tmpfp)
|
|
+ if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
|
|
write_fatal ();
|
|
if (chars_read == 2 && strEQ (buf, ".\n"))
|
|
break;
|
|
@@ -2437,13 +2442,49 @@ do_ed_script (char const *inname, char const *outname,
|
|
break;
|
|
}
|
|
}
|
|
- if (!pipefp)
|
|
+ if (!tmpfp)
|
|
return;
|
|
- if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0
|
|
- || fflush (pipefp) != 0)
|
|
+ if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0
|
|
+ || fflush (tmpfp) != 0)
|
|
write_fatal ();
|
|
- if (pclose (pipefp) != 0)
|
|
- fatal ("%s FAILED", editor_program);
|
|
+
|
|
+ if (lseek (tmpfd, 0, SEEK_SET) == -1)
|
|
+ pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname));
|
|
+
|
|
+ if (! dry_run && ! skip_rest_of_patch) {
|
|
+ int exclusive = *outname_needs_removal ? 0 : O_EXCL;
|
|
+ *outname_needs_removal = true;
|
|
+ if (inerrno != ENOENT)
|
|
+ {
|
|
+ *outname_needs_removal = true;
|
|
+ copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
|
|
+ }
|
|
+ sprintf (buf, "%s %s%s", editor_program,
|
|
+ verbosity == VERBOSE ? "" : "- ",
|
|
+ outname);
|
|
+ fflush (stdout);
|
|
+
|
|
+ pid = fork();
|
|
+ if (pid == -1)
|
|
+ pfatal ("Can't fork");
|
|
+ else if (pid == 0)
|
|
+ {
|
|
+ dup2 (tmpfd, 0);
|
|
+ execl ("/bin/sh", "sh", "-c", buf, (char *) 0);
|
|
+ _exit (2);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ int wstatus;
|
|
+ if (waitpid (pid, &wstatus, 0) == -1
|
|
+ || ! WIFEXITED (wstatus)
|
|
+ || WEXITSTATUS (wstatus) != 0)
|
|
+ fatal ("%s FAILED", editor_program);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ fclose (tmpfp);
|
|
+ safe_unlink (tmpname);
|
|
|
|
if (ofp)
|
|
{
|
|
diff --git a/tests/Makefile.am b/tests/Makefile.am
|
|
index 6b6df63..16f8693 100644
|
|
--- a/tests/Makefile.am
|
|
+++ b/tests/Makefile.am
|
|
@@ -32,6 +32,7 @@ TESTS = \
|
|
crlf-handling \
|
|
dash-o-append \
|
|
deep-directories \
|
|
+ ed-style \
|
|
empty-files \
|
|
false-match \
|
|
fifo \
|
|
diff --git a/tests/ed-style b/tests/ed-style
|
|
new file mode 100644
|
|
index 0000000..d8c0689
|
|
--- /dev/null
|
|
+++ b/tests/ed-style
|
|
@@ -0,0 +1,41 @@
|
|
+# Copyright (C) 2018 Free Software Foundation, Inc.
|
|
+#
|
|
+# Copying and distribution of this file, with or without modification,
|
|
+# in any medium, are permitted without royalty provided the copyright
|
|
+# notice and this notice are preserved.
|
|
+
|
|
+. $srcdir/test-lib.sh
|
|
+
|
|
+require cat
|
|
+use_local_patch
|
|
+use_tmpdir
|
|
+
|
|
+# ==============================================================
|
|
+
|
|
+cat > ed1.diff <<EOF
|
|
+0a
|
|
+foo
|
|
+.
|
|
+EOF
|
|
+
|
|
+check 'patch -e foo -i ed1.diff' <<EOF
|
|
+EOF
|
|
+
|
|
+check 'cat foo' <<EOF
|
|
+foo
|
|
+EOF
|
|
+
|
|
+cat > ed2.diff <<EOF
|
|
+1337a
|
|
+r !echo bar
|
|
+,p
|
|
+EOF
|
|
+
|
|
+check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' <<EOF
|
|
+?
|
|
+Status: 2
|
|
+EOF
|
|
+
|
|
+check 'cat foo' <<EOF
|
|
+foo
|
|
+EOF
|
|
--
|
|
cgit v1.0-41-gc330
|
|
|