diff options
author | R David Murray <rdmurray@bitdance.com> | 2013-09-18 00:30:02 (GMT) |
---|---|---|
committer | R David Murray <rdmurray@bitdance.com> | 2013-09-18 00:30:02 (GMT) |
commit | 104aab956f6de4131cab800f742cc108f9f92edf (patch) | |
tree | 2d651d492a76a00270e0c86bf9c1a2a289e12219 | |
parent | c17a8dfaca76016202c49b2150c946f5ba70db29 (diff) | |
download | cpython-104aab956f6de4131cab800f742cc108f9f92edf.zip cpython-104aab956f6de4131cab800f742cc108f9f92edf.tar.gz cpython-104aab956f6de4131cab800f742cc108f9f92edf.tar.bz2 |
#14984: On POSIX, enforce permissions when reading default .netrc.
Initial patch by Bruno Piguet.
This is implemented as if a useful .netrc file could exist without passwords,
which is possible in the general case; but in fact our netrc implementation
does not support it. Fixing that issue will be an enhancement.
-rw-r--r-- | Doc/library/netrc.rst | 8 | ||||
-rw-r--r-- | Lib/netrc.py | 27 | ||||
-rw-r--r-- | Lib/test/test_netrc.py | 26 | ||||
-rw-r--r-- | Misc/NEWS | 6 |
4 files changed, 61 insertions, 6 deletions
diff --git a/Doc/library/netrc.rst b/Doc/library/netrc.rst index 91990df..b6c1066 100644 --- a/Doc/library/netrc.rst +++ b/Doc/library/netrc.rst @@ -19,6 +19,14 @@ the Unix :program:`ftp` program and other FTP clients. no argument is given, the file :file:`.netrc` in the user's home directory will be read. Parse errors will raise :exc:`NetrcParseError` with diagnostic information including the file name, line number, and terminating token. + If no argument is specified on a POSIX system, the presence of passwords in + the :file:`.netrc` file will raise a :exc:`NetrcParseError` if the file + ownership or permissions are insecure (owned by a user other than the user + running the process, or accessible for read or write by any other user). + This implements security behavior equivalent to that of ftp and other + programs that use :file:`.netrc`. + + .. versionchanged:: 3.1.6 Added the POSIX permission check. .. exception:: NetrcParseError diff --git a/Lib/netrc.py b/Lib/netrc.py index c96db6f..8fe4cee 100644 --- a/Lib/netrc.py +++ b/Lib/netrc.py @@ -2,7 +2,7 @@ # Module and documentation by Eric S. Raymond, 21 Dec 1998 -import io, os, shlex +import io, os, shlex, stat, pwd __all__ = ["netrc", "NetrcParseError"] @@ -21,6 +21,7 @@ class NetrcParseError(Exception): class netrc: def __init__(self, file=None): + default_netrc = file is None if file is None: try: file = os.path.join(os.environ['HOME'], ".netrc") @@ -29,9 +30,9 @@ class netrc: self.hosts = {} self.macros = {} with open(file) as fp: - self._parse(file, fp) + self._parse(file, fp, default_netrc) - def _parse(self, file, fp): + def _parse(self, file, fp, default_netrc): lexer = shlex.shlex(fp) lexer.wordchars += r"""!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~""" lexer.commenters = lexer.commenters.replace('#', '') @@ -86,6 +87,26 @@ class netrc: elif tt == 'account': account = lexer.get_token() elif tt == 'password': + if os.name == 'posix' and default_netrc: + prop = os.fstat(fp.fileno()) + if prop.st_uid != os.getuid(): + try: + fowner = pwd.getpwuid(prop.st_uid)[0] + except KeyError: + fowner = 'uid %s' % prop.st_uid + try: + user = pwd.getpwuid(os.getuid())[0] + except KeyError: + user = 'uid %s' % os.getuid() + raise NetrcParseError( + ("~/.netrc file owner (%s) does not match" + " current user (%s)") % (fowner, user), + file, lexer.lineno) + if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)): + raise NetrcParseError( + "~/.netrc access too permissive: access" + " permissions must restrict access to only" + " the owner", file, lexer.lineno) password = lexer.get_token() else: raise NetrcParseError("bad follower token %r" % tt, diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py index ef70e37..60a3ec9 100644 --- a/Lib/test/test_netrc.py +++ b/Lib/test/test_netrc.py @@ -5,9 +5,6 @@ temp_filename = support.TESTFN class NetrcTestCase(unittest.TestCase): - def tearDown(self): - os.unlink(temp_filename) - def make_nrc(self, test_data): test_data = textwrap.dedent(test_data) mode = 'w' @@ -15,6 +12,7 @@ class NetrcTestCase(unittest.TestCase): mode += 't' with open(temp_filename, mode) as fp: fp.write(test_data) + self.addCleanup(os.unlink, temp_filename) return netrc.netrc(temp_filename) def test_default(self): @@ -103,6 +101,28 @@ class NetrcTestCase(unittest.TestCase): """, '#pass') + @unittest.skipUnless(os.name == 'posix', 'POSIX only test') + def test_security(self): + # This test is incomplete since we are normally not run as root and + # therefore can't test the file ownership being wrong. + d = support.TESTFN + os.mkdir(d) + self.addCleanup(support.rmtree, d) + fn = os.path.join(d, '.netrc') + with open(fn, 'wt') as f: + f.write("""\ + machine foo.domain.com login bar password pass + default login foo password pass + """) + with support.EnvironmentVarGuard() as environ: + environ.set('HOME', d) + os.chmod(fn, 0o600) + nrc = netrc.netrc() + self.assertEqual(nrc.hosts['foo.domain.com'], + ('bar', None, 'pass')) + os.chmod(fn, 0o622) + self.assertRaises(netrc.NetrcParseError, netrc.netrc) + def test_main(): support.run_unittest(NetrcTestCase) @@ -13,6 +13,12 @@ Core and Builtins Library ------- +- Issue #14984: On POSIX systems, when netrc is called without a filename + argument (and therefore is reading the user's $HOME/.netrc file), it now + enforces the same security rules as typical ftp clients: the .netrc file must + be owned by the user that owns the process and must not be readable by any + other user. + - Issue #16248: Disable code execution from the user's home directory by tkinter when the -E flag is passed to Python. |