1*4882a593SmuzhiyunFrom 123eaff0d5d1aebe128295959435b9ca5909c26d Mon Sep 17 00:00:00 2001 2*4882a593SmuzhiyunFrom: Andreas Gruenbacher <agruen@gnu.org> 3*4882a593SmuzhiyunDate: Fri, 6 Apr 2018 12:14:49 +0200 4*4882a593SmuzhiyunSubject: [PATCH] Fix arbitrary command execution in ed-style patches 5*4882a593Smuzhiyun (CVE-2018-1000156) 6*4882a593Smuzhiyun 7*4882a593Smuzhiyun* src/pch.c (do_ed_script): Write ed script to a temporary file instead 8*4882a593Smuzhiyunof piping it to ed: this will cause ed to abort on invalid commands 9*4882a593Smuzhiyuninstead of rejecting them and carrying on. 10*4882a593Smuzhiyun* tests/ed-style: New test case. 11*4882a593Smuzhiyun* tests/Makefile.am (TESTS): Add test case. 12*4882a593Smuzhiyun 13*4882a593Smuzhiyun[baruch: drop test hunks to avoid autoreconf] 14*4882a593SmuzhiyunSigned-off-by: Baruch Siach <baruch@tkos.co.il> 15*4882a593Smuzhiyun--- 16*4882a593SmuzhiyunUpstream status: commit 123eaff0d5d1 17*4882a593Smuzhiyun 18*4882a593Smuzhiyun src/pch.c | 91 ++++++++++++++++++++++++++++++++++++++++--------------- 19*4882a593Smuzhiyun tests/Makefile.am | 1 + 20*4882a593Smuzhiyun tests/ed-style | 41 +++++++++++++++++++++++++ 21*4882a593Smuzhiyun 3 files changed, 108 insertions(+), 25 deletions(-) 22*4882a593Smuzhiyun create mode 100644 tests/ed-style 23*4882a593Smuzhiyun 24*4882a593Smuzhiyundiff --git a/src/pch.c b/src/pch.c 25*4882a593Smuzhiyunindex 0c5cc2623079..4fd5a05a6f5c 100644 26*4882a593Smuzhiyun--- a/src/pch.c 27*4882a593Smuzhiyun+++ b/src/pch.c 28*4882a593Smuzhiyun@@ -33,6 +33,7 @@ 29*4882a593Smuzhiyun # include <io.h> 30*4882a593Smuzhiyun #endif 31*4882a593Smuzhiyun #include <safe.h> 32*4882a593Smuzhiyun+#include <sys/wait.h> 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun #define INITHUNKMAX 125 /* initial dynamic allocation size */ 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun@@ -2389,24 +2390,28 @@ do_ed_script (char const *inname, char const *outname, 37*4882a593Smuzhiyun static char const editor_program[] = EDITOR_PROGRAM; 38*4882a593Smuzhiyun 39*4882a593Smuzhiyun file_offset beginning_of_this_line; 40*4882a593Smuzhiyun- FILE *pipefp = 0; 41*4882a593Smuzhiyun size_t chars_read; 42*4882a593Smuzhiyun+ FILE *tmpfp = 0; 43*4882a593Smuzhiyun+ char const *tmpname; 44*4882a593Smuzhiyun+ int tmpfd; 45*4882a593Smuzhiyun+ pid_t pid; 46*4882a593Smuzhiyun+ 47*4882a593Smuzhiyun+ if (! dry_run && ! skip_rest_of_patch) 48*4882a593Smuzhiyun+ { 49*4882a593Smuzhiyun+ /* Write ed script to a temporary file. This causes ed to abort on 50*4882a593Smuzhiyun+ invalid commands such as when line numbers or ranges exceed the 51*4882a593Smuzhiyun+ number of available lines. When ed reads from a pipe, it rejects 52*4882a593Smuzhiyun+ invalid commands and treats the next line as a new command, which 53*4882a593Smuzhiyun+ can lead to arbitrary command execution. */ 54*4882a593Smuzhiyun+ 55*4882a593Smuzhiyun+ tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0); 56*4882a593Smuzhiyun+ if (tmpfd == -1) 57*4882a593Smuzhiyun+ pfatal ("Can't create temporary file %s", quotearg (tmpname)); 58*4882a593Smuzhiyun+ tmpfp = fdopen (tmpfd, "w+b"); 59*4882a593Smuzhiyun+ if (! tmpfp) 60*4882a593Smuzhiyun+ pfatal ("Can't open stream for file %s", quotearg (tmpname)); 61*4882a593Smuzhiyun+ } 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun- if (! dry_run && ! skip_rest_of_patch) { 64*4882a593Smuzhiyun- int exclusive = *outname_needs_removal ? 0 : O_EXCL; 65*4882a593Smuzhiyun- if (inerrno != ENOENT) 66*4882a593Smuzhiyun- { 67*4882a593Smuzhiyun- *outname_needs_removal = true; 68*4882a593Smuzhiyun- copy_file (inname, outname, 0, exclusive, instat.st_mode, true); 69*4882a593Smuzhiyun- } 70*4882a593Smuzhiyun- sprintf (buf, "%s %s%s", editor_program, 71*4882a593Smuzhiyun- verbosity == VERBOSE ? "" : "- ", 72*4882a593Smuzhiyun- outname); 73*4882a593Smuzhiyun- fflush (stdout); 74*4882a593Smuzhiyun- pipefp = popen(buf, binary_transput ? "wb" : "w"); 75*4882a593Smuzhiyun- if (!pipefp) 76*4882a593Smuzhiyun- pfatal ("Can't open pipe to %s", quotearg (buf)); 77*4882a593Smuzhiyun- } 78*4882a593Smuzhiyun for (;;) { 79*4882a593Smuzhiyun char ed_command_letter; 80*4882a593Smuzhiyun beginning_of_this_line = file_tell (pfp); 81*4882a593Smuzhiyun@@ -2417,14 +2422,14 @@ do_ed_script (char const *inname, char const *outname, 82*4882a593Smuzhiyun } 83*4882a593Smuzhiyun ed_command_letter = get_ed_command_letter (buf); 84*4882a593Smuzhiyun if (ed_command_letter) { 85*4882a593Smuzhiyun- if (pipefp) 86*4882a593Smuzhiyun- if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) 87*4882a593Smuzhiyun+ if (tmpfp) 88*4882a593Smuzhiyun+ if (! fwrite (buf, sizeof *buf, chars_read, tmpfp)) 89*4882a593Smuzhiyun write_fatal (); 90*4882a593Smuzhiyun if (ed_command_letter != 'd' && ed_command_letter != 's') { 91*4882a593Smuzhiyun p_pass_comments_through = true; 92*4882a593Smuzhiyun while ((chars_read = get_line ()) != 0) { 93*4882a593Smuzhiyun- if (pipefp) 94*4882a593Smuzhiyun- if (! fwrite (buf, sizeof *buf, chars_read, pipefp)) 95*4882a593Smuzhiyun+ if (tmpfp) 96*4882a593Smuzhiyun+ if (! fwrite (buf, sizeof *buf, chars_read, tmpfp)) 97*4882a593Smuzhiyun write_fatal (); 98*4882a593Smuzhiyun if (chars_read == 2 && strEQ (buf, ".\n")) 99*4882a593Smuzhiyun break; 100*4882a593Smuzhiyun@@ -2437,13 +2442,49 @@ do_ed_script (char const *inname, char const *outname, 101*4882a593Smuzhiyun break; 102*4882a593Smuzhiyun } 103*4882a593Smuzhiyun } 104*4882a593Smuzhiyun- if (!pipefp) 105*4882a593Smuzhiyun+ if (!tmpfp) 106*4882a593Smuzhiyun return; 107*4882a593Smuzhiyun- if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0 108*4882a593Smuzhiyun- || fflush (pipefp) != 0) 109*4882a593Smuzhiyun+ if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0 110*4882a593Smuzhiyun+ || fflush (tmpfp) != 0) 111*4882a593Smuzhiyun write_fatal (); 112*4882a593Smuzhiyun- if (pclose (pipefp) != 0) 113*4882a593Smuzhiyun- fatal ("%s FAILED", editor_program); 114*4882a593Smuzhiyun+ 115*4882a593Smuzhiyun+ if (lseek (tmpfd, 0, SEEK_SET) == -1) 116*4882a593Smuzhiyun+ pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname)); 117*4882a593Smuzhiyun+ 118*4882a593Smuzhiyun+ if (! dry_run && ! skip_rest_of_patch) { 119*4882a593Smuzhiyun+ int exclusive = *outname_needs_removal ? 0 : O_EXCL; 120*4882a593Smuzhiyun+ *outname_needs_removal = true; 121*4882a593Smuzhiyun+ if (inerrno != ENOENT) 122*4882a593Smuzhiyun+ { 123*4882a593Smuzhiyun+ *outname_needs_removal = true; 124*4882a593Smuzhiyun+ copy_file (inname, outname, 0, exclusive, instat.st_mode, true); 125*4882a593Smuzhiyun+ } 126*4882a593Smuzhiyun+ sprintf (buf, "%s %s%s", editor_program, 127*4882a593Smuzhiyun+ verbosity == VERBOSE ? "" : "- ", 128*4882a593Smuzhiyun+ outname); 129*4882a593Smuzhiyun+ fflush (stdout); 130*4882a593Smuzhiyun+ 131*4882a593Smuzhiyun+ pid = fork(); 132*4882a593Smuzhiyun+ if (pid == -1) 133*4882a593Smuzhiyun+ pfatal ("Can't fork"); 134*4882a593Smuzhiyun+ else if (pid == 0) 135*4882a593Smuzhiyun+ { 136*4882a593Smuzhiyun+ dup2 (tmpfd, 0); 137*4882a593Smuzhiyun+ execl ("/bin/sh", "sh", "-c", buf, (char *) 0); 138*4882a593Smuzhiyun+ _exit (2); 139*4882a593Smuzhiyun+ } 140*4882a593Smuzhiyun+ else 141*4882a593Smuzhiyun+ { 142*4882a593Smuzhiyun+ int wstatus; 143*4882a593Smuzhiyun+ if (waitpid (pid, &wstatus, 0) == -1 144*4882a593Smuzhiyun+ || ! WIFEXITED (wstatus) 145*4882a593Smuzhiyun+ || WEXITSTATUS (wstatus) != 0) 146*4882a593Smuzhiyun+ fatal ("%s FAILED", editor_program); 147*4882a593Smuzhiyun+ } 148*4882a593Smuzhiyun+ } 149*4882a593Smuzhiyun+ 150*4882a593Smuzhiyun+ fclose (tmpfp); 151*4882a593Smuzhiyun+ safe_unlink (tmpname); 152*4882a593Smuzhiyun 153*4882a593Smuzhiyun if (ofp) 154*4882a593Smuzhiyun { 155*4882a593Smuzhiyun-- 156*4882a593Smuzhiyun2.16.3 157*4882a593Smuzhiyun 158