mirror of
https://github.com/certbot/certbot.git
synced 2026-07-28 08:45:22 +02:00
[Windows|Linux] Forbid os.stat and os.fstat (#7325)
Fixes #7212 This PR forbid os.stat and os.fstat, and fix or provide alternatives to avoid its usage in certbot outside of certbot.compat.filesystem. * Reimplement private key mode propagation * Remove other os.stat * Remove last call of os.stat in certbot package * Forbid stat and fstat * Implement mode comparison checks * Add unit tests * Update certbot/compat/filesystem.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Update certbot/compat/filesystem.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Handle case where multiple ace concerns a given SID in has_min_permissions * Add a new test scenario * Add a simple test for has_same_ownership * Fix name function * Add a comment explaining an ACE structure * Move a test in its dedicated class * Improve a message error * Calculate has_min_permission result using effective permission rights to be more generic. * Change an exception message * Add comments, avoid to skip a test. * Update certbot/compat/filesystem.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com>
This commit is contained in:
committed by
Brad Warren
co-authored by
Brad Warren
parent
ada2f5c767
commit
ab76834100
@@ -2,7 +2,6 @@
|
||||
|
||||
import abc
|
||||
import logging
|
||||
import stat
|
||||
from time import sleep
|
||||
|
||||
import configobj
|
||||
@@ -12,6 +11,7 @@ from acme import challenges
|
||||
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot.compat import filesystem
|
||||
from certbot.compat import os
|
||||
from certbot.display import ops
|
||||
from certbot.display import util as display_util
|
||||
@@ -312,8 +312,7 @@ def validate_file_permissions(filename):
|
||||
|
||||
validate_file(filename)
|
||||
|
||||
permissions = stat.S_IMODE(os.stat(filename).st_mode)
|
||||
if permissions & stat.S_IRWXO:
|
||||
if filesystem.has_world_permissions(filename):
|
||||
logger.warning('Unsafe permissions on credentials configuration file: %s', filename)
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,15 @@ import unittest
|
||||
import mock
|
||||
|
||||
from certbot import errors
|
||||
from certbot import util
|
||||
from certbot.compat import os
|
||||
from certbot.display import util as display_util
|
||||
from certbot.plugins import dns_common
|
||||
from certbot.plugins import dns_test_common
|
||||
from certbot.tests import util
|
||||
from certbot.tests import util as test_util
|
||||
|
||||
|
||||
class DNSAuthenticatorTest(util.TempDirTestCase, dns_test_common.BaseAuthenticatorTest):
|
||||
class DNSAuthenticatorTest(test_util.TempDirTestCase, dns_test_common.BaseAuthenticatorTest):
|
||||
# pylint: disable=protected-access
|
||||
|
||||
class _FakeDNSAuthenticator(dns_common.DNSAuthenticator):
|
||||
@@ -50,7 +51,7 @@ class DNSAuthenticatorTest(util.TempDirTestCase, dns_test_common.BaseAuthenticat
|
||||
|
||||
self.auth._cleanup.assert_called_once_with(dns_test_common.DOMAIN, mock.ANY, mock.ANY)
|
||||
|
||||
@util.patch_get_utility()
|
||||
@test_util.patch_get_utility()
|
||||
def test_prompt(self, mock_get_utility):
|
||||
mock_display = mock_get_utility()
|
||||
mock_display.input.side_effect = ((display_util.OK, "",),
|
||||
@@ -59,14 +60,14 @@ class DNSAuthenticatorTest(util.TempDirTestCase, dns_test_common.BaseAuthenticat
|
||||
self.auth._configure("other_key", "")
|
||||
self.assertEqual(self.auth.config.fake_other_key, "value")
|
||||
|
||||
@util.patch_get_utility()
|
||||
@test_util.patch_get_utility()
|
||||
def test_prompt_canceled(self, mock_get_utility):
|
||||
mock_display = mock_get_utility()
|
||||
mock_display.input.side_effect = ((display_util.CANCEL, "c",),)
|
||||
|
||||
self.assertRaises(errors.PluginError, self.auth._configure, "other_key", "")
|
||||
|
||||
@util.patch_get_utility()
|
||||
@test_util.patch_get_utility()
|
||||
def test_prompt_file(self, mock_get_utility):
|
||||
path = os.path.join(self.tempdir, 'file.ini')
|
||||
open(path, "wb").close()
|
||||
@@ -80,7 +81,7 @@ class DNSAuthenticatorTest(util.TempDirTestCase, dns_test_common.BaseAuthenticat
|
||||
self.auth._configure_file("file_path", "")
|
||||
self.assertEqual(self.auth.config.fake_file_path, path)
|
||||
|
||||
@util.patch_get_utility()
|
||||
@test_util.patch_get_utility()
|
||||
def test_prompt_file_canceled(self, mock_get_utility):
|
||||
mock_display = mock_get_utility()
|
||||
mock_display.directory_select.side_effect = ((display_util.CANCEL, "c",),)
|
||||
@@ -96,7 +97,7 @@ class DNSAuthenticatorTest(util.TempDirTestCase, dns_test_common.BaseAuthenticat
|
||||
|
||||
self.assertEqual(credentials.conf("test"), "value")
|
||||
|
||||
@util.patch_get_utility()
|
||||
@test_util.patch_get_utility()
|
||||
def test_prompt_credentials(self, mock_get_utility):
|
||||
bad_path = os.path.join(self.tempdir, 'bad-file.ini')
|
||||
dns_test_common.write({"fake_other": "other_value"}, bad_path)
|
||||
@@ -116,7 +117,7 @@ class DNSAuthenticatorTest(util.TempDirTestCase, dns_test_common.BaseAuthenticat
|
||||
self.assertEqual(credentials.conf("test"), "value")
|
||||
|
||||
|
||||
class CredentialsConfigurationTest(util.TempDirTestCase):
|
||||
class CredentialsConfigurationTest(test_util.TempDirTestCase):
|
||||
class _MockLoggingHandler(logging.Handler):
|
||||
messages = None
|
||||
|
||||
@@ -150,14 +151,14 @@ class CredentialsConfigurationTest(util.TempDirTestCase):
|
||||
dns_common.logger.addHandler(log)
|
||||
|
||||
path = os.path.join(self.tempdir, 'too-permissive-file.ini')
|
||||
open(path, "wb").close()
|
||||
util.safe_open(path, "wb", 0o744).close()
|
||||
|
||||
dns_common.CredentialsConfiguration(path)
|
||||
|
||||
self.assertEqual(1, len([_ for _ in log.messages['warning'] if _.startswith("Unsafe")]))
|
||||
|
||||
|
||||
class CredentialsConfigurationRequireTest(util.TempDirTestCase):
|
||||
class CredentialsConfigurationRequireTest(test_util.TempDirTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(CredentialsConfigurationRequireTest, self).setUp()
|
||||
|
||||
@@ -34,7 +34,13 @@ class AuthenticatorTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
from certbot.plugins.webroot import Authenticator
|
||||
self.path = tempfile.mkdtemp()
|
||||
# On Linux directories created by tempfile.mkdtemp inherit their permissions from their
|
||||
# parent directory. So the actual permissions are inconsistent over various tests env.
|
||||
# To circumvent this, a dedicated sub-workspace is created under the workspace, using
|
||||
# filesystem.mkdir to get consistent permissions.
|
||||
self.workspace = tempfile.mkdtemp()
|
||||
self.path = os.path.join(self.workspace, 'webroot')
|
||||
filesystem.mkdir(self.path)
|
||||
self.partial_root_challenge_path = os.path.join(
|
||||
self.path, ".well-known")
|
||||
self.root_challenge_path = os.path.join(
|
||||
@@ -170,17 +176,12 @@ class AuthenticatorTest(unittest.TestCase):
|
||||
self.assertTrue(filesystem.check_mode(self.validation_path, 0o644))
|
||||
|
||||
# Check permissions of the directories
|
||||
|
||||
for dirpath, dirnames, _ in os.walk(self.path):
|
||||
for directory in dirnames:
|
||||
full_path = os.path.join(dirpath, directory)
|
||||
self.assertTrue(filesystem.check_mode(full_path, 0o755))
|
||||
|
||||
parent_gid = os.stat(self.path).st_gid
|
||||
parent_uid = os.stat(self.path).st_uid
|
||||
|
||||
self.assertEqual(os.stat(self.validation_path).st_gid, parent_gid)
|
||||
self.assertEqual(os.stat(self.validation_path).st_uid, parent_uid)
|
||||
self.assertTrue(filesystem.has_same_ownership(self.validation_path, self.path))
|
||||
|
||||
def test_perform_cleanup(self):
|
||||
self.auth.prepare()
|
||||
|
||||
Reference in New Issue
Block a user