From f4266d78c8b2bb6a19e6ffd79a15db7122b4daec Mon Sep 17 00:00:00 2001 From: Martin Natano Date: Mon, 27 Jul 2015 21:07:32 +0200 Subject: [PATCH] Remove automatic checkout of RCS and SCCS files from patch(1). The code used for the checkout contained a shell injection bug and the functionality was rarely used - there is no point in fixing it. This commit is a fix for CVE-2015-1416. ok pedro@ --- usr.bin/patch/common.h | 4 --- usr.bin/patch/inp.c | 66 ++---------------------------------------- usr.bin/patch/patch.1 | 9 ------ usr.bin/patch/pch.c | 23 +++------------ usr.bin/patch/util.c | 26 ----------------- usr.bin/patch/util.h | 1 - 6 files changed, 6 insertions(+), 123 deletions(-) diff --git a/usr.bin/patch/common.h b/usr.bin/patch/common.h index 57b6614d7d0..a93b447ec30 100644 --- a/usr.bin/patch/common.h +++ b/usr.bin/patch/common.h @@ -43,10 +43,6 @@ #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 ORIGEXT ".orig" #define REJEXT ".rej" diff --git a/usr.bin/patch/inp.c b/usr.bin/patch/inp.c index 67cc3a83d72..5f908024a4c 100644 --- a/usr.bin/patch/inp.c +++ b/usr.bin/patch/inp.c @@ -163,70 +163,8 @@ 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. */ - 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())) { - char *cs = NULL, *filebase, *filedir; - struct stat cstat; - - filebase = basename(filename); - filedir = dirname(filename); - - /* Leave room in lbuf for the diff command. */ - s = lbuf + 20; - -#define try(f, a1, a2, a3) \ - (snprintf(s, sizeof lbuf - 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, sizeof buf, 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, sizeof buf, GET, s); - snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename); - cs = "SCCS"; - } else if (statfailed) - fatal("can't find %s\n", filename); - /* - * else we can't write to it but it's not under a version - * control system, so just proceed. - */ - if (cs) { - 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); - /* - * It might be checked out unlocked. See if - * it's safe to check out the default version - * locked. - */ - if (verbose) - say("Comparing file %s to default " - "%s version...\n", - filename, cs); - if (system(lbuf)) - fatal("can't check out file %s: " - "differs from default %s version\n", - filename, cs); - } - 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); - } - } + if (statfailed) + fatal("can't find %s\n", filename); filemode = filestat.st_mode; if (!S_ISREG(filemode)) fatal("%s is not a normal file--can't patch\n", filename); diff --git a/usr.bin/patch/patch.1 b/usr.bin/patch/patch.1 index 14cf67af60c..874b8b0fbdf 100644 --- a/usr.bin/patch/patch.1 +++ b/usr.bin/patch/patch.1 @@ -479,15 +479,6 @@ file names or, for a non-context diff, the file name, and choose the file name with the fewest path components, the shortest basename, and the shortest total file name length (in that order). .It -If no file exists, -.Nm -checks for the existence of the files in an SCCS or RCS directory -(using the appropriate prefix or suffix) using the criteria specified -above. -If found, -.Nm -will attempt to get or check out the file. -.It If no suitable file was found to patch, the patch file is a context or unified diff, and the old file was zero length, the new file name is created and used. diff --git a/usr.bin/patch/pch.c b/usr.bin/patch/pch.c index 85dc5ae2647..ea0ba7130bc 100644 --- a/usr.bin/patch/pch.c +++ b/usr.bin/patch/pch.c @@ -1447,15 +1447,6 @@ posix_name(const struct file_name *names, bool assume_exists) } } if (path == NULL && !assume_exists) { - /* - * No files found, look for something we can checkout from - * RCS/SCCS dirs. Same order as above. - */ - for (i = 0; i < MAX_FILE; i++) { - if (names[i].path != NULL && - (path = checked_in(names[i].path)) != NULL) - break; - } /* * Still no match? Check to see if the diff could be creating * a new file. @@ -1469,7 +1460,7 @@ posix_name(const struct file_name *names, bool assume_exists) } static char * -compare_names(const struct file_name *names, bool assume_exists, int phase) +compare_names(const struct file_name *names, bool assume_exists) { size_t min_components, min_baselen, min_len, tmp; char *best = NULL; @@ -1486,9 +1477,7 @@ compare_names(const struct file_name *names, bool assume_exists, int phase) min_components = min_baselen = min_len = SIZE_MAX; for (i = INDEX_FILE; i >= OLD_FILE; i--) { path = names[i].path; - if (path == NULL || - (phase == 1 && !names[i].exists && !assume_exists) || - (phase == 2 && checked_in(path) == NULL)) + if (path == NULL || (!names[i].exists && !assume_exists)) continue; if ((tmp = num_components(path)) > min_components) continue; @@ -1519,13 +1508,9 @@ best_name(const struct file_name *names, bool assume_exists) { char *best; - best = compare_names(names, assume_exists, 1); + best = compare_names(names, assume_exists); if (best == NULL) { - best = compare_names(names, assume_exists, 2); - /* - * Still no match? Check to see if the diff could be creating - * a new file. - */ + /* Check to see if the diff could be creating a new file. */ if (best == NULL && ok_to_create_file && names[NEW_FILE].path != NULL) best = names[NEW_FILE].path; diff --git a/usr.bin/patch/util.c b/usr.bin/patch/util.c index 40414a75c4c..f9f516f9886 100644 --- a/usr.bin/patch/util.c +++ b/usr.bin/patch/util.c @@ -373,32 +373,6 @@ fetchname(const char *at, bool *exists, int strip_leading) return name; } -/* - * Takes the name returned by fetchname and looks in RCS/SCCS directories - * for a checked in version. - */ -char * -checked_in(char *file) -{ - char *filebase, *filedir, tmpbuf[MAXPATHLEN]; - struct stat filestat; - - filebase = basename(file); - filedir = dirname(file); - -#define try(f, a1, a2, a3) \ -(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0) - - if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || - try("%s/RCS/%s%s", filedir, filebase, "") || - try("%s/%s%s", filedir, filebase, RCSSUFFIX) || - try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || - try("%s/%s%s", filedir, SCCSPREFIX, filebase)) - return file; - - return NULL; -} - void version(void) { diff --git a/usr.bin/patch/util.h b/usr.bin/patch/util.h index b27bbe3cc3a..92fb0be22e7 100644 --- a/usr.bin/patch/util.h +++ b/usr.bin/patch/util.h @@ -27,7 +27,6 @@ */ char *fetchname(const char *, bool *, int); -char *checked_in(char *); int backup_file(const char *); int move_file(const char *, const char *); int copy_file(const char *, const char *);