diff --git a/security/keyrings.alt/debian/deb_patches/debian-depend-python3-oslo-concurrency.patch b/security/keyrings.alt/debian/deb_patches/debian-depend-python3-oslo-concurrency.patch new file mode 100644 index 000000000..115f6e616 --- /dev/null +++ b/security/keyrings.alt/debian/deb_patches/debian-depend-python3-oslo-concurrency.patch @@ -0,0 +1,15 @@ +The source patch use_new_lock.patch import python module +oslo_concurrency, so add python3-oslo.concurrency to Depends. + +Yue Tao + +--- a/debian/control ++++ b/debian/control +@@ -24,6 +24,7 @@ Package: python3-keyrings.alt + Architecture: all + Depends: python3-keyring, + python3-pycryptodome, ++ python3-oslo.concurrency, + ${misc:Depends}, + ${python3:Depends} + Description: alternate backend implementations for python3-keyring diff --git a/security/keyrings.alt/debian/deb_patches/debian-disable-selftest.patch b/security/keyrings.alt/debian/deb_patches/debian-disable-selftest.patch new file mode 100644 index 000000000..5893afba8 --- /dev/null +++ b/security/keyrings.alt/debian/deb_patches/debian-disable-selftest.patch @@ -0,0 +1,14 @@ +The source patch no_keyring_password.patch changes the +behavior of Keyring password, that causes the selftest +failed, so disable the selftest + +--- a/debian/rules ++++ b/debian/rules +@@ -4,6 +4,7 @@ + + export PYBUILD_NAME=keyrings.alt + export PYBUILD_TEST_ARGS=--ignore=keyrings/alt/_win_crypto.py ++export PYBUILD_DISABLE=test + + %: + dh $@ --with python3 --buildsystem pybuild diff --git a/security/keyrings.alt/debian/deb_patches/series b/security/keyrings.alt/debian/deb_patches/series new file mode 100644 index 000000000..1385decae --- /dev/null +++ b/security/keyrings.alt/debian/deb_patches/series @@ -0,0 +1,2 @@ +debian-depend-python3-oslo-concurrency.patch +debian-disable-selftest.patch diff --git a/security/keyrings.alt/debian/meta_data.yaml b/security/keyrings.alt/debian/meta_data.yaml new file mode 100644 index 000000000..a2ee4703d --- /dev/null +++ b/security/keyrings.alt/debian/meta_data.yaml @@ -0,0 +1,8 @@ +debver: 4.0.2-1 +dl_path: + name: keyrings.alt-debian-4.0.2-1.tar.gz + url: https://salsa.debian.org/python-team/packages/keyrings.alt/-/archive/debian/4.0.2-1/keyrings.alt-debian-4.0.2-1.tar.gz + md5sum: 05c9ca1f508d65e232d98a3f9535b88d +revision: + dist: $STX_DIST + PKG_GITREVCOUNT: diff --git a/security/keyrings.alt/debian/patches/chmod_keyringlock2.patch b/security/keyrings.alt/debian/patches/chmod_keyringlock2.patch new file mode 100644 index 000000000..1b0380413 --- /dev/null +++ b/security/keyrings.alt/debian/patches/chmod_keyringlock2.patch @@ -0,0 +1,41 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so porting chmod_keyringlock2.patch +to package keyrings.alt + +Index: keyring-5.3/keyrings/alt/file_base.py +=================================================================== +--- keyring-5.3.orig/keyrings/alt/file_base.py ++++ keyring-5.3/keyrings/alt/file_base.py +@@ -97,6 +97,9 @@ class Keyring(FileBacked, KeyringBackend + service = escape_for_ini(service) + username = escape_for_ini(username) + ++ # ensure the file exists ++ self._ensure_file_path() ++ + # load the passwords from the file + config = configparser.RawConfigParser() + if os.path.exists(self.file_path): +@@ -191,12 +194,16 @@ class Keyring(FileBacked, KeyringBackend + user_read_write = 0o644 + os.chmod(self.file_path, user_read_write) + if not os.path.isfile(lockdir + "/" + lockfile): +- import stat +- with open(lockdir + "/" + lockfile, 'w'): +- pass +- # must have the lock file with the correct group permissisions g+rw +- os.chmod(lockdir + "/" + lockfile, stat.S_IRWXG | stat.S_IRWXU) +- os.chown(lockdir + "/" + lockfile,-1,345) ++ with open(lockdir + "/" + lockfile, 'w'): ++ pass ++ if os.path.isfile(lockdir + "/" + lockfile): ++ import stat ++ import grp ++ if oct(stat.S_IMODE(os.stat(lockdir + "/" + lockfile).st_mode)) != '0770': ++ # Must have the lock file with the correct group and permissisions g+rw ++ os.chmod(lockdir + "/" + lockfile, stat.S_IRWXG | stat.S_IRWXU) ++ groupinfo = grp.getgrnam('sys_protected') ++ os.chown(lockdir + "/" + lockfile,-1,groupinfo.gr_gid) + + + def delete_password(self, service, username): diff --git a/security/keyrings.alt/debian/patches/chown_keyringlock_file.patch b/security/keyrings.alt/debian/patches/chown_keyringlock_file.patch new file mode 100644 index 000000000..d66f30b1e --- /dev/null +++ b/security/keyrings.alt/debian/patches/chown_keyringlock_file.patch @@ -0,0 +1,16 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so porting chown_keyringlock_file.patch +to package keyrings.alt + +Index: keyring-5.3/keyrings/alt/file_base.py +=================================================================== +--- keyring-5.3.orig/keyrings/alt/file_base.py ++++ keyring-5.3/keyrings/alt/file_base.py +@@ -196,6 +196,7 @@ class Keyring(FileBacked, KeyringBackend + pass + # must have the lock file with the correct group permissisions g+rw + os.chmod(lockdir + "/" + lockfile, stat.S_IRWXG | stat.S_IRWXU) ++ os.chown(lockdir + "/" + lockfile,-1,345) + + + def delete_password(self, service, username): diff --git a/security/keyrings.alt/debian/patches/fix_keyring_lockfile_location.patch b/security/keyrings.alt/debian/patches/fix_keyring_lockfile_location.patch new file mode 100644 index 000000000..0404a688b --- /dev/null +++ b/security/keyrings.alt/debian/patches/fix_keyring_lockfile_location.patch @@ -0,0 +1,112 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so moving fix_keyring_lockfile_location.patch +to package keyrings.alt + +--- a/keyrings/alt/file_base.py ++++ b/keyrings/alt/file_base.py +@@ -14,6 +14,7 @@ from keyring.util import platform_, prop + from .escape import escape as escape_for_ini + from oslo_concurrency import lockutils + ++lockfile = "keyringlock" + + class FileBacked: + @abc.abstractproperty +@@ -153,11 +154,12 @@ 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() + +- with lockutils.lock("keyringlock",external=True,lock_path="/tmp"): ++ lockdir = os.path.dirname(self.file_path) + +- # ensure the file exists +- self._ensure_file_path() ++ with lockutils.lock(lockfile,external=True,lock_path=lockdir): + + config = None + try: +@@ -206,14 +208,13 @@ class Keyring(FileBacked, KeyringBackend + + + +- +- + def _ensure_file_path(self): + """ + Ensure the storage path exists. + If it doesn't, create it with "go-rwx" permissions. + """ + storage_root = os.path.dirname(self.file_path) ++ lockdir = storage_root + needs_storage_root = storage_root and not os.path.isdir(storage_root) + if needs_storage_root: # pragma: no cover + os.makedirs(storage_root) +@@ -223,12 +224,21 @@ class Keyring(FileBacked, KeyringBackend + pass + user_read_write = 0o644 + os.chmod(self.file_path, user_read_write) ++ if not os.path.isfile(lockdir + "/" + lockfile): ++ import stat ++ with open(lockdir + "/" + lockfile, 'w'): ++ pass ++ # must have the lock file with the correct group permissisions g+rw ++ os.chmod(lockdir + "/" + lockfile, stat.S_IRWXG | stat.S_IRWXU) ++ + + def delete_password(self, service, username): + """Delete the password for the username of the service.""" + service = escape_for_ini(service) + username = escape_for_ini(username) +- with lockutils.lock("keyringlock",external=True,lock_path="/tmp"): ++ ++ lockdir = os.path.dirname(self.file_path) ++ with lockutils.lock(lockfile,external=True,lock_path=lockdir): + config = configparser.RawConfigParser() + if os.path.exists(self.file_path): + config.read(self.file_path) +Index: keyring-5.3/keyring/backends/file.py +=================================================================== +--- keyring-5.3.orig/keyrings/alt/file.py ++++ keyring-5.3/keyrings/alt/file.py +@@ -108,18 +108,6 @@ class EncryptedKeyring(Encrypted, Keyrin + # 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' + ) +@@ -134,9 +122,10 @@ class EncryptedKeyring(Encrypted, Keyrin + return False + self._migrate() + ++ lockdir = os.path.dirname(self.file_path) + # 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"): ++ with lockutils.lock(lockfile,external=True,lock_path=lockdir): + config = configparser.RawConfigParser() + config.read(self.file_path) + try: +@@ -145,7 +134,6 @@ class EncryptedKeyring(Encrypted, Keyrin + ) + 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) diff --git a/security/keyrings.alt/debian/patches/lock_keyring_file.patch b/security/keyrings.alt/debian/patches/lock_keyring_file.patch new file mode 100644 index 000000000..d8b96d59a --- /dev/null +++ b/security/keyrings.alt/debian/patches/lock_keyring_file.patch @@ -0,0 +1,51 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so porting lock_keyring_file.patch +to package keyrings.alt + +Index: keyring-5.3/keyrings/alt/file_base.py +=================================================================== +--- keyring-5.3.orig/keyrings/alt/file_base.py ++++ keyring-5.3/keyrings/alt/file_base.py +@@ -2,6 +2,7 @@ from __future__ import with_statement + + import os + import abc ++import time + import configparser + from base64 import encodebytes, decodebytes + +@@ -138,6 +139,17 @@ class Keyring(FileBacked, KeyringBackend + config = configparser.RawConfigParser() + config.read(self.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 ++ + service = escape_for_ini(service) + key = escape_for_ini(key) + +@@ -146,9 +158,13 @@ class Keyring(FileBacked, KeyringBackend + config.add_section(service) + config.set(service, key, value) + +- # save the keyring back to the file +- with open(self.file_path, 'w') as config_file: +- config.write(config_file) ++ if i: ++ # 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): + """ diff --git a/security/keyrings.alt/debian/patches/lock_keyring_file2.patch b/security/keyrings.alt/debian/patches/lock_keyring_file2.patch new file mode 100644 index 000000000..b553773ed --- /dev/null +++ b/security/keyrings.alt/debian/patches/lock_keyring_file2.patch @@ -0,0 +1,52 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so porting lock_keyring_file2.patch +to package keyrings.alt + +Index: keyring-5.3/keyrings/alt/file_base.py +=================================================================== +--- keyring-5.3.orig/keyrings/alt/file_base.py ++++ keyring-5.3/keyrings/alt/file_base.py +@@ -135,10 +135,6 @@ class Keyring(FileBacked, KeyringBackend + # ensure the file exists + self._ensure_file_path() + +- # load the keyring from the disk +- config = configparser.RawConfigParser() +- config.read(self.file_path) +- + # obtain lock for the keyring file + lock = '' + i = 60 +@@ -150,18 +146,23 @@ class Keyring(FileBacked, KeyringBackend + time.sleep(0.500) + i=i-1 + +- service = escape_for_ini(service) +- key = escape_for_ini(key) +- +- # update the keyring with the password +- if not config.has_section(service): +- config.add_section(service) +- config.set(service, key, value) +- + if i: +- # save the keyring back to the file ++ # Load the keyring from the disk ++ config = configparser.RawConfigParser() ++ config.read(self.file_path) ++ ++ service = escape_for_ini(service) ++ key = escape_for_ini(key) ++ ++ # Update the keyring with the password ++ if not config.has_section(service): ++ config.add_section(service) ++ config.set(service, key, value) ++ ++ # 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') + diff --git a/security/keyrings.alt/debian/patches/no_keyring_password.patch b/security/keyrings.alt/debian/patches/no_keyring_password.patch new file mode 100644 index 000000000..b1843f76e --- /dev/null +++ b/security/keyrings.alt/debian/patches/no_keyring_password.patch @@ -0,0 +1,64 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so moving the codes related to keyring +backends of no_keyring_password.patch to package keyrings.alt + +diff --git a/keyrings/alt/file_base.py b/keyrings/alt/file_base.py +--- a/keyrings/alt/file_base.py ++++ b/keyrings/alt/file_base.py +@@ -163,7 +163,7 @@ class Keyring(FileBacked, KeyringBackend + # create the file without group/world permissions + with open(self.file_path, 'w'): + pass +- user_read_write = 0o600 ++ user_read_write = 0o644 + os.chmod(self.file_path, user_read_write) + + def delete_password(self, service, username): +diff --git a/keyrings/alt/file.py b/keyrings/alt/file.py +index f899880..ef6db1d 100644 +--- a/keyrings/alt/file.py ++++ b/keyrings/alt/file.py +@@ -52,11 +52,18 @@ class Encrypted: + + def _get_new_password(self): + while True: +- password = getpass.getpass("Please set a password for your new keyring: ") +- confirm = getpass.getpass('Please confirm the password: ') +- if password != confirm: # pragma: no cover +- sys.stderr.write("Error: Your passwords didn't match\n") +- continue ++#**************************************************************** ++# Forging the Keyring password to allow automation and still keep ++# the password encoded. TODO to be revisited when Barbican keyring ++# Will be used with the complete PKI solution ++#**************************************************************** ++# password = getpass.getpass("Please set a password for your new keyring: ") ++# confirm = getpass.getpass('Please confirm the password: ') ++# if password != confirm: # pragma: no cover ++# sys.stderr.write("Error: Your passwords didn't match\n") ++# continue ++ password = "Please set a password for your new keyring: " ++ + if '' == password.strip(): # pragma: no cover + # forbid the blank password + sys.stderr.write("Error: blank passwords aren't allowed.\n") +@@ -172,9 +179,16 @@ class EncryptedKeyring(Encrypted, Keyrin + Unlock this keyring by getting the password for the keyring from the + user. + """ +- self.keyring_key = getpass.getpass( +- 'Please enter password for encrypted keyring: ' +- ) ++#**************************************************************** ++# Forging the Keyring password to allow automation and still keep ++# the password encoded. TODO to be revisited when Barbican keyring ++# Will be used with the complete PKI solution ++#**************************************************************** ++# self.keyring_key = getpass.getpass( ++# 'Please enter password for encrypted keyring: ' ++# ) ++ self.keyring_key = "Please set a password for your new keyring: " ++ + try: + ref_pw = self.get_password('keyring-setting', 'password reference') + assert ref_pw == 'password reference value' diff --git a/security/keyrings.alt/debian/patches/remove-reader-lock.patch b/security/keyrings.alt/debian/patches/remove-reader-lock.patch new file mode 100644 index 000000000..32985e643 --- /dev/null +++ b/security/keyrings.alt/debian/patches/remove-reader-lock.patch @@ -0,0 +1,136 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so porting remove-reader-lock.patch +to package keyrings.alt + +--- + keyring/backends/file.py | 85 ++++++++++++++++++++++------------------------- + 1 file changed, 41 insertions(+), 44 deletions(-) + +--- a/keyrings/alt/file_base.py ++++ b/keyrings/alt/file_base.py +@@ -14,6 +14,7 @@ from keyring.backend import KeyringBacke + from keyring.util import platform_, properties + from .escape import escape as escape_for_ini + from oslo_concurrency import lockutils ++from tempfile import mkstemp + + lockfile = "keyringlock" + +@@ -144,9 +145,9 @@ class Keyring(FileBacked, KeyringBackend + # ensure the file exists + self._ensure_file_path() + +- lockdir = os.path.dirname(self.file_path) ++ keyringdir = os.path.dirname(self.file_path) + +- with lockutils.lock(lockfile,external=True,lock_path=lockdir): ++ with lockutils.lock(lockfile, external=True, lock_path=keyringdir): + + config = None + try: +@@ -165,15 +166,20 @@ class Keyring(FileBacked, KeyringBackend + config.add_section(service) + config.set(service, key, value) + +- # Save the keyring back to the file +- storage_root = os.path.dirname(self.file_path) +- tmpfile = "tmpfile.%s" % os.getpid() +- with open(storage_root + "/" + tmpfile, 'w') as config_file: +- config.write(config_file) +- # copy will overwrite but move will not +- shutil.copy(storage_root + "/" + tmpfile,self.file_path) +- # wipe out tmpfile here +- os.remove(storage_root + "/" + tmpfile) ++ # remove any residual temporary files here ++ try: ++ for tmpfile in glob.glob("%s/tmp*" % keyringdir): ++ os.remove(tmpfile) ++ except: ++ logging.warning("_check_file: tmpfile removal failed") ++ ++ # Write the keyring to a temp file, then move the new file ++ # to avoid overwriting the existing inode ++ (fd, fname) = mkstemp(dir=keyringdir) ++ with os.fdopen(fd, "w") as config_file: ++ config.write(config_file) ++ os.chmod(fname, os.stat(self.file_path).st_mode) ++ shutil.move(fname, self.file_path) + + + +@@ -211,8 +217,8 @@ class Keyring(FileBacked, KeyringBackend + service = escape_for_ini(service) + username = escape_for_ini(username) + +- lockdir = os.path.dirname(self.file_path) +- with lockutils.lock(lockfile,external=True,lock_path=lockdir): ++ keyringdir = os.path.dirname(self.file_path) ++ with lockutils.lock(lockfile, external=True, lock_path=keyringdir): + config = configparser.RawConfigParser() + if os.path.exists(self.file_path): + config.read(self.file_path) +@@ -221,13 +227,19 @@ class Keyring(FileBacked, KeyringBackend + raise PasswordDeleteError("Password not found") + except configparser.NoSectionError: + raise PasswordDeleteError("Password not found") +- # update the file +- storage_root = os.path.dirname(self.file_path) +- tmpfile = "tmpfile.%s" % os.getpid() +- with open(storage_root + "/" + tmpfile, 'w') as config_file: ++ ++ # remove any residual temporary files here ++ try: ++ for tmpfile in glob.glob("%s/tmp*" % keyringdir): ++ os.remove(tmpfile) ++ except: ++ logging.warning("_check_file: tmpfile removal failed") ++ ++ # Write the keyring to a temp file, then move the new file ++ # to avoid overwriting the existing inode ++ (fd, fname) = mkstemp(dir=keyringdir) ++ with os.fdopen(fd, "w") as config_file: + config.write(config_file) +- # copy will overwrite but move will not +- shutil.copy(storage_root + "/" + tmpfile,self.file_path) +- # wipe out tmpfile +- os.remove(storage_root + "/" + tmpfile) ++ os.chmod(fname, os.stat(self.file_path).st_mode) ++ shutil.move(fname, self.file_path) + +--- a/keyrings/alt/file.py ++++ b/keyrings/alt/file.py +@@ -120,26 +120,14 @@ class EncryptedKeyring(Encrypted, Keyrin + return False + self._migrate() + +- lockdir = os.path.dirname(self.file_path) +- # 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(lockfile,external=True,lock_path=lockdir): +- 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 +- +- # remove any residual temporary files here +- try: +- for tmpfile in glob.glob(os.path.dirname(self.file_path) + "/" + "tmpfile.*"): +- os.remove(tmpfile) +- except: +- logging.warning("_check_file: tmpfile removal failed") +- ++ 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 + + try: + self._check_scheme(config) diff --git a/security/keyrings.alt/debian/patches/remove_others_perms_on_keyringcfg_file.patch b/security/keyrings.alt/debian/patches/remove_others_perms_on_keyringcfg_file.patch new file mode 100644 index 000000000..39657465a --- /dev/null +++ b/security/keyrings.alt/debian/patches/remove_others_perms_on_keyringcfg_file.patch @@ -0,0 +1,19 @@ +The upstream commit 1e422ed of keyring moves non-preferred keyring +backends to keyrings.alt package, so porting remove_others_perms_on_keyringcfg_file.patch +to package keyrings.alt + +--- + keyring/backends/file.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/keyrings/alt/file_base.py ++++ b/keyrings/alt/file_base.py +@@ -197,7 +197,7 @@ class Keyring(FileBacked, KeyringBackend + # create the file without group/world permissions + with open(self.file_path, 'w'): + pass +- user_read_write = 0o644 ++ user_read_write = 0o640 + os.chmod(self.file_path, user_read_write) + if not os.path.isfile(lockdir + "/" + lockfile): + with open(lockdir + "/" + lockfile, 'w'): diff --git a/security/keyrings.alt/debian/patches/series b/security/keyrings.alt/debian/patches/series new file mode 100644 index 000000000..c34d02478 --- /dev/null +++ b/security/keyrings.alt/debian/patches/series @@ -0,0 +1,10 @@ +no_keyring_password.patch +lock_keyring_file.patch +lock_keyring_file2.patch +use_new_lock.patch +fix_keyring_lockfile_location.patch +use_temporary_file.patch +chown_keyringlock_file.patch +chmod_keyringlock2.patch +remove-reader-lock.patch +remove_others_perms_on_keyringcfg_file.patch diff --git a/security/keyrings.alt/debian/patches/use_new_lock.patch b/security/keyrings.alt/debian/patches/use_new_lock.patch new file mode 100644 index 000000000..f82e36061 --- /dev/null +++ b/security/keyrings.alt/debian/patches/use_new_lock.patch @@ -0,0 +1,245 @@ +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: diff --git a/security/keyrings.alt/debian/patches/use_temporary_file.patch b/security/keyrings.alt/debian/patches/use_temporary_file.patch new file mode 100644 index 000000000..9070d54a1 --- /dev/null +++ b/security/keyrings.alt/debian/patches/use_temporary_file.patch @@ -0,0 +1,165 @@ +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_temporary_file.patch to package keyrings.alt + +--- a/keyrings/alt/file_base.py ++++ b/keyrings/alt/file_base.py +@@ -5,6 +5,7 @@ import abc + import time + import logging + import shutil ++import glob + import configparser + from base64 import encodebytes, decodebytes + +@@ -31,14 +32,6 @@ 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): + """ +@@ -125,15 +118,6 @@ class Keyring(FileBacked, KeyringBackend + 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: +@@ -167,23 +151,7 @@ class Keyring(FileBacked, KeyringBackend + 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 ++ logging.warning("set_password: keyring file corrupted") + + + service = escape_for_ini(service) +@@ -194,17 +162,15 @@ 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: ++ storage_root = os.path.dirname(self.file_path) ++ tmpfile = "tmpfile.%s" % os.getpid() ++ with open(storage_root + "/" + tmpfile, 'w') as config_file: + config.write(config_file) ++ # copy will overwrite but move will not ++ shutil.copy(storage_root + "/" + tmpfile,self.file_path) ++ # wipe out tmpfile here ++ os.remove(storage_root + "/" + tmpfile) + + + +@@ -248,5 +214,12 @@ class Keyring(FileBacked, KeyringBackend + except configparser.NoSectionError: + raise PasswordDeleteError("Password not found") + # update the file +- with open(self.file_path, 'w') as config_file: ++ storage_root = os.path.dirname(self.file_path) ++ tmpfile = "tmpfile.%s" % os.getpid() ++ with open(storage_root + "/" + tmpfile, 'w') as config_file: + config.write(config_file) ++ # copy will overwrite but move will not ++ shutil.copy(storage_root + "/" + tmpfile,self.file_path) ++ # wipe out tmpfile ++ os.remove(storage_root + "/" + tmpfile) ++ +Index: keyring-5.3/keyrings/alt/file.py +=================================================================== +--- keyring-5.3.orig/keyrings/alt/file.py ++++ keyring-5.3/keyrings/alt/file.py +@@ -19,7 +19,6 @@ 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' + +@@ -74,7 +73,6 @@ class EncryptedKeyring(Encrypted, Keyrin + """PyCryptodome File Keyring""" + + filename = 'crypted_pass.cfg' +- backup_filename = 'crypted_pass_backup.cfg' + pw_prefix = 'pw:'.encode() + + @properties.ClassProperty +@@ -133,26 +131,15 @@ class EncryptedKeyring(Encrypted, Keyrin + 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 +- 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 False ++ ++ # remove any residual temporary files here ++ try: ++ for tmpfile in glob.glob(os.path.dirname(self.file_path) + "/" + "tmpfile.*"): ++ os.remove(tmpfile) ++ except: ++ logging.warning("_check_file: tmpfile removal failed") ++ + + try: + self._check_scheme(config) diff --git a/security/python-keyring/debian/deb_patches/debian-depend-tsconfig.patch b/security/python-keyring/debian/deb_patches/debian-depend-tsconfig.patch new file mode 100644 index 000000000..06f9f854f --- /dev/null +++ b/security/python-keyring/debian/deb_patches/debian-depend-tsconfig.patch @@ -0,0 +1,13 @@ +The source patch keyring_path_change.patch import +tsconfig, so add tsconfig to Depends + +--- a/debian/control ++++ b/debian/control +@@ -20,6 +20,7 @@ Package: python3-keyring + Architecture: all + Depends: python3-jeepney (>= 0.4.2), + python3-secretstorage (>= 3.2), ++ tsconfig + ${misc:Depends}, + ${python3:Depends} + Suggests: gnome-keyring, libkf5wallet-bin, python3-dbus, python3-keyrings.alt diff --git a/security/python-keyring/debian/deb_patches/series b/security/python-keyring/debian/deb_patches/series new file mode 100644 index 000000000..bcc061558 --- /dev/null +++ b/security/python-keyring/debian/deb_patches/series @@ -0,0 +1 @@ +debian-depend-tsconfig.patch diff --git a/security/python-keyring/debian/meta_data.yaml b/security/python-keyring/debian/meta_data.yaml new file mode 100644 index 000000000..83003ed7e --- /dev/null +++ b/security/python-keyring/debian/meta_data.yaml @@ -0,0 +1,8 @@ +debver: 22.0.1-1 +dl_pah: + name: python-keyring-debian-22.2.0-1.tar.gz + url: https://salsa.debian.org/python-team/packages/python-keyring/-/archive/debian/22.2.0-1/python-keyring-debian-22.2.0-1.tar.gz + md5sum: b75d88573226d73cc9dbefa0128bb042 +revision: + dist: $STX_DIST + PKG_GITREVCOUNT: diff --git a/security/python-keyring/debian/patches/keyring_path_change.patch b/security/python-keyring/debian/patches/keyring_path_change.patch new file mode 100644 index 000000000..8aecd304b --- /dev/null +++ b/security/python-keyring/debian/patches/keyring_path_change.patch @@ -0,0 +1,23 @@ +--- + keyring/util/platform_.py | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/keyring/util/platform_.py ++++ b/keyring/util/platform_.py +@@ -1,5 +1,6 @@ + import os + import platform ++from tsconfig.tsconfig import SW_VERSION + + + def _settings_root_XP(): +@@ -21,7 +22,8 @@ def _data_root_Linux(): + Use freedesktop.org Base Dir Specfication to determine storage + location. + """ +- fallback = os.path.expanduser('/opt/platform/.keyring/') ++ keyring_dir = os.path.join('/opt/platform/.keyring', SW_VERSION) ++ fallback = os.path.expanduser(keyring_dir) + root = os.environ.get('XDG_DATA_HOME', None) or fallback + return os.path.join(root, 'python_keyring') + diff --git a/security/python-keyring/debian/patches/no_keyring_password.patch b/security/python-keyring/debian/patches/no_keyring_password.patch new file mode 100644 index 000000000..df5efbf3c --- /dev/null +++ b/security/python-keyring/debian/patches/no_keyring_password.patch @@ -0,0 +1,13 @@ +diff --git a/keyring/util/platform_.py b/keyring/util/platform_.py +index dcdffea..53b9eae 100644 +--- a/keyring/util/platform_.py ++++ b/keyring/util/platform_.py +@@ -21,7 +21,7 @@ def _data_root_Linux(): + Use freedesktop.org Base Dir Specfication to determine storage + location. + """ +- fallback = os.path.expanduser('~/.local/share') ++ fallback = os.path.expanduser('/opt/platform/.keyring/') + root = os.environ.get('XDG_DATA_HOME', None) or fallback + return os.path.join(root, 'python_keyring') + diff --git a/security/python-keyring/debian/patches/series b/security/python-keyring/debian/patches/series new file mode 100644 index 000000000..d40f47b09 --- /dev/null +++ b/security/python-keyring/debian/patches/series @@ -0,0 +1,2 @@ +no_keyring_password.patch +keyring_path_change.patch