From 3538119541a179881ccc8d240871354ed4d77b61 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Oct 30 2018 05:03:45 +0000 Subject: import yum-utils-1.1.31-50.el7 --- diff --git a/SOURCES/BZ-1493489-yum-config-manager-fix-add-repo-2.patch b/SOURCES/BZ-1493489-yum-config-manager-fix-add-repo-2.patch new file mode 100644 index 0000000..d26f921 --- /dev/null +++ b/SOURCES/BZ-1493489-yum-config-manager-fix-add-repo-2.patch @@ -0,0 +1,78 @@ +diff -up yum-utils-1.1.31/yum-config-manager.py.orig yum-utils-1.1.31/yum-config-manager.py +--- yum-utils-1.1.31/yum-config-manager.py.orig 2018-08-17 20:19:31.581457215 +0200 ++++ yum-utils-1.1.31/yum-config-manager.py 2018-08-17 20:19:34.077487757 +0200 +@@ -8,6 +8,8 @@ sys.path.insert(0,'/usr/share/yum-cli') + from utils import YumUtilBase + import logging + import fnmatch ++import tempfile ++import shutil + + from iniparse import INIConfig + import yum.config +@@ -215,6 +217,7 @@ if opts.addrepo: + myrepodir = yb.conf.reposdir[0] + os.makedirs(myrepodir) + ++ error = False + for url in opts.addrepo: + print 'adding repo from: %s' % url + if url.endswith('.repo'): # this is a .repo file - fetch it, put it in our reposdir and enable it +@@ -223,18 +226,24 @@ if opts.addrepo: + + # dummy grabfunc, using [main] options + repo = yum.yumRepo.YumRepository('dummy') +- repo.baseurl = ['http://dummy'] ++ repo.baseurl = ['http://dummy/'] + repo.populate(yum.config.ConfigParser(), None, yb.conf) + grabber = repo.grabfunc; del repo + + print 'grabbing file %s to %s' % (url, destname) ++ f = tempfile.NamedTemporaryFile() + try: +- result = grabber.urlgrab(url, filename=destname, copy_local=True, reget=None) ++ grabber.urlgrab(url, filename=f.name, copy_local=True, reget=None) ++ shutil.copy2(f.name, destname) ++ os.chmod(destname, 0o644) + except (IOError, OSError, yum.Errors.YumBaseError), e: + logger.error('Could not fetch/save url %s to file %s: %s' % (url, destname, e)) ++ error = True + continue + else: +- print 'repo saved to %s' % result ++ print 'repo saved to %s' % destname ++ finally: ++ f.close() + + else: + repoid = sanitize_url_to_fs(url) +@@ -244,7 +253,16 @@ if opts.addrepo: + thisrepo = yb.add_enable_repo(repoid, baseurl=[url], name=reponame) + except yum.Errors.DuplicateRepoError, e: + logger.error('Cannot add repo from %s as is a duplicate of an existing repo' % url) ++ error = True + continue ++ ++ try: ++ yum.config.UrlOption().parse(url) ++ except ValueError, e: ++ logger.error('Cannot add repo from %s: %s' % (url, e)) ++ error = True ++ continue ++ + repoout = """\n[%s]\nname=%s\nbaseurl=%s\nenabled=1\n\n""" % (repoid, reponame, url) + + try: +@@ -253,9 +271,10 @@ if opts.addrepo: + print repoout + except (IOError, OSError), e: + logger.error('Could not save repo to repofile %s: %s' % (repofile, e)) ++ error = True + continue + else: + fo.close() +- +- + ++ if error: ++ sys.exit(1) diff --git a/SOURCES/BZ-1497351-versionlock-add-hint-and-status-cmd.patch b/SOURCES/BZ-1497351-versionlock-add-hint-and-status-cmd.patch new file mode 100644 index 0000000..3f3e3f3 --- /dev/null +++ b/SOURCES/BZ-1497351-versionlock-add-hint-and-status-cmd.patch @@ -0,0 +1,299 @@ +commit 691faec2bed8457edd19b8ad5587768bfbe4c653 +Author: Michal Domonkos +Date: Mon May 28 16:49:10 2018 +0200 + + docs: fix versionlock + + Fixes the formatting and clarifies the "exclude" subcommand (inspired + by: https://illiterat.livejournal.com/8221.html). + +diff --git a/docs/yum-versionlock.1 b/docs/yum-versionlock.1 +index f7cd467..e14bbd5 100644 +--- a/docs/yum-versionlock.1 ++++ b/docs/yum-versionlock.1 +@@ -16,22 +16,17 @@ This allows you to protect packages from being updated by newer versions. + The plugin provides a command "versionlock" which allows you to view and edit + the list of locked packages easily. + .br +-.I \fR yum versionlock add ... +-.PP ++.IP "\fByum versionlock add ...\fR" + Add a versionlock for all of the packages in the rpmdb matching the given + wildcards. +-.I \fR yum versionlock exclude ... +-.PP +-Add a exclude (within versionlock) for the latest versions of the +-packages in the available repos. matching the given wildcards. +-.I \fR yum versionlock list +-.PP ++.IP "\fByum versionlock exclude ...\fR" ++Opposite; disallow currently available versions of the packages matching the ++given wildcards. ++.IP "\fByum versionlock list\fR" + List the current versionlock entries. +-.I \fR yum versionlock delete ... +-.PP ++.IP "\fByum versionlock delete ...\fR" + Remove any matching versionlock entries. +-.I \fR yum versionlock clear +-.PP ++.IP "\fByum versionlock clear\fR" + Remove all versionlock entries. + + .SH FILES +commit f761392dc6209ec17e718291c2319ea011923880 +Author: Michal Domonkos +Date: Mon May 28 16:55:50 2018 +0200 + + versionlock: add hint and "status" subcommand. BZ 1497351 + + It is nice to be reminded of any versionlocks in effect when running yum + transactions (esp. when doing a "yum update" and wondering why no + updates were found). This commit adds the following message that is + printed in the excludes hook: + + Excluding X updates due to versionlock (use "yum versionlock status" + to show them) + + Since it would be too annoying to have all the excludes printed every + time, we only show their count (X) and provide the new "status" + subcommand that allows the user to list them if they are interested. + + Note that even this short message might be annoying as it appears on + every yum run, so we're also adding an option to disable it. + +diff --git a/docs/yum-versionlock.1 b/docs/yum-versionlock.1 +index e14bbd5..6190fea 100644 +--- a/docs/yum-versionlock.1 ++++ b/docs/yum-versionlock.1 +@@ -24,6 +24,10 @@ Opposite; disallow currently available versions of the packages matching the + given wildcards. + .IP "\fByum versionlock list\fR" + List the current versionlock entries. ++.IP "\fByum versionlock status\fR" ++List any available updates that are currently blocked by versionlock. ++That is, for each entry in the lock list, print the newest package available ++in the repos unless it is the particular locked/excluded version. + .IP "\fByum versionlock delete ...\fR" + Remove any matching versionlock entries. + .IP "\fByum versionlock clear\fR" +diff --git a/docs/yum-versionlock.conf.5 b/docs/yum-versionlock.conf.5 +index 78a6e49..b5be24e 100644 +--- a/docs/yum-versionlock.conf.5 ++++ b/docs/yum-versionlock.conf.5 +@@ -26,7 +26,7 @@ character to the version. + .B yum-versionlock.conf(5) + utilizes configuration options in the form of + .IP OPTION=VALUE +-.SH OPTION ++.SH OPTIONS + .IP follow_obsoletes + This option is a boolean flag which specifies if the versionlock plugin should + look at all the obsoletes, and see if any of the packages specified have an +@@ -36,13 +36,20 @@ excluded. This option is off by default, as + will take some time to do the obsoletes processing, and for non-rename + obsoletes any issues you had which kept you at a specific version of a package + should be different with another package. +-.SH OPTION + .IP locklist + This option is a string with points to the file which will have the versionlock + information in it. Note that the file + .B has + to exist (or the versionlock plugin will make yum exit). However it can be + empty. ++.IP show_hint ++This option is a boolean flag which specifies if the versionlock plugin should ++print a hint message whenever yum runs saying how many package updates ++available from the repos are being blocked due to versionlocks. ++This hint serves as a reminder that there are locks in effect and that you may ++want to reconsider them since newer versions of those packages have been ++released. ++Default is 1 (show the hint). + .SH AUTHOR + .RS + Chitlesh Goorah +diff --git a/plugins/versionlock/versionlock.conf b/plugins/versionlock/versionlock.conf +index 4e997da..42e05c9 100644 +--- a/plugins/versionlock/versionlock.conf ++++ b/plugins/versionlock/versionlock.conf +@@ -1,5 +1,7 @@ + [main] + enabled = 1 + locklist = /etc/yum/pluginconf.d/versionlock.list ++# Show a hint when any locked packages have updates available ++show_hint = 1 + # Uncomment this to lock out "upgrade via. obsoletes" etc. (slower) + # follow_obsoletes = 1 +diff --git a/plugins/versionlock/versionlock.py b/plugins/versionlock/versionlock.py +index dfe4dd3..ad66855 100644 +--- a/plugins/versionlock/versionlock.py ++++ b/plugins/versionlock/versionlock.py +@@ -42,6 +42,9 @@ _version_lock_excluder_B_nevr = set() + # _version_lock_excluder_pkgtup = set() + + fileurl = None ++show_hint = True ++follow_obsoletes = False ++no_exclude = False + + def _read_locklist(): + locklist = [] +@@ -73,6 +76,68 @@ def _match(ent, patterns): + return True + return False + ++def _get_updates(base): ++ """Return packages that update or obsolete anything in our locklist. ++ ++ Returns a dict of locked_name->X, where X is either a package object or a ++ list of them. If it's the former, it's the updating package. If it's the ++ latter, it's the obsoleting packages (since multiple packages may obsolete ++ the same name). ++ """ ++ ++ updates = {} ++ ++ # Read in the locked versions ++ locks = {} ++ for ent in _read_locklist(): ++ (n, v, r, e, a) = splitFilename(ent) ++ if e and e[0] == '!': ++ e = e[1:] ++ elif e == '': ++ e = '0' ++ locks.setdefault(n, []).append((e, v, r)) ++ ++ # Process regular updates ++ # ++ # We are using searchNames() + packagesNewestByName() here instead of just ++ # returnNewestByName() because the former way is much, much faster for big ++ # name lists. ++ # ++ # The problem with returnNewestByName() is that it may easily end up ++ # querying all the packages in pkgSack which is terribly slow (takes ++ # seconds); all it takes is a "-" in a package name and more than ++ # PATTERNS_MAX (8 by default) package names to trigger that. ++ # ++ # Since we know that we only ever deal with names, we can just go straight ++ # to searchNames() to avoid the full query. ++ pkgs = base.pkgSack.searchNames(locks.keys()) ++ for p in packagesNewestByName(pkgs): ++ name = p.name ++ evr = p.returnEVR() ++ if (evr.epoch, evr.version, evr.release) in locks[name]: ++ # This one is either the locked or excluded version, skip ++ continue ++ updates[name] = p ++ ++ # Process obsoletes ++ tups = base.up.getObsoletesTuples() if follow_obsoletes else [] ++ for new, old in tups: ++ nname = new[0] ++ oname = old[0] ++ if oname not in locks: ++ # Not our package, skip ++ continue ++ if nname in locks and new[2:] in locks[nname]: ++ # This one is either the locked or excluded version, skip ++ continue ++ # Only record obsoletes for any given package name ++ if oname not in updates or not isinstance(updates[oname], list): ++ updates[oname] = [] ++ p = base.getPackageObject(new) ++ updates[oname].append(p) ++ ++ return updates ++ + class VersionLockCommand: + created = 1247693044 + +@@ -80,7 +145,7 @@ class VersionLockCommand: + return ["versionlock"] + + def getUsage(self): +- return '[add|exclude|list|delete|clear] [PACKAGE-wildcard]' ++ return '[add|exclude|list|status|delete|clear] [PACKAGE-wildcard]' + + def getSummary(self): + return 'Control package version locks.' +@@ -93,7 +158,7 @@ class VersionLockCommand: + if extcmds: + if extcmds[0] not in ('add', + 'exclude', 'add-!', 'add!', 'blacklist', +- 'list', 'del', 'delete', 'clear'): ++ 'list', 'status', 'del', 'delete', 'clear'): + cmd = 'add' + else: + cmd = {'del' : 'delete', +@@ -190,6 +255,19 @@ class VersionLockCommand: + os.rename(tmpfilename, filename) + return 0, ['versionlock deleted: ' + str(count)] + ++ if cmd == 'status': ++ global no_exclude ++ no_exclude = True ++ updates = _get_updates(base) ++ for name, value in updates.iteritems(): ++ if isinstance(value, list): ++ value = set(p.envr + '.*' for p in value) ++ for v in value: ++ print '%s (replacing %s)' % (v, name) ++ continue ++ print value.envr + '.*' ++ return 0, ['versionlock status done'] ++ + assert cmd == 'list' + for ent in _read_locklist(): + print ent +@@ -201,8 +279,12 @@ class VersionLockCommand: + + def config_hook(conduit): + global fileurl ++ global follow_obsoletes ++ global show_hint + + fileurl = conduit.confString('main', 'locklist') ++ follow_obsoletes = conduit.confBool('main', 'follow_obsoletes', default=False) ++ show_hint = conduit.confBool('main', 'show_hint', default=True) + + if hasattr(conduit._base, 'registerCommand'): + conduit.registerCommand(VersionLockCommand()) +@@ -227,6 +309,9 @@ def _add_versionlock_blacklist(conduit): + ape(None, exid + str(3), 'exclude.marked') + + def exclude_hook(conduit): ++ if no_exclude: ++ return ++ + conduit.info(3, 'Reading version lock configuration') + + if not fileurl: +@@ -250,8 +335,7 @@ def exclude_hook(conduit): + _version_lock_excluder_n.add(n) + _version_lock_excluder_nevr.add("%s-%s:%s-%s" % (n, e, v, r)) + +- if (_version_lock_excluder_n and +- conduit.confBool('main', 'follow_obsoletes', default=False)): ++ if (_version_lock_excluder_n and follow_obsoletes): + # If anything obsoletes something that we have versionlocked ... then + # remove all traces of that too. + for (pkgtup, instTup) in conduit._base.up.getObsoletesTuples(): +@@ -259,6 +343,18 @@ def exclude_hook(conduit): + continue + _version_lock_excluder_n.add(pkgtup[0].lower()) + ++ total = len(_get_updates(conduit._base)) if show_hint else 0 ++ if total: ++ if total > 1: ++ suffix = 's' ++ what = 'them' ++ else: ++ suffix = '' ++ what = 'it' ++ conduit.info(2, 'Excluding %d update%s due to versionlock ' ++ '(use "yum versionlock status" to show %s)' ++ % (total, suffix, what)) ++ + if _version_lock_excluder_n: + _add_versionlock_whitelist(conduit) + if _version_lock_excluder_B_nevr: diff --git a/SOURCES/BZ-1506205-repotrack-add-repofrompath-opt.patch b/SOURCES/BZ-1506205-repotrack-add-repofrompath-opt.patch new file mode 100644 index 0000000..9e18cb6 --- /dev/null +++ b/SOURCES/BZ-1506205-repotrack-add-repofrompath-opt.patch @@ -0,0 +1,74 @@ +commit c01c1bab3b3530e04d9cb13bcf03d6f1a95a7f33 +Author: Michal Domonkos +Date: Fri Jun 22 22:39:30 2018 +0200 + + repotrack: add --repofrompath option. BZ 1506205 + + Code taken from repoquery. + +diff --git a/docs/repotrack.1 b/docs/repotrack.1 +index 6b46ed0..597b7a5 100644 +--- a/docs/repotrack.1 ++++ b/docs/repotrack.1 +@@ -19,6 +19,10 @@ Act as if running the specified arch (default: current arch). + .IP "\fB\-r REPOID, \-\-repoid=REPOID\fP" + Specify repo ids to query, can be specified multiple times (default is + all enabled). ++.IP "\fB\-\-repofrompath=,\fP" ++Specify repoid & paths of additional repositories - unique repoid and complete ++path required, can be specified multiple times. ++Example: --repofrompath=myrepo,/path/to/repo + .IP "\fB\-t, \-\-tempcache\fP" + Use a temp dir for storing/accessing yum-cache. + .IP "\fB\-p DESTDIR, \-\-download_path=DESTDIR\fP" +diff --git a/repotrack.py b/repotrack.py +index 8dd8b9c..79b761f 100755 +--- a/repotrack.py ++++ b/repotrack.py +@@ -87,6 +87,10 @@ def parseArgs(): + help='check as if running the specified arch (default: current arch)') + parser.add_option("-r", "--repoid", default=[], action='append', + help="specify repo ids to query, can be specified multiple times (default is all enabled)") ++ parser.add_option("--repofrompath", action="append", ++ help="specify repoid & paths of additional repositories - unique repoid " ++ "and complete path required, can be specified multiple times. " ++ "Example: --repofrompath=myrepo,/path/to/repo") + parser.add_option("-t", "--tempcache", default=False, action="store_true", + help="Use a temp dir for storing/accessing yum-cache") + parser.add_option("-p", "--download_path", dest='destdir', +@@ -157,8 +161,33 @@ def main(): + repo.enable() + my._getSacks(archlist=archlist, thisrepo=repo.id) + +- my.doRepoSetup() +- my._getSacks(archlist=archlist) ++ if opts.repofrompath: ++ for repo in opts.repofrompath: ++ tmp = tuple(repo.split(',')) ++ if len(tmp) != 2: ++ my.logger.error("Error: Bad repofrompath argument: %s" %repo) ++ continue ++ repoid, repopath = tmp ++ if repopath and repopath[0] == '/': ++ baseurl = 'file://' + repopath ++ else: ++ baseurl = repopath ++ try: ++ my.add_enable_repo(repoid, baseurls=[baseurl], ++ basecachedir=my.conf.cachedir, ++ timestamp_check=False) ++ except yum.Errors.DuplicateRepoError, e: ++ my.logger.error(e) ++ sys.exit(1) ++ if not opts.quiet: ++ my.logger.info("Added %s repo from %s" % (repoid, repopath)) ++ ++ try: ++ my.doRepoSetup() ++ my._getSacks(archlist=archlist) ++ except yum.Errors.RepoError, e: ++ my.logger.error(e) ++ sys.exit(1) + + unprocessed_pkgs = {} + final_pkgs = {} diff --git a/SOURCES/BZ-1600617-reposync-prevent-path-traversal.patch b/SOURCES/BZ-1600617-reposync-prevent-path-traversal.patch deleted file mode 100644 index a478370..0000000 --- a/SOURCES/BZ-1600617-reposync-prevent-path-traversal.patch +++ /dev/null @@ -1,119 +0,0 @@ -diff -up yum-utils-1.1.31/docs/reposync.1.orig yum-utils-1.1.31/docs/reposync.1 ---- yum-utils-1.1.31/docs/reposync.1.orig 2018-07-20 11:01:16.789747160 +0200 -+++ yum-utils-1.1.31/docs/reposync.1 2018-07-20 11:01:21.765700655 +0200 -@@ -47,6 +47,16 @@ Download all the non-default metadata. - Download only newest packages per-repo. - .IP "\fB\-q, \-\-quiet\fP" - Output as little information as possible. -+.IP "\fB\-\-allow-path-traversal\fP" -+Allow packages stored outside their repo directory to be synced. -+These are packages that are referenced in metadata by using absolute paths or -+up-level ".." symbols, and are normally skipped by \fBreposync\fR for security -+reasons. -+ -+\fBCAUTION:\fR Using this option has potential security implications since, by -+providing malicious repodata, an attacker could make \fBreposync\fR write to -+arbitrary locations on the file system that are accessible by the user running -+it. - .SH "EXAMPLES" - .IP "Sync all packages from the 'updates' repo to the current directory:" - \fB reposync \-\-repoid=updates\fP -diff -up yum-utils-1.1.31/reposync.py.orig yum-utils-1.1.31/reposync.py ---- yum-utils-1.1.31/reposync.py.orig 2018-07-20 11:01:16.752747506 +0200 -+++ yum-utils-1.1.31/reposync.py 2018-07-20 11:01:54.717392693 +0200 -@@ -84,6 +84,12 @@ def localpkgs(directory): - cache[name] = { 'path': fn, 'size': st.st_size, 'device': st.st_dev } - return cache - -+def is_subpath(path, root): -+ root = os.path.realpath(root) -+ path = os.path.realpath(os.path.join(root, path)) -+ # join() is used below to ensure root ends with a slash -+ return path.startswith(os.path.join(root, '')) -+ - def parseArgs(): - usage = """ - Reposync is used to synchronize a remote yum repository to a local -@@ -126,6 +132,10 @@ def parseArgs(): - parser.add_option("","--download-metadata", dest="downloadmd", - default=False, action="store_true", - help="download all the non-default metadata") -+ parser.add_option("", "--allow-path-traversal", default=False, -+ action="store_true", -+ help="Allow packages stored outside their repo directory to be synced " -+ "(UNSAFE, USE WITH CAUTION!)") - (opts, args) = parser.parse_args() - return (opts, args) - -@@ -226,13 +236,36 @@ def main(): - else: - local_repo_path = opts.destdir + '/' + repo.id - -+ # Ensure we don't traverse out of local_repo_path by dropping any -+ # packages whose remote_path is absolute or contains up-level -+ # references (unless explicitly allowed). -+ # See RHBZ#1600221 for details. -+ if not opts.allow_path_traversal: -+ newlist = [] -+ skipped = False -+ for pkg in download_list: -+ if is_subpath(pkg.remote_path, local_repo_path): -+ newlist.append(pkg) -+ continue -+ my.logger.warning( -+ 'WARNING: skipping package %s: remote path "%s" not ' -+ 'within repodir, unsafe to mirror locally' -+ % (pkg, pkg.remote_path) -+ ) -+ skipped = True -+ if skipped: -+ my.logger.info( -+ 'You can enable unsafe remote paths by using ' -+ '--allow-path-traversal (see reposync(1) for details)' -+ ) -+ download_list = newlist -+ - if opts.delete and os.path.exists(local_repo_path): - current_pkgs = localpkgs(local_repo_path) - - download_set = {} - for pkg in download_list: -- remote = pkg.returnSimple('relativepath') -- rpmname = os.path.basename(remote) -+ rpmname = os.path.basename(pkg.remote_path) - download_set[rpmname] = 1 - - for pkg in current_pkgs: -@@ -280,8 +313,7 @@ def main(): - local_size = 0 - if not opts.urls: - for pkg in download_list: -- remote = pkg.returnSimple('relativepath') -- local = local_repo_path + '/' + remote -+ local = os.path.join(local_repo_path, pkg.remote_path) - sz = int(pkg.returnSimple('packagesize')) - if os.path.exists(local) and os.path.getsize(local) == sz: - continue -@@ -293,10 +325,9 @@ def main(): - download_list.sort(sortPkgObj) - if opts.urls: - for pkg in download_list: -- remote = pkg.returnSimple('relativepath') -- local = os.path.join(local_repo_path, remote) -+ local = os.path.join(local_repo_path, pkg.remote_path) - if not (os.path.exists(local) and my.verifyPkg(local, pkg, False)): -- print urljoin(pkg.repo.urls[0], pkg.relativepath) -+ print urljoin(pkg.repo.urls[0], pkg.remote_path) - continue - - # create dest dir -@@ -305,8 +336,7 @@ def main(): - - # set localpaths - for pkg in download_list: -- rpmfn = pkg.remote_path -- pkg.localpath = os.path.join(local_repo_path, rpmfn) -+ pkg.localpath = os.path.join(local_repo_path, pkg.remote_path) - pkg.repo.copy_local = True - pkg.repo.cache = 0 - localdir = os.path.dirname(pkg.localpath) diff --git a/SOURCES/BZ-1600618-reposync-prevent-path-traversal.patch b/SOURCES/BZ-1600618-reposync-prevent-path-traversal.patch new file mode 100644 index 0000000..a478370 --- /dev/null +++ b/SOURCES/BZ-1600618-reposync-prevent-path-traversal.patch @@ -0,0 +1,119 @@ +diff -up yum-utils-1.1.31/docs/reposync.1.orig yum-utils-1.1.31/docs/reposync.1 +--- yum-utils-1.1.31/docs/reposync.1.orig 2018-07-20 11:01:16.789747160 +0200 ++++ yum-utils-1.1.31/docs/reposync.1 2018-07-20 11:01:21.765700655 +0200 +@@ -47,6 +47,16 @@ Download all the non-default metadata. + Download only newest packages per-repo. + .IP "\fB\-q, \-\-quiet\fP" + Output as little information as possible. ++.IP "\fB\-\-allow-path-traversal\fP" ++Allow packages stored outside their repo directory to be synced. ++These are packages that are referenced in metadata by using absolute paths or ++up-level ".." symbols, and are normally skipped by \fBreposync\fR for security ++reasons. ++ ++\fBCAUTION:\fR Using this option has potential security implications since, by ++providing malicious repodata, an attacker could make \fBreposync\fR write to ++arbitrary locations on the file system that are accessible by the user running ++it. + .SH "EXAMPLES" + .IP "Sync all packages from the 'updates' repo to the current directory:" + \fB reposync \-\-repoid=updates\fP +diff -up yum-utils-1.1.31/reposync.py.orig yum-utils-1.1.31/reposync.py +--- yum-utils-1.1.31/reposync.py.orig 2018-07-20 11:01:16.752747506 +0200 ++++ yum-utils-1.1.31/reposync.py 2018-07-20 11:01:54.717392693 +0200 +@@ -84,6 +84,12 @@ def localpkgs(directory): + cache[name] = { 'path': fn, 'size': st.st_size, 'device': st.st_dev } + return cache + ++def is_subpath(path, root): ++ root = os.path.realpath(root) ++ path = os.path.realpath(os.path.join(root, path)) ++ # join() is used below to ensure root ends with a slash ++ return path.startswith(os.path.join(root, '')) ++ + def parseArgs(): + usage = """ + Reposync is used to synchronize a remote yum repository to a local +@@ -126,6 +132,10 @@ def parseArgs(): + parser.add_option("","--download-metadata", dest="downloadmd", + default=False, action="store_true", + help="download all the non-default metadata") ++ parser.add_option("", "--allow-path-traversal", default=False, ++ action="store_true", ++ help="Allow packages stored outside their repo directory to be synced " ++ "(UNSAFE, USE WITH CAUTION!)") + (opts, args) = parser.parse_args() + return (opts, args) + +@@ -226,13 +236,36 @@ def main(): + else: + local_repo_path = opts.destdir + '/' + repo.id + ++ # Ensure we don't traverse out of local_repo_path by dropping any ++ # packages whose remote_path is absolute or contains up-level ++ # references (unless explicitly allowed). ++ # See RHBZ#1600221 for details. ++ if not opts.allow_path_traversal: ++ newlist = [] ++ skipped = False ++ for pkg in download_list: ++ if is_subpath(pkg.remote_path, local_repo_path): ++ newlist.append(pkg) ++ continue ++ my.logger.warning( ++ 'WARNING: skipping package %s: remote path "%s" not ' ++ 'within repodir, unsafe to mirror locally' ++ % (pkg, pkg.remote_path) ++ ) ++ skipped = True ++ if skipped: ++ my.logger.info( ++ 'You can enable unsafe remote paths by using ' ++ '--allow-path-traversal (see reposync(1) for details)' ++ ) ++ download_list = newlist ++ + if opts.delete and os.path.exists(local_repo_path): + current_pkgs = localpkgs(local_repo_path) + + download_set = {} + for pkg in download_list: +- remote = pkg.returnSimple('relativepath') +- rpmname = os.path.basename(remote) ++ rpmname = os.path.basename(pkg.remote_path) + download_set[rpmname] = 1 + + for pkg in current_pkgs: +@@ -280,8 +313,7 @@ def main(): + local_size = 0 + if not opts.urls: + for pkg in download_list: +- remote = pkg.returnSimple('relativepath') +- local = local_repo_path + '/' + remote ++ local = os.path.join(local_repo_path, pkg.remote_path) + sz = int(pkg.returnSimple('packagesize')) + if os.path.exists(local) and os.path.getsize(local) == sz: + continue +@@ -293,10 +325,9 @@ def main(): + download_list.sort(sortPkgObj) + if opts.urls: + for pkg in download_list: +- remote = pkg.returnSimple('relativepath') +- local = os.path.join(local_repo_path, remote) ++ local = os.path.join(local_repo_path, pkg.remote_path) + if not (os.path.exists(local) and my.verifyPkg(local, pkg, False)): +- print urljoin(pkg.repo.urls[0], pkg.relativepath) ++ print urljoin(pkg.repo.urls[0], pkg.remote_path) + continue + + # create dest dir +@@ -305,8 +336,7 @@ def main(): + + # set localpaths + for pkg in download_list: +- rpmfn = pkg.remote_path +- pkg.localpath = os.path.join(local_repo_path, rpmfn) ++ pkg.localpath = os.path.join(local_repo_path, pkg.remote_path) + pkg.repo.copy_local = True + pkg.repo.cache = 0 + localdir = os.path.dirname(pkg.localpath) diff --git a/SPECS/yum-utils.spec b/SPECS/yum-utils.spec index 24eb629..37cc210 100644 --- a/SPECS/yum-utils.spec +++ b/SPECS/yum-utils.spec @@ -12,7 +12,7 @@ Summary: Utilities based around the yum package manager Name: yum-utils Version: 1.1.31 -Release: 46%{?dist} +Release: 50%{?dist} License: GPLv2+ Group: Development/Tools Source: http://yum.baseurl.org/download/yum-utils/%{name}-%{version}.tar.gz @@ -72,7 +72,12 @@ Patch165: BZ-1437636-yum-builddep-add-define-opt.patch Patch166: BZ-1349433-verifytree-handle-no-core-group.patch Patch167: BZ-1333353-verifytree-fix-handling-no-comps.patch Patch168: BZ-1127783-transaction-actions-fix-file-globs.patch -Patch169: BZ-1600617-reposync-prevent-path-traversal.patch + +#rhel-7.6 +Patch180: BZ-1497351-versionlock-add-hint-and-status-cmd.patch +Patch181: BZ-1506205-repotrack-add-repofrompath-opt.patch +Patch182: BZ-1600618-reposync-prevent-path-traversal.patch +Patch183: BZ-1493489-yum-config-manager-fix-add-repo-2.patch URL: http://yum.baseurl.org/download/yum-utils/ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) @@ -512,7 +517,12 @@ This plugin touches rpmdb files to work around overlayfs issues. %patch166 -p1 %patch167 -p1 %patch168 -p1 -%patch169 -p1 + +#rhel-7.6 +%patch180 -p1 +%patch181 -p1 +%patch182 -p1 +%patch183 -p1 %install rm -rf $RPM_BUILD_ROOT @@ -844,9 +854,31 @@ fi %{_mandir}/man1/yum-ovl.1.* %changelog -* Fri Jul 20 2018 Michal Domonkos - 1.1.31-46 -- reposync: prevent path traversal. -- Resolves: bug#1600617 +* Fri Aug 24 2018 Michal Domonkos - 1.1.31-50 +- yum-config-manager: additional fixes for --add-repo +- Related: bug#1493489 +- repotrack: print error instead of traceback on unavailable --repofrompath +- Related: bug#1506205 +- versionlock: note default value of show_hint in man page +- Related: bug#1497351 + +* Fri Jul 20 2018 Michal Domonkos - 1.1.31-49 +- reposync: fix up traversal patch and have it reference new CVE bug +- Resolves: bug#1600618 + +* Sat Jun 23 2018 Michal Domonkos - 1.1.31-48 +- yum-config-manager: fix --add-repo dummy URL +- Resolves: bug#1493489 + +* Fri Jun 22 2018 Michal Domonkos - 1.1.31-47 +- repotrack: add --repofrompath option +- Resolves: bug#1506205 +- reposync: check for .. in remote paths +- Resolves: bug#1552328 + +* Fri Jun 22 2018 Michal Domonkos - 1.1.31-46 +- versionlock: add hint and "status" subcommand +- Resolves: bug#1497351 * Tue Nov 21 2017 Valentina Mukhamedzhanova - 1.1.31-45 - Fix file globbing in transaction-actions.