mirror of
https://github.com/certbot/certbot.git
synced 2026-07-27 16:30:31 +02:00
This PR implements the filesystem.copy_ownership_and_apply_mode method from #6497. This method is used in two places in Certbot, replacing os.chown, to copy the owner and group owner from a file to another one, and apply to the latter the given POSIX mode. * Implement copy_ownership_and_apply_mode * Update certbot/compat/os.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Remove default values * Rewrite a comment. * Relaunch CI * Pass as keyword arguments * 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> * Update certbot/compat/filesystem.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Make the private key permissions transfer platform specific * Update certbot/compat/filesystem.py Co-Authored-By: Brad Warren <bmw@users.noreply.github.com> * Rename variable * Fix comment0 * Add unit test for copy_ownership_and_apply_mode * Adapt coverage * Execute unconditionally chmod with copy_ownership_and_apply_mode. Improve doc.
146 lines
4.3 KiB
Python
146 lines
4.3 KiB
Python
"""
|
|
This compat module handles various platform specific calls that do not fall into one
|
|
particular category.
|
|
"""
|
|
from __future__ import absolute_import
|
|
|
|
import select
|
|
import stat
|
|
import sys
|
|
|
|
try:
|
|
from win32com.shell import shell as shellwin32 # pylint: disable=import-error
|
|
POSIX_MODE = False
|
|
except ImportError: # pragma: no cover
|
|
POSIX_MODE = True
|
|
|
|
from certbot import errors
|
|
from certbot.compat import os
|
|
|
|
|
|
# MASK_FOR_PRIVATE_KEY_PERMISSIONS defines what are the permissions flags to keep
|
|
# when transferring the permissions from an old private key to a new one.
|
|
if POSIX_MODE:
|
|
# On Linux, we keep read/write/execute permissions
|
|
# for group and read permissions for everybody.
|
|
MASK_FOR_PRIVATE_KEY_PERMISSIONS = stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH
|
|
else:
|
|
# On Windows, the mode returned by os.stat is not reliable,
|
|
# so we do not keep any permission from the previous private key.
|
|
MASK_FOR_PRIVATE_KEY_PERMISSIONS = 0
|
|
|
|
|
|
def raise_for_non_administrative_windows_rights():
|
|
# type: () -> None
|
|
"""
|
|
On Windows, raise if current shell does not have the administrative rights.
|
|
Do nothing on Linux.
|
|
|
|
:raises .errors.Error: If the current shell does not have administrative rights on Windows.
|
|
"""
|
|
if not POSIX_MODE and shellwin32.IsUserAnAdmin() == 0: # pragma: no cover
|
|
raise errors.Error('Error, certbot must be run on a shell with administrative rights.')
|
|
|
|
|
|
def os_geteuid():
|
|
"""
|
|
Get current user uid
|
|
|
|
:returns: The current user uid.
|
|
:rtype: int
|
|
|
|
"""
|
|
try:
|
|
# Linux specific
|
|
return os.geteuid()
|
|
except AttributeError:
|
|
# Windows specific
|
|
return 0
|
|
|
|
|
|
def readline_with_timeout(timeout, prompt):
|
|
# type: (float, str) -> str
|
|
"""
|
|
Read user input to return the first line entered, or raise after specified timeout.
|
|
|
|
:param float timeout: The timeout in seconds given to the user.
|
|
:param str prompt: The prompt message to display to the user.
|
|
|
|
:returns: The first line entered by the user.
|
|
:rtype: str
|
|
|
|
"""
|
|
try:
|
|
# Linux specific
|
|
#
|
|
# Call to select can only be done like this on UNIX
|
|
rlist, _, _ = select.select([sys.stdin], [], [], timeout)
|
|
if not rlist:
|
|
raise errors.Error(
|
|
"Timed out waiting for answer to prompt '{0}'".format(prompt))
|
|
return rlist[0].readline()
|
|
except OSError:
|
|
# Windows specific
|
|
#
|
|
# No way with select to make a timeout to the user input on Windows,
|
|
# as select only supports socket in this case.
|
|
# So no timeout on Windows for now.
|
|
return sys.stdin.readline()
|
|
|
|
|
|
def compare_file_modes(mode1, mode2):
|
|
"""Return true if the two modes can be considered as equals for this platform"""
|
|
if os.name != 'nt':
|
|
# Linux specific: standard compare
|
|
return oct(stat.S_IMODE(mode1)) == oct(stat.S_IMODE(mode2))
|
|
# Windows specific: most of mode bits are ignored on Windows. Only check user R/W rights.
|
|
return (stat.S_IMODE(mode1) & stat.S_IREAD == stat.S_IMODE(mode2) & stat.S_IREAD
|
|
and stat.S_IMODE(mode1) & stat.S_IWRITE == stat.S_IMODE(mode2) & stat.S_IWRITE)
|
|
|
|
|
|
WINDOWS_DEFAULT_FOLDERS = {
|
|
'config': 'C:\\Certbot',
|
|
'work': 'C:\\Certbot\\lib',
|
|
'logs': 'C:\\Certbot\\log',
|
|
}
|
|
LINUX_DEFAULT_FOLDERS = {
|
|
'config': '/etc/letsencrypt',
|
|
'work': '/var/lib/letsencrypt',
|
|
'logs': '/var/log/letsencrypt',
|
|
}
|
|
|
|
|
|
def get_default_folder(folder_type):
|
|
# type: (str) -> str
|
|
"""
|
|
Return the relevant default folder for the current OS
|
|
|
|
:param str folder_type: The type of folder to retrieve (config, work or logs)
|
|
|
|
:returns: The relevant default folder.
|
|
:rtype: str
|
|
|
|
"""
|
|
if os.name != 'nt':
|
|
# Linux specific
|
|
return LINUX_DEFAULT_FOLDERS[folder_type]
|
|
# Windows specific
|
|
return WINDOWS_DEFAULT_FOLDERS[folder_type]
|
|
|
|
|
|
def underscores_for_unsupported_characters_in_path(path):
|
|
# type: (str) -> str
|
|
"""
|
|
Replace unsupported characters in path for current OS by underscores.
|
|
:param str path: the path to normalize
|
|
:return: the normalized path
|
|
:rtype: str
|
|
"""
|
|
if os.name != 'nt':
|
|
# Linux specific
|
|
return path
|
|
|
|
# Windows specific
|
|
drive, tail = os.path.splitdrive(path)
|
|
return drive + tail.replace(':', '_')
|