[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gentoo-portage-dev
Subject:    [gentoo-portage-dev] [PATCH] fetch: drop privileges early for NFS root_squash (bug 601252)
From:       Zac Medico <zmedico () gentoo ! org>
Date:       2020-02-15 22:38:59
Message-ID: 20200215223859.45671-1-zmedico () gentoo ! org
[Download RAW message or body]

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@gentoo.org>
---
 lib/_emerge/EbuildFetcher.py           | 12 +++++++++++-
 lib/portage/package/ebuild/doebuild.py | 20 +++++++++++++++----
 lib/portage/package/ebuild/fetch.py    | 27 ++++++++++++++++++++++++++
 3 files changed, 54 insertions(+), 5 deletions(-)

diff --git a/lib/_emerge/EbuildFetcher.py b/lib/_emerge/EbuildFetcher.py
index ad5109c28..b9dc3a10c 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._pty import _create_pty_or_pipe
@@ -229,6 +234,11 @@ class _EbuildFetcherProcess(ForkProcess):
 		self._settings = None
 
 	def _run(self):
+		# 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)
+
 		# Force consistent color output, in case we are capturing fetch
 		# output through a normal pipe due to unavailability of ptys.
 		portage.output.havecolor = self._settings.get('NOCOLOR') \
diff --git a/lib/portage/package/ebuild/doebuild.py b/lib/portage/package/ebuild/doebuild.py
index 92e9d755c..be4a6019d 100644
--- a/lib/portage/package/ebuild/doebuild.py
+++ b/lib/portage/package/ebuild/doebuild.py
@@ -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..6189c0245 100644
--- a/lib/portage/package/ebuild/fetch.py
+++ b/lib/portage/package/ebuild/fetch.py
@@ -69,6 +69,33 @@ _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)
+	_ensure_distdir(settings, settings['DISTDIR'])
+	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
-- 
2.24.1


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic