summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/netrc.rst6
-rw-r--r--Lib/netrc.py23
-rw-r--r--Lib/test/test_netrc.py23
-rw-r--r--Misc/NEWS6
4 files changed, 56 insertions, 2 deletions
diff --git a/Doc/library/netrc.rst b/Doc/library/netrc.rst
index 8a2f1c6..9792799 100644
--- a/Doc/library/netrc.rst
+++ b/Doc/library/netrc.rst
@@ -21,6 +21,12 @@ 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`.
.. exception:: NetrcParseError
diff --git a/Lib/netrc.py b/Lib/netrc.py
index 5493d77..0b4eedf 100644
--- a/Lib/netrc.py
+++ b/Lib/netrc.py
@@ -2,7 +2,7 @@
# Module and documentation by Eric S. Raymond, 21 Dec 1998
-import os, shlex
+import os, stat, shlex, 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")
@@ -77,6 +78,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 b536255..24ad786 100644
--- a/Lib/test/test_netrc.py
+++ b/Lib/test/test_netrc.py
@@ -32,7 +32,7 @@ class NetrcTestCase(unittest.TestCase):
def tearDown (self):
del self.netrc
- os.unlink(temp_filename)
+ test_support.unlink(temp_filename)
def test_case_1(self):
self.assert_(self.netrc.macros == {'macro1':['line1\n', 'line2\n'],
@@ -41,6 +41,27 @@ class NetrcTestCase(unittest.TestCase):
self.assert_(self.netrc.hosts['foo'] == ('log1', 'acct1', 'pass1'))
self.assert_(self.netrc.hosts['default'] == ('log2', None, 'pass2'))
+ if os.name == 'posix':
+ 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.
+ os.unlink(temp_filename)
+ d = test_support.TESTFN
+ try:
+ os.mkdir(d)
+ fn = os.path.join(d, '.netrc')
+ with open(fn, 'wt') as f:
+ f.write(TEST_NETRC)
+ with test_support.EnvironmentVarGuard() as environ:
+ environ.set('HOME', d)
+ os.chmod(fn, 0600)
+ self.netrc = netrc.netrc()
+ self.test_case_1()
+ os.chmod(fn, 0622)
+ self.assertRaises(netrc.NetrcParseError, netrc.netrc)
+ finally:
+ test_support.rmtree(d)
+
def test_main():
test_support.run_unittest(NetrcTestCase)
diff --git a/Misc/NEWS b/Misc/NEWS
index de8c202..833cd05 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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. Patch by Zachary Ware.