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

Reply via email to