integ/security/keyrings.alt/debian/patches/use_new_lock.patch
Yue Tao de2af4d74d Add debian package for python-keyring
The upstream commit 1e422ed of python-keyring moved non-preferred
keyring backends to separated package "keyrings.alt", so adding the
keyrings.alt and porting the patches related to non-preferred
keyring backends to keyrings.alt.

Patches are not up to our standard. Bringing them up to standard
is future work, tracked by:

https://bugs.launchpad.net/starlingx/+bug/1950506.

Related-Bug: 1950506

Building successfully.

Installing python3-keyrings.alt, python3-keyring and the dependence
package tsconfig successfully.

Booting up ISO successfully on qemu.

Story: 2009221
Task: 43438

Signed-off-by: Yue Tao <yue.tao@windriver.com>
Change-Id: I4b70927709f0cc968e32af1d0e2a9402f47b2fe9
2021-11-22 13:11:46 +08:00

246 lines
9.5 KiB
Diff

The upstream commit 1e422ed of keyring moves non-preferred keyring
backends to keyrings.alt package, so moving the codes related to keyring
backends of use_new_lock.patch to package keyrings.alt
--- a/keyrings/alt/file_base.py
+++ b/keyrings/alt/file_base.py
@@ -3,6 +3,8 @@ from __future__ import with_statement
import os
import abc
import time
+import logging
+import shutil
import configparser
from base64 import encodebytes, decodebytes
@@ -10,6 +12,7 @@ from keyring.errors import PasswordDelet
from keyring.backend import KeyringBackend
from keyring.util import platform_, properties
from .escape import escape as escape_for_ini
+from oslo_concurrency import lockutils
class FileBacked:
@@ -27,6 +30,14 @@ class FileBacked:
"""
return os.path.join(platform_.data_root(), self.filename)
+ @properties.NonDataProperty
+ def backup_file_path(self):
+ """
+ The path to the file where passwords are stored. This property
+ may be overridden by the subclass or at the instance level.
+ """
+ return os.path.join(platform_.data_root(), self.backup_filename)
+
@abc.abstractproperty
def scheme(self):
"""
@@ -112,6 +123,16 @@ class Keyring(FileBacked, KeyringBackend
password = None
return password
+
+ def filecopy(self,src,dest):
+ """copy file src to dest with default buffer size
+ """
+ with open(src, 'r') as f1:
+ with open(dest, 'w') as f2:
+ shutil.copyfileobj(f1,f2)
+ f2.flush()
+
+
def set_password(self, service, username, password):
"""Write the password in the file."""
if not username:
@@ -132,24 +153,36 @@ class Keyring(FileBacked, KeyringBackend
return (escape_for_ini(service) + r'\0' + escape_for_ini(username)).encode()
def _write_config_value(self, service, key, value):
- # ensure the file exists
- self._ensure_file_path()
- # obtain lock for the keyring file
- lock = ''
- i = 60
- while i:
- if not os.path.isfile('/tmp/.keyringlock'):
- lock = open('/tmp/.keyringlock', 'w')
- break
- else:
- time.sleep(0.500)
- i=i-1
+ with lockutils.lock("keyringlock",external=True,lock_path="/tmp"):
+
+ # ensure the file exists
+ self._ensure_file_path()
+
+ config = None
+ try:
+ # Load the keyring from the disk
+ config = configparser.RawConfigParser()
+ config.read(self.file_path)
+ except configparser.ParsingError as e:
+ logging.warning("set_password: keyring file corrupted, Reverting to Backup")
+ # Revert to the backup file (copy backup over current file)
+ try:
+ src = self.backup_file_path
+ dest = self.file_path
+ self.filecopy(src,dest)
+ except shutil.Error as e:
+ logging.warning("set_password: Revert from Backup failed. Error: %s" % e)
+ raise
+ # Load the keyring from the disk, if this fails exception is raised
+ try:
+ config = configparser.RawConfigParser()
+ config.read(self.file_path)
+ except:
+ e = sys.exc_info()[0]
+ logging.warning("set_password: Both keyring files are non useable. Error: %s" % e)
+ raise
- if i:
- # Load the keyring from the disk
- config = configparser.RawConfigParser()
- config.read(self.file_path)
service = escape_for_ini(service)
key = escape_for_ini(key)
@@ -159,12 +192,20 @@ class Keyring(FileBacked, KeyringBackend
config.add_section(service)
config.set(service, key, value)
+ # Make a back up of the keyring file here
+ try:
+ src = self.file_path
+ dest = self.backup_file_path
+ self.filecopy(src,dest)
+ except shutil.Error as e:
+ logging.warning("set_password: Backup failed. Error: %s" % e)
+
# Save the keyring back to the file
with open(self.file_path, 'w') as config_file:
config.write(config_file)
- lock.close()
- os.remove('/tmp/.keyringlock')
+
+
def _ensure_file_path(self):
@@ -187,14 +228,15 @@ class Keyring(FileBacked, KeyringBackend
"""Delete the password for the username of the service."""
service = escape_for_ini(service)
username = escape_for_ini(username)
- config = configparser.RawConfigParser()
- if os.path.exists(self.file_path):
- config.read(self.file_path)
- try:
- if not config.remove_option(service, username):
+ with lockutils.lock("keyringlock",external=True,lock_path="/tmp"):
+ config = configparser.RawConfigParser()
+ if os.path.exists(self.file_path):
+ config.read(self.file_path)
+ try:
+ if not config.remove_option(service, username):
+ raise PasswordDeleteError("Password not found")
+ except configparser.NoSectionError:
raise PasswordDeleteError("Password not found")
- except configparser.NoSectionError:
- raise PasswordDeleteError("Password not found")
- # update the file
- with open(self.file_path, 'w') as config_file:
- config.write(config_file)
+ # update the file
+ with open(self.file_path, 'w') as config_file:
+ config.write(config_file)
Index: keyring-5.3/keyrings/alt/file.py
===================================================================
--- keyring-5.3.orig/keyrings/alt/file.py
+++ keyring-5.3/keyrings/alt/file.py
@@ -19,6 +19,7 @@ class PlaintextKeyring(Keyring):
"Applicable for all platforms, but not recommended"
filename = 'keyring_pass.cfg'
+ backup_filename = 'crypted_pass_backup.cfg'
scheme = 'no encyption'
version = '1.0'
@@ -73,6 +74,7 @@ class EncryptedKeyring(Encrypted, Keyrin
"""PyCryptodome File Keyring"""
filename = 'crypted_pass.cfg'
+ backup_filename = 'crypted_pass_backup.cfg'
pw_prefix = 'pw:'.encode()
@properties.ClassProperty
@@ -105,6 +107,19 @@ class EncryptedKeyring(Encrypted, Keyrin
self.keyring_key = self._get_new_password()
# set a reference password, used to check that the password provided
# matches for subsequent checks.
+
+ # try to pre-create the /tmp/keyringlock if it doesn't exist
+ lockfile = "/tmp/keyringlock"
+ if os.geteuid() == 0 and (not os.path.exists(lockfile)):
+ from pwd import getpwnam
+ import stat
+ nonrootuser = "sysadmin"
+ with open(lockfile, 'w'):
+ pass
+ # must have the lock file with the correct group permissisions g+rw
+ os.chmod(lockfile, stat.S_IRWXG | stat.S_IRWXU)
+
+
self.set_password(
'keyring-setting', 'password reference', 'password reference value'
)
@@ -118,14 +133,39 @@ class EncryptedKeyring(Encrypted, Keyrin
if not os.path.exists(self.file_path):
return False
self._migrate()
- config = configparser.RawConfigParser()
- config.read(self.file_path)
- try:
- config.get(
- escape_for_ini('keyring-setting'), escape_for_ini('password reference')
- )
- except (configparser.NoSectionError, configparser.NoOptionError):
- return False
+
+ # lock access to the file_path here, make sure it's not being written
+ # to while while we're checking for keyring-setting
+ with lockutils.lock("keyringlock",external=True,lock_path="/tmp"):
+ config = configparser.RawConfigParser()
+ config.read(self.file_path)
+ try:
+ config.get(
+ escape_for_ini('keyring-setting'), escape_for_ini('password reference'),
+ )
+ except (configparser.NoSectionError, configparser.NoOptionError):
+ # The current file doesn't have the keyring-setting, check the backup
+ logging.warning("_check_file: The current file doesn't have the keyring-setting, check the backup")
+ if os.path.exists(self.backup_file_path):
+ config = configparser.RawConfigParser()
+ config.read(self.backup_file_path)
+ try:
+ config.get(
+ escape_for_ini('keyring-setting'), escape_for_ini('password reference')
+ )
+ except (configparser.NoSectionError, configparser.NoOptionError):
+ return False
+ # backup file has it, let's use it
+ try:
+ src = self.backup_file_path
+ dest = self.file_path
+ shutil.copy(src,dest)
+ except shutil.Error as e:
+ logging.warning("Revert from Backup failed. Error: %s" % e)
+ return False
+ else:
+ return False
+
try:
self._check_scheme(config)
except AttributeError: