Commit 8f3680ae authored by R David Murray's avatar R David Murray

#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.
parent aa5e726a
......@@ -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
......
......@@ -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,
......
......@@ -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)
......
......@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment