commit: ec654122c0eb191c90ffb2c191403d342dbc361e Author: Zac Medico <zmedico <AT> gentoo <DOT> org> AuthorDate: Sun Mar 1 05:58:00 2020 +0000 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> CommitDate: Sun Mar 1 05:58:46 2020 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=ec654122
fetch: drop privileges early for NFS root_squash (bug 601252) Drop privileges prior to fetch function calls, so that all necessary operations can succeed when DISTDIR is on NFS with root_squash enabled. Bug: https://bugs.gentoo.org/601252 Signed-off-by: Zac Medico <zmedico <AT> gentoo.org> lib/_emerge/EbuildFetcher.py | 12 +++++++++++- lib/portage/package/ebuild/doebuild.py | 22 +++++++++++++++++----- lib/portage/package/ebuild/fetch.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/lib/_emerge/EbuildFetcher.py b/lib/_emerge/EbuildFetcher.py index c9e03dc97..d315d4f02 100644 --- a/lib/_emerge/EbuildFetcher.py +++ b/lib/_emerge/EbuildFetcher.py @@ -12,7 +12,12 @@ from portage import _unicode_encode from portage import _unicode_decode from portage.checksum import _hash_filter from portage.elog.messages import eerror -from portage.package.ebuild.fetch import _check_distfile, fetch +from portage.package.ebuild.fetch import ( + _check_distfile, + _drop_privs_userfetch, + _want_userfetch, + fetch, +) from portage.util._async.AsyncTaskFuture import AsyncTaskFuture from portage.util._async.ForkProcess import ForkProcess from portage.util.futures.compat_coroutine import coroutine @@ -239,6 +244,11 @@ class _EbuildFetcherProcess(ForkProcess): portage.output.havecolor = self._settings.get('NOCOLOR') \ not in ('yes', 'true') + # For userfetch, drop privileges for the entire fetch call, in + # order to handle DISTDIR on NFS with root_squash for bug 601252. + if _want_userfetch(self._settings): + _drop_privs_userfetch(self._settings) + rval = 1 allow_missing = self._get_manifest().allow_missing or \ 'digest' in self._settings.features diff --git a/lib/portage/package/ebuild/doebuild.py b/lib/portage/package/ebuild/doebuild.py index 92e9d755c..71e3a74ce 100644 --- a/lib/portage/package/ebuild/doebuild.py +++ b/lib/portage/package/ebuild/doebuild.py @@ -1,4 +1,4 @@ -# Copyright 2010-2019 Gentoo Authors +# Copyright 2010-2020 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 from __future__ import unicode_literals @@ -30,7 +30,7 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.package.ebuild.config:check_config_instance', 'portage.package.ebuild.digestcheck:digestcheck', 'portage.package.ebuild.digestgen:digestgen', - 'portage.package.ebuild.fetch:fetch', + 'portage.package.ebuild.fetch:_drop_privs_userfetch,_want_userfetch,fetch', 'portage.package.ebuild.prepare_build_dirs:_prepare_fake_distdir', 'portage.package.ebuild._ipc.QueryCommand:QueryCommand', 'portage.dep._slot_operator:evaluate_slot_operator_equal_deps', @@ -83,6 +83,7 @@ from portage.util.cpuinfo import get_cpu_count from portage.util.lafilefixer import rewrite_lafile from portage.util.compression_probe import _compressors from portage.util.futures import asyncio +from portage.util.futures.executor.fork import ForkExecutor from portage.util.path import first_existing from portage.util.socks5 import get_socks5_proxy from portage.versions import _pkgsplit @@ -1082,9 +1083,20 @@ def doebuild(myebuild, mydo, _unused=DeprecationWarning, settings=None, debug=0, dist_digests = None if mf is not None: dist_digests = mf.getTypeDigests("DIST") - if not fetch(fetchme, mysettings, listonly=listonly, - fetchonly=fetchonly, allow_missing_digests=False, - digests=dist_digests): + + def _fetch_subprocess(fetchme, mysettings, listonly, dist_digests): + # For userfetch, drop privileges for the entire fetch call, in + # order to handle DISTDIR on NFS with root_squash for bug 601252. + if _want_userfetch(mysettings): + _drop_privs_userfetch(mysettings) + + return fetch(fetchme, mysettings, listonly=listonly, + fetchonly=fetchonly, allow_missing_digests=False, + digests=dist_digests) + + loop = asyncio._safe_loop() + if not loop.run_until_complete(loop.run_in_executor(ForkExecutor(loop=loop), + _fetch_subprocess, fetchme, mysettings, listonly, dist_digests)): # Since listonly mode is called by emerge --pretend in an # asynchronous context, spawn_nofetch would trigger event loop # recursion here, therefore delegate execution of pkg_nofetch diff --git a/lib/portage/package/ebuild/fetch.py b/lib/portage/package/ebuild/fetch.py index 06118b1a6..f7984130f 100644 --- a/lib/portage/package/ebuild/fetch.py +++ b/lib/portage/package/ebuild/fetch.py @@ -69,6 +69,37 @@ _userpriv_spawn_kwargs = ( def _hide_url_passwd(url): return re.sub(r'//(.+):.+@(.+)', r'//\1:*password*@\2', url) + +def _want_userfetch(settings): + """ + Check if it's desirable to drop privileges for userfetch. + + @param settings: portage config + @type settings: portage.package.ebuild.config.config + @return: True if desirable, False otherwise + """ + return ('userfetch' in settings.features and + portage.data.secpass >= 2 and os.getuid() == 0) + + +def _drop_privs_userfetch(settings): + """ + Drop privileges for userfetch, and update portage.data.secpass + to correspond to the new privilege level. + """ + spawn_kwargs = dict(_userpriv_spawn_kwargs) + try: + _ensure_distdir(settings, settings['DISTDIR']) + except PortageException: + if not os.path.isdir(settings['DISTDIR']): + raise + os.setgid(int(spawn_kwargs['gid'])) + os.setgroups(spawn_kwargs['groups']) + os.setuid(int(spawn_kwargs['uid'])) + os.umask(spawn_kwargs['umask']) + portage.data.secpass = 1 + + def _spawn_fetch(settings, args, **kwargs): """ Spawn a process with appropriate settings for fetching, including
