We do not build the tests for this package, so we can avoid needing autoreconf when the host automake version doesn't match the version this package was bootstrapped with by not changing the .am file in this patch. Signed-off-by: Michael Pratt <mcpratt@pm.me>
189 lines
5.2 KiB
Diff
189 lines
5.2 KiB
Diff
From b56779aed483f0036a32a65e62ab7b5e461b07cc Mon Sep 17 00:00:00 2001
|
|
From: Andreas Gruenbacher <agruen@gnu.org>
|
|
Date: Fri, 6 Apr 2018 12:14:49 +0200
|
|
Subject: [PATCH] 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. (OPENWRT REMOVED)
|
|
---
|
|
src/pch.c | 89 +++++++++++++++++++++++++++++++++++------------
|
|
tests/Makefile.am | 1 + (OPENWRT REMOVED)
|
|
tests/ed-style | 41 ++++++++++++++++++++++
|
|
3 files changed, 108 insertions(+), 23 deletions(-)
|
|
create mode 100644 tests/ed-style
|
|
|
|
--- 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,22 +2390,28 @@ do_ed_script (char const *inname, char c
|
|
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;
|
|
- assert (! inerrno);
|
|
- *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);
|
|
@@ -2415,14 +2422,14 @@ do_ed_script (char const *inname, char c
|
|
}
|
|
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;
|
|
@@ -2435,13 +2442,49 @@ do_ed_script (char const *inname, char c
|
|
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)
|
|
{
|
|
--- /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
|