+++ /dev/null
-Index: keyring-5.3/keyring/backends/file.py
-===================================================================
---- keyring-5.3.orig/keyring/backends/file.py
-+++ keyring-5.3/keyring/backends/file.py
-@@ -7,6 +7,8 @@ import sys
- import json
- import abc
- import time
-+import logging
-+import shutil
-
- from ..py27compat import configparser
-
-@@ -14,6 +16,7 @@ from ..errors import PasswordDeleteError
- from ..backend import KeyringBackend
- from ..util import platform_, properties
- from ..util.escape import escape as escape_for_ini
-+from oslo_concurrency import lockutils
-
-
- class FileBacked(object):
-@@ -31,6 +34,13 @@ class FileBacked(object):
- """
- 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)
-
- class BaseKeyring(FileBacked, KeyringBackend):
- """
-@@ -78,6 +88,16 @@ class BaseKeyring(FileBacked, KeyringBac
- 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.
- """
-@@ -89,37 +109,56 @@ class BaseKeyring(FileBacked, KeyringBac
- # encode with base64
- password_base64 = base64.encodestring(password_encrypted).decode()
-
-- # 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)
-
- # Update the keyring with the password
- if not config.has_section(service):
- config.add_section(service)
- config.set(service, username, password_base64)
-
-+ # 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):
-@@ -142,17 +181,18 @@ class BaseKeyring(FileBacked, KeyringBac
- """
- 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)
-
- class PlaintextKeyring(BaseKeyring):
- """Simple File Keyring with no encryption"""
-@@ -161,6 +201,7 @@ class PlaintextKeyring(BaseKeyring):
- "Applicable for all platforms, but not recommended"
-
- filename = 'keyring_pass.cfg'
-+ backup_filename = 'crypted_pass_backup.cfg'
-
- def encrypt(self, password):
- """Directly return the password itself.
-@@ -214,6 +255,7 @@ class EncryptedKeyring(Encrypted, BaseKe
- """PyCrypto File Keyring"""
-
- filename = 'crypted_pass.cfg'
-+ backup_filename = 'crypted_pass_backup.cfg'
- pw_prefix = 'pw:'.encode()
-
- @properties.ClassProperty
-@@ -247,6 +289,19 @@ class EncryptedKeyring(Encrypted, BaseKe
- 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')
-
-@@ -257,15 +312,41 @@ class EncryptedKeyring(Encrypted, BaseKe
- 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
-+
- return True
-
- def _unlock(self):