OpenWrt – Rev 1

Subversion Repositories:
Rev:
From b3a0ca3deed00334f9feece43f76776b6a168e47 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.
---
 src/pch.c | 89 +++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 66 insertions(+), 23 deletions(-)

--- 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)
       {