From 8fcff7446ffe8d26d87a166039088c7db060f17c Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Sun, 4 Jan 2009 14:00:30 +0000 Subject: [PATCH] Cope better with being run through sudo If we find ourselves running as root but $HOME is owned by a user, get root's real home from /etc/passwd and use that for the configuration and cache files and for gpg's home. Otherwise, we are likely to create root-owned files in the user's home directory. --- zeroinstall/injector/gpg.py | 19 ++++++++++++------- zeroinstall/support/basedir.py | 26 ++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/zeroinstall/injector/gpg.py b/zeroinstall/injector/gpg.py index 9aeb1ad..6f92542 100644 --- a/zeroinstall/injector/gpg.py +++ b/zeroinstall/injector/gpg.py @@ -15,10 +15,15 @@ import os import tempfile from logging import info -from zeroinstall import support +from zeroinstall.support import find_in_path, basedir from zeroinstall.injector.trust import trust_db from zeroinstall.injector.model import SafeException +_gnupg_options = ['gpg', '--no-secmem-warning'] +if os.geteuid() == 0 and 'GNUPGHOME' not in os.environ: + _gnupg_options += ['--homedir', os.path.join(basedir.home, '.gnupg')] + info("Running as root, so setting GnuPG home to %s", _gnupg_options[-1]) + class Signature(object): """Abstract base class for signature check results.""" status = None @@ -55,7 +60,7 @@ class ValidSig(Signature): def get_details(self): """Call 'gpg --list-keys' and return the results split into lines and columns. @rtype: [[str]]""" - child = subprocess.Popen(['gpg', '--with-colons', '--no-secmem-warning', '--list-keys', self.fingerprint], stdout = subprocess.PIPE) + child = subprocess.Popen(_gnupg_options + ['--with-colons', '--list-keys', self.fingerprint], stdout = subprocess.PIPE) cout, unused = child.communicate() if child.returncode: info("GPG exited with code %d" % child.returncode) @@ -128,7 +133,7 @@ def load_keys(fingerprints): current_fpr = None current_uid = None - cin, cout = os.popen2(['gpg', '--fixed-list-mode', '--with-colons', '--list-keys', + cin, cout = os.popen2(_gnupg_options + ['--fixed-list-mode', '--with-colons', '--list-keys', '--with-fingerprint', '--with-fingerprint'] + fingerprints) cin.close() try: @@ -167,7 +172,7 @@ def import_key(stream): """Run C{gpg --import} with this stream as stdin.""" errors = tempfile.TemporaryFile() - child = subprocess.Popen(['gpg', '--no-secmem-warning', '--quiet', '--import'], + child = subprocess.Popen(_gnupg_options + ['--quiet', '--import'], stdin = stream, stderr = errors) status = child.wait() @@ -186,7 +191,7 @@ def _check_plain_stream(stream): status_r, status_w = os.pipe() # Note: Should ideally close status_r in the child, but we want to support Windows too - child = subprocess.Popen(['gpg', '--no-secmem-warning', '--decrypt', + child = subprocess.Popen(_gnupg_options + ['--decrypt', # Not all versions support this: #'--max-output', str(1024 * 1024), '--batch', @@ -245,7 +250,7 @@ def _check_xml_stream(stream): status_r, status_w = os.pipe() # Note: Should ideally close status_r in the child, but we want to support Windows too - child = subprocess.Popen(['gpg', '--no-secmem-warning', + child = subprocess.Popen(_gnupg_options + [ # Not all versions support this: #'--max-output', str(1024 * 1024), '--batch', @@ -272,7 +277,7 @@ def check_stream(stream): data is the original stream). stream must be seekable. @note: Stream returned may or may not be the one passed in. Be careful! @return: (data_stream, [Signatures])""" - if not support.find_in_path('gpg'): + if not find_in_path('gpg'): raise SafeException("GnuPG is not installed ('gpg' not in $PATH). See http://gnupg.org") #stream.seek(0) diff --git a/zeroinstall/support/basedir.py b/zeroinstall/support/basedir.py index 8d2b967..92bdf07 100644 --- a/zeroinstall/support/basedir.py +++ b/zeroinstall/support/basedir.py @@ -4,6 +4,10 @@ Support code for the freedesktop.org basedir spec. This module provides functions for locating configuration files. @see: U{http://freedesktop.org/wiki/Standards/basedir-spec} + +@var home: The value of $HOME (or '/' if not set). If we're running as root and +$HOME isn't owned by root, then this will be root's home from /etc/passwd +instead. """ # Copyright (C) 2006, Thomas Leonard @@ -11,22 +15,36 @@ This module provides functions for locating configuration files. import os -_home = os.environ.get('HOME', '/') +home = os.environ.get('HOME', '/') + +if os.geteuid() == 0: + # We're running as root. Ensure that $HOME really is root's home, + # not the user's home, or we're likely to fill it will unreadable + # root-owned files. + home_owner = os.stat(home).st_uid + if home_owner != 0: + import pwd + from logging import info + old_home = home + home = pwd.getpwuid(0).pw_dir or '/' + info("$HOME (%s) is owned by user %d, but we are root (0). Using %s instead.", old_home, home_owner, home) + del old_home + del home_owner xdg_data_home = os.environ.get('XDG_DATA_HOME', - os.path.join(_home, '.local', 'share')) + os.path.join(home, '.local', 'share')) xdg_data_dirs = [xdg_data_home] + \ os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':') xdg_cache_home = os.environ.get('XDG_CACHE_HOME', - os.path.join(_home, '.cache')) + os.path.join(home, '.cache')) xdg_cache_dirs = [xdg_cache_home] + \ os.environ.get('XDG_CACHE_DIRS', '/var/cache').split(':') xdg_config_home = os.environ.get('XDG_CONFIG_HOME', - os.path.join(_home, '.config')) + os.path.join(home, '.config')) xdg_config_dirs = [xdg_config_home] + \ os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg').split(':') -- 2.11.4.GIT