From 1c8b8fc4063852d2b8f09800007653ba047fe648 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Wed, 2 May 2018 22:47:47 -0500 Subject: [PATCH 1/2] appdirs: return paths relative to effective user's homedir Since `expanduser()` is used to expand the `~` to the home directory, `~/.cache` will always return the home directory of the user under which the python process is running. However, if pip is executed from within a Python process in which the euid has changed, this means that the cache dir (and other paths) will not be writable, causing pip to fail. This commit fixes that issue by attempting to get the username matching the euid, and using that username (e.g. `~user/.cache`) when invoking `expanduser()`. --- src/pip/_internal/utils/appdirs.py | 73 ++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/src/pip/_internal/utils/appdirs.py b/src/pip/_internal/utils/appdirs.py index 28c5d4b3c75..d650524f31d 100644 --- a/src/pip/_internal/utils/appdirs.py +++ b/src/pip/_internal/utils/appdirs.py @@ -11,6 +11,18 @@ from pip._internal.compat import WINDOWS, expanduser +try: + import pwd +except ImportError: + pwd = None + + +def _effective_user(): + try: + return pwd.getpwuid(os.geteuid()).pw_name + except Exception: + return '' + def user_cache_dir(appname): r""" @@ -44,15 +56,17 @@ def user_cache_dir(appname): # Add our app name and Cache directory to it path = os.path.join(path, appname, "Cache") - elif sys.platform == "darwin": - # Get the base path - path = expanduser("~/Library/Caches") - - # Add our app name to it - path = os.path.join(path, appname) else: - # Get the base path - path = os.getenv("XDG_CACHE_HOME", expanduser("~/.cache")) + user = _effective_user() + if sys.platform == "darwin": + # Get the base path + path = expanduser("~{0}/Library/Caches".format(user)) + else: + # Get the base path + path = os.getenv( + "XDG_CACHE_HOME", + expanduser("~{0}/.cache".format(user)) + ) # Add our app name to it path = os.path.join(path, appname) @@ -91,23 +105,28 @@ def user_data_dir(appname, roaming=False): if WINDOWS: const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA" path = os.path.join(os.path.normpath(_get_win_folder(const)), appname) - elif sys.platform == "darwin": - path = os.path.join( - expanduser('~/Library/Application Support/'), - appname, - ) if os.path.isdir(os.path.join( - expanduser('~/Library/Application Support/'), - appname, - ) - ) else os.path.join( - expanduser('~/.config/'), - appname, - ) else: - path = os.path.join( - os.getenv('XDG_DATA_HOME', expanduser("~/.local/share")), - appname, - ) + user = _effective_user() + if sys.platform == "darwin": + path = os.path.join( + expanduser('~{0}/Library/Application Support/'.format(user)), + appname, + ) if os.path.isdir(os.path.join( + expanduser('~{0}/Library/Application Support/'.format(user)), + appname, + ) + ) else os.path.join( + expanduser('~{0}/.config/'.format(user)), + appname, + ) + else: + path = os.path.join( + os.getenv( + 'XDG_DATA_HOME', + expanduser("~{0}/.local/share".format(user)) + ), + appname, + ) return path @@ -137,7 +156,11 @@ def user_config_dir(appname, roaming=True): elif sys.platform == "darwin": path = user_data_dir(appname) else: - path = os.getenv('XDG_CONFIG_HOME', expanduser("~/.config")) + user = _effective_user() + path = os.getenv( + 'XDG_CONFIG_HOME', + expanduser("~{0}/.config".format(user)) + ) path = os.path.join(path, appname) return path From 1915d8b0ccf57f28da277006c360a211f6eaaa33 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Thu, 3 May 2018 00:12:36 -0500 Subject: [PATCH 2/2] Add news file for #5359 bugfix --- news/5359.bugfix | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 news/5359.bugfix diff --git a/news/5359.bugfix b/news/5359.bugfix new file mode 100644 index 00000000000..48b5fb7fb76 --- /dev/null +++ b/news/5359.bugfix @@ -0,0 +1,48 @@ +When executing a pip install from within a Python process in which the euid has +been changed, the appdirs helpers which use `expanduser()` return paths +relative to the home directory of the actual user, rather than the effective +user. This causes the install to fail when the effective user cannot write to +the cachedir: + +.. code-block:: python + + >>> import subprocess + >>> import os + >>> os.geteuid() + 0 + >>> os.seteuid(1000) + >>> os.geteuid() + 1000 + >>> p = subprocess.Popen(['/home/erik/virtualenv/pipdev/bin/pip', 'install', 'pudb'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + >>> stdout, stderr = p.communicate() + >>> p.returncode + 1 + >>> stderr + "Could not install packages due to an EnvironmentError: [Errno 13] Permission denied: '/root/.cache/pip/wheels/ec/bb/79/09cd8a9982b637b251208ac3400f9702c72e63accc8e4b69e3'\nConsider using the `--user` option or check the permissions.\n\n" + >>> + +With this bugfix, the installation proceeds as expected, and the sources/wheels +are downloaded to the effective user's cachedir: + +.. code-block:: python + + >>> import subprocess + >>> import os + >>> os.geteuid() + 0 + >>> os.seteuid(1000) + >>> os.geteuid() + 1000 + >>> p = subprocess.Popen(['/home/erik/virtualenv/pipdev/bin/pip', 'install', 'pudb'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + >>> stdout, stderr = p.communicate() + >>> p.returncode + 0 + >>> print(stdout) + Collecting pudb + Collecting urwid>=1.1.1 (from pudb) + Collecting pygments>=1.0 (from pudb) + Using cached https://files.pythonhosted.org/packages/02/ee/b6e02dc6529e82b75bb06823ff7d005b141037cb1416b10c6f00fc419dca/Pygments-2.2.0-py2.py3-none-any.whl + Installing collected packages: urwid, pygments, pudb + Successfully installed pudb-2017.1.4 pygments-2.2.0 urwid-2.0.1 + + >>>