svn commit: r285974 - head/usr.bin/patch
Xin LI
delphij at FreeBSD.org
Tue Jul 28 19:58:37 UTC 2015
Author: delphij
Date: Tue Jul 28 19:58:36 2015
New Revision: 285974
URL: https://svnweb.freebsd.org/changeset/base/285974
Log:
Fix shell injection vulnerability in patch(1) and drop SCCS
support by replacing system() with execve().
Future revisions may remove the functionality completely.
Obtained from: Bitrig
Security: CVE-2015-1416
Modified:
head/usr.bin/patch/common.h
head/usr.bin/patch/inp.c
Modified: head/usr.bin/patch/common.h
==============================================================================
--- head/usr.bin/patch/common.h Tue Jul 28 19:15:44 2015 (r285973)
+++ head/usr.bin/patch/common.h Tue Jul 28 19:58:36 2015 (r285974)
@@ -43,12 +43,10 @@
#define LINENUM_MAX LONG_MAX
#define SCCSPREFIX "s."
-#define GET "get -e %s"
-#define SCCSDIFF "get -p %s | diff - %s >/dev/null"
#define RCSSUFFIX ",v"
-#define CHECKOUT "co -l %s"
-#define RCSDIFF "rcsdiff %s > /dev/null"
+#define CHECKOUT "/usr/bin/co"
+#define RCSDIFF "/usr/bin/rcsdiff"
#define ORIGEXT ".orig"
#define REJEXT ".rej"
Modified: head/usr.bin/patch/inp.c
==============================================================================
--- head/usr.bin/patch/inp.c Tue Jul 28 19:15:44 2015 (r285973)
+++ head/usr.bin/patch/inp.c Tue Jul 28 19:58:36 2015 (r285974)
@@ -31,8 +31,10 @@
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mman.h>
+#include <sys/wait.h>
#include <ctype.h>
+#include <errno.h>
#include <libgen.h>
#include <stddef.h>
#include <stdint.h>
@@ -133,12 +135,14 @@ reallocate_lines(size_t *lines_allocated
static bool
plan_a(const char *filename)
{
- int ifd, statfailed;
+ int ifd, statfailed, devnull, pstat;
char *p, *s, lbuf[INITLINELEN];
struct stat filestat;
ptrdiff_t sz;
size_t i;
size_t iline, lines_allocated;
+ pid_t pid;
+ char *argp[4] = {NULL};
#ifdef DEBUGGING
if (debug & 8)
@@ -166,13 +170,14 @@ plan_a(const char *filename)
}
if (statfailed && check_only)
fatal("%s not found, -C mode, can't probe further\n", filename);
- /* For nonexistent or read-only files, look for RCS or SCCS versions. */
+ /* For nonexistent or read-only files, look for RCS versions. */
+
if (statfailed ||
/* No one can write to it. */
(filestat.st_mode & 0222) == 0 ||
/* I can't write to it. */
((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) {
- const char *cs = NULL, *filebase, *filedir;
+ char *filebase, *filedir;
struct stat cstat;
char *tmp_filename1, *tmp_filename2;
@@ -180,43 +185,26 @@ plan_a(const char *filename)
tmp_filename2 = strdup(filename);
if (tmp_filename1 == NULL || tmp_filename2 == NULL)
fatal("strdupping filename");
+
filebase = basename(tmp_filename1);
filedir = dirname(tmp_filename2);
- /* Leave room in lbuf for the diff command. */
- s = lbuf + 20;
-
#define try(f, a1, a2, a3) \
- (snprintf(s, buf_size - 20, f, a1, a2, a3), stat(s, &cstat) == 0)
-
- if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
- try("%s/RCS/%s%s", filedir, filebase, "") ||
- try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
- snprintf(buf, buf_size, CHECKOUT, filename);
- snprintf(lbuf, sizeof lbuf, RCSDIFF, filename);
- cs = "RCS";
- } else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
- try("%s/%s%s", filedir, SCCSPREFIX, filebase)) {
- snprintf(buf, buf_size, GET, s);
- snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename);
- cs = "SCCS";
- } else if (statfailed)
- fatal("can't find %s\n", filename);
-
- free(tmp_filename1);
- free(tmp_filename2);
+ (snprintf(lbuf, sizeof(lbuf), f, a1, a2, a3), stat(lbuf, &cstat) == 0)
/*
* else we can't write to it but it's not under a version
* control system, so just proceed.
*/
- if (cs) {
+ if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
+ try("%s/RCS/%s%s", filedir, filebase, "") ||
+ try("%s/%s%s", filedir, filebase, RCSSUFFIX)) {
if (!statfailed) {
if ((filestat.st_mode & 0222) != 0)
/* The owner can write to it. */
fatal("file %s seems to be locked "
- "by somebody else under %s\n",
- filename, cs);
+ "by somebody else under RCS\n",
+ filename);
/*
* It might be checked out unlocked. See if
* it's safe to check out the default version
@@ -224,21 +212,59 @@ plan_a(const char *filename)
*/
if (verbose)
say("Comparing file %s to default "
- "%s version...\n",
- filename, cs);
- if (system(lbuf))
+ "RCS version...\n", filename);
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("can't fork: %s\n",
+ strerror(errno));
+ case 0:
+ devnull = open("/dev/null", O_RDONLY);
+ if (devnull == -1) {
+ fatal("can't open /dev/null: %s",
+ strerror(errno));
+ }
+ (void)dup2(devnull, STDOUT_FILENO);
+ argp[0] = strdup(RCSDIFF);
+ argp[1] = strdup(filename);
+ execv(RCSDIFF, argp);
+ exit(127);
+ }
+ pid = waitpid(pid, &pstat, 0);
+ if (pid == -1 || WEXITSTATUS(pstat) != 0) {
fatal("can't check out file %s: "
- "differs from default %s version\n",
- filename, cs);
+ "differs from default RCS version\n",
+ filename);
+ }
}
+
if (verbose)
- say("Checking out file %s from %s...\n",
- filename, cs);
- if (system(buf) || stat(filename, &filestat))
- fatal("can't check out file %s from %s\n",
- filename, cs);
+ say("Checking out file %s from RCS...\n",
+ filename);
+
+ switch (pid = fork()) {
+ case -1:
+ fatal("can't fork: %s\n", strerror(errno));
+ case 0:
+ argp[0] = strdup(CHECKOUT);
+ argp[1] = strdup("-l");
+ argp[2] = strdup(filename);
+ execv(CHECKOUT, argp);
+ exit(127);
+ }
+ pid = waitpid(pid, &pstat, 0);
+ if (pid == -1 || WEXITSTATUS(pstat) != 0 ||
+ stat(filename, &filestat)) {
+ fatal("can't check out file %s from RCS\n",
+ filename);
+ }
+ } else if (statfailed) {
+ fatal("can't find %s\n", filename);
}
+ free(tmp_filename1);
+ free(tmp_filename2);
}
+
filemode = filestat.st_mode;
if (!S_ISREG(filemode))
fatal("%s is not a normal file--can't patch\n", filename);
More information about the svn-src-head
mailing list