Skip to content
Permalink
Browse files

reposync: prevent path traversal. BZ 1552328

  • Loading branch information...
dmnks committed Jul 19, 2018
1 parent 7554c01 commit 6a8de061f8fdc885e74ebe8c94625bf53643b71c
Showing with 44 additions and 0 deletions.
  1. +10 −0 docs/reposync.1
  2. +34 −0 reposync.py
@@ -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
@@ -74,6 +74,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
@@ -116,6 +122,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)

@@ -216,6 +226,30 @@ 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)

0 comments on commit 6a8de06

Please sign in to comment.
You can’t perform that action at this time.