mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-135815: skip netrc
security checks if os.getuid
is missing (#135816)
This commit is contained in:
parent
e7295a89b8
commit
b57b619e34
4 changed files with 30 additions and 18 deletions
|
@ -24,12 +24,14 @@ the Unix :program:`ftp` program and other FTP clients.
|
||||||
a :exc:`FileNotFoundError` exception will be raised.
|
a :exc:`FileNotFoundError` exception will be raised.
|
||||||
Parse errors will raise :exc:`NetrcParseError` with diagnostic
|
Parse errors will raise :exc:`NetrcParseError` with diagnostic
|
||||||
information including the file name, line number, and terminating token.
|
information including the file name, line number, and terminating token.
|
||||||
|
|
||||||
If no argument is specified on a POSIX system, the presence of passwords in
|
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
|
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
|
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).
|
running the process, or accessible for read or write by any other user).
|
||||||
This implements security behavior equivalent to that of ftp and other
|
This implements security behavior equivalent to that of ftp and other
|
||||||
programs that use :file:`.netrc`.
|
programs that use :file:`.netrc`. Such security checks are not available
|
||||||
|
on platforms that do not support :func:`os.getuid`.
|
||||||
|
|
||||||
.. versionchanged:: 3.4 Added the POSIX permission check.
|
.. versionchanged:: 3.4 Added the POSIX permission check.
|
||||||
|
|
||||||
|
|
29
Lib/netrc.py
29
Lib/netrc.py
|
@ -7,6 +7,19 @@ import os, stat
|
||||||
__all__ = ["netrc", "NetrcParseError"]
|
__all__ = ["netrc", "NetrcParseError"]
|
||||||
|
|
||||||
|
|
||||||
|
def _can_security_check():
|
||||||
|
# On WASI, getuid() is indicated as a stub but it may also be missing.
|
||||||
|
return os.name == 'posix' and hasattr(os, 'getuid')
|
||||||
|
|
||||||
|
|
||||||
|
def _getpwuid(uid):
|
||||||
|
try:
|
||||||
|
import pwd
|
||||||
|
return pwd.getpwuid(uid)[0]
|
||||||
|
except (ImportError, LookupError):
|
||||||
|
return f'uid {uid}'
|
||||||
|
|
||||||
|
|
||||||
class NetrcParseError(Exception):
|
class NetrcParseError(Exception):
|
||||||
"""Exception raised on syntax errors in the .netrc file."""
|
"""Exception raised on syntax errors in the .netrc file."""
|
||||||
def __init__(self, msg, filename=None, lineno=None):
|
def __init__(self, msg, filename=None, lineno=None):
|
||||||
|
@ -142,18 +155,12 @@ class netrc:
|
||||||
self._security_check(fp, default_netrc, self.hosts[entryname][0])
|
self._security_check(fp, default_netrc, self.hosts[entryname][0])
|
||||||
|
|
||||||
def _security_check(self, fp, default_netrc, login):
|
def _security_check(self, fp, default_netrc, login):
|
||||||
if os.name == 'posix' and default_netrc and login != "anonymous":
|
if _can_security_check() and default_netrc and login != "anonymous":
|
||||||
prop = os.fstat(fp.fileno())
|
prop = os.fstat(fp.fileno())
|
||||||
if prop.st_uid != os.getuid():
|
current_user_id = os.getuid()
|
||||||
import pwd
|
if prop.st_uid != current_user_id:
|
||||||
try:
|
fowner = _getpwuid(prop.st_uid)
|
||||||
fowner = pwd.getpwuid(prop.st_uid)[0]
|
user = _getpwuid(current_user_id)
|
||||||
except KeyError:
|
|
||||||
fowner = 'uid %s' % prop.st_uid
|
|
||||||
try:
|
|
||||||
user = pwd.getpwuid(os.getuid())[0]
|
|
||||||
except KeyError:
|
|
||||||
user = 'uid %s' % os.getuid()
|
|
||||||
raise NetrcParseError(
|
raise NetrcParseError(
|
||||||
(f"~/.netrc file owner ({fowner}, {user}) does not match"
|
(f"~/.netrc file owner ({fowner}, {user}) does not match"
|
||||||
" current user"))
|
" current user"))
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
import netrc, os, unittest, sys, textwrap
|
import netrc, os, unittest, sys, textwrap
|
||||||
|
from test import support
|
||||||
from test.support import os_helper
|
from test.support import os_helper
|
||||||
|
|
||||||
try:
|
|
||||||
import pwd
|
|
||||||
except ImportError:
|
|
||||||
pwd = None
|
|
||||||
|
|
||||||
temp_filename = os_helper.TESTFN
|
temp_filename = os_helper.TESTFN
|
||||||
|
|
||||||
class NetrcTestCase(unittest.TestCase):
|
class NetrcTestCase(unittest.TestCase):
|
||||||
|
@ -269,9 +265,14 @@ class NetrcTestCase(unittest.TestCase):
|
||||||
machine bar.domain.com login foo password pass
|
machine bar.domain.com login foo password pass
|
||||||
""", '#pass')
|
""", '#pass')
|
||||||
|
|
||||||
|
@unittest.skipUnless(support.is_wasi, 'WASI only test')
|
||||||
|
def test_security_on_WASI(self):
|
||||||
|
self.assertFalse(netrc._can_security_check())
|
||||||
|
self.assertEqual(netrc._getpwuid(0), 'uid 0')
|
||||||
|
self.assertEqual(netrc._getpwuid(123456), 'uid 123456')
|
||||||
|
|
||||||
@unittest.skipUnless(os.name == 'posix', 'POSIX only test')
|
@unittest.skipUnless(os.name == 'posix', 'POSIX only test')
|
||||||
@unittest.skipIf(pwd is None, 'security check requires pwd module')
|
@unittest.skipUnless(hasattr(os, 'getuid'), "os.getuid is required")
|
||||||
@os_helper.skip_unless_working_chmod
|
@os_helper.skip_unless_working_chmod
|
||||||
def test_security(self):
|
def test_security(self):
|
||||||
# This test is incomplete since we are normally not run as root and
|
# This test is incomplete since we are normally not run as root and
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:mod:`netrc`: skip security checks if :func:`os.getuid` is missing.
|
||||||
|
Patch by Bénédikt Tran.
|
Loading…
Add table
Add a link
Reference in a new issue