cpython: 0d9e471221da (original) (raw)
Mercurial > cpython
changeset 85740:0d9e471221da 3.2
Merge #14984: On POSIX, enforce permissions when reading default .netrc. [#14984]
R David Murray rdmurray@bitdance.com | |
---|---|
date | Tue, 17 Sep 2013 20:32:54 -0400 |
parents | 778239ec8162(current diff)6396d1fc72da(diff) |
children | b657196b9fc2 ef90c40fe6cf |
files | Doc/library/netrc.rst Misc/NEWS |
diffstat | 4 files changed, 61 insertions(+), 6 deletions(-)[+] [-] Doc/library/netrc.rst 8 Lib/netrc.py 27 Lib/test/test_netrc.py 26 Misc/NEWS 6 |
line wrap: on
line diff
--- a/Doc/library/netrc.rst
+++ b/Doc/library/netrc.rst
@@ -22,6 +22,14 @@ the Unix :program:ftp
program and othe
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.2.6 Added the POSIX permission check.
.. exception:: NetrcParseError
--- 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[](#l2.16) if file is None:[](#l2.17) try:[](#l2.18) file = os.path.join(os.environ['HOME'], ".netrc")[](#l2.19)
@@ -29,9 +30,9 @@ class netrc: self.hosts = {} self.macros = {} with open(file) as fp:
self._parse(file, fp)[](#l2.24)
self._parse(file, fp, default_netrc)[](#l2.25)
- 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:[](#l2.36)
prop = os.fstat(fp.fileno())[](#l2.37)
if prop.st_uid != os.getuid():[](#l2.38)
try:[](#l2.39)
fowner = pwd.getpwuid(prop.st_uid)[0][](#l2.40)
except KeyError:[](#l2.41)
fowner = 'uid %s' % prop.st_uid[](#l2.42)
try:[](#l2.43)
user = pwd.getpwuid(os.getuid())[0][](#l2.44)
except KeyError:[](#l2.45)
user = 'uid %s' % os.getuid()[](#l2.46)
raise NetrcParseError([](#l2.47)
("~/.netrc file owner (%s) does not match"[](#l2.48)
" current user (%s)") % (fowner, user),[](#l2.49)
file, lexer.lineno)[](#l2.50)
if (prop.st_mode & (stat.S_IRWXG | stat.S_IRWXO)):[](#l2.51)
raise NetrcParseError([](#l2.52)
"~/.netrc access too permissive: access"[](#l2.53)
" permissions must restrict access to only"[](#l2.54)
" the owner", file, lexer.lineno)[](#l2.55) password = lexer.get_token()[](#l2.56) else:[](#l2.57) raise NetrcParseError("bad follower token %r" % tt,[](#l2.58)
--- 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 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)[](#l3.17) return netrc.netrc(temp_filename)[](#l3.18)
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[](#l3.27)
# therefore can't test the file ownership being wrong.[](#l3.28)
d = support.TESTFN[](#l3.29)
os.mkdir(d)[](#l3.30)
self.addCleanup(support.rmtree, d)[](#l3.31)
fn = os.path.join(d, '.netrc')[](#l3.32)
with open(fn, 'wt') as f:[](#l3.33)
f.write("""\[](#l3.34)
machine foo.domain.com login bar password pass[](#l3.35)
default login foo password pass[](#l3.36)
""")[](#l3.37)
with support.EnvironmentVarGuard() as environ:[](#l3.38)
environ.set('HOME', d)[](#l3.39)
os.chmod(fn, 0o600)[](#l3.40)
nrc = netrc.netrc()[](#l3.41)
self.assertEqual(nrc.hosts['foo.domain.com'],[](#l3.42)
('bar', None, 'pass'))[](#l3.43)
os.chmod(fn, 0o622)[](#l3.44)
self.assertRaises(netrc.NetrcParseError, netrc.netrc)[](#l3.45)
+ def test_main(): support.run_unittest(NetrcTestCase)
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -10,6 +10,12 @@ What's New in Python 3.2.6? Library ------- +- Issue #14984: On POSIX systems, when netrc is called without a filename