CVE-2022-4724 (published 2022-12-27T15:15Z)

Improper Access Control in GitHub repository ikus060/rdiffweb prior to 2.5.5.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-284
cwe_name: Improper Access Control
description: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
extended_description: 
            Access control involves the use of several protection mechanisms such as:
            
               Authentication (proving the identity of an actor)
               Authorization (ensuring that a given actor can access a resource), and
               Accountability (tracking of activities that were performed)
            
            When any mechanism is not applied or otherwise fails, attackers can compromise the security of the product by gaining privileges, reading sensitive information, executing commands, evading detection, etc.
            There are two distinct behaviors that can introduce access control weaknesses:
               
                  Specification: incorrect privileges, permissions, ownership, etc. are explicitly specified for either the user or the resource (for example, setting a password file to be world-writable, or giving administrator capabilities to a guest user). This action could be performed by the program or the administrator.
                  Enforcement: the mechanism contains errors that prevent it from properly enforcing the specified access control requirements (e.g., allowing the user to specify their own privileges, or allowing a syntactically-incorrect ACL to produce insecure settings). This problem occurs within the program itself, in that it does not actually enforce the intended security policy that the administrator specifies.
               
         
		 

Fix information:
repo_url: https://github.com/ikus060/rdiffweb
commit_details: repo_url: https://github.com/ikus060/rdiffweb
author: Patrik Dufresne
author_date: 2022-12-23 13:01:27-05:00
msg: Make sure that all ssh keys are unique, regardless of the user
repository: repo_url: https://github.com/ikus060/rdiffweb
repo_name: ikus060/rdiffweb
description: A simplified backup management software for quick access to your archives through an efficient web interface.
repo_language: Python
owner: ikus060
file_changes: filename: README.md
change_type: ModificationType.MODIFY
diff: @@ -115,6 +115,7 @@ Professional support for Rdiffweb is available by contacting [IKUS Soft](https:/
 * Sent email notification to user when a new SSH Key get added - credit to [Nehal Pillai](https://www.linkedin.com/in/nehal-pillai-02a854172)
 * Ratelimit "Resend code to my email" in Two-Factor Authentication view - credit to [Nehal Pillai](https://www.linkedin.com/in/nehal-pillai-02a854172)
 * Username are not case-insensitive - credits to [raiders0786](https://www.linkedin.com/in/chirag-agrawal-770488144/)
+* Make sure that all ssh keys are unique, regardless of the user - credit to [Nehal Pillai](https://www.linkedin.com/in/nehal-pillai-02a854172)
 
 Breaking changes:
 

programming_language: Markdown
filename: __init__.py
change_type: ModificationType.MODIFY
diff: @@ -19,12 +19,12 @@ import logging
 import sys
 
 import cherrypy
-from sqlalchemy import event
+from sqlalchemy import event, func
 from sqlalchemy.exc import IntegrityError
 
 from ._repo import RepoObject  # noqa
 from ._session import DbSession, SessionObject  # noqa
-from ._sshkey import SshKey  # noqa
+from ._sshkey import SshKey, sshkey_fingerprint_index  # noqa
 from ._token import Token  # noqa
 from ._user import DuplicateSSHKeyError, UserObject, user_username_index  # noqa
 
@@ -137,3 +137,22 @@ def db_after_create(target, connection, **kw):
             logger.error(msg)
             print(msg, file=sys.stderr)
             raise SystemExit(12)
+
+    # Fix SSH Key uniqueness - since 2.5.4
+    if not _index_exists(connection, 'sshkey_fingerprint_index'):
+        duplicate_sshkeys = (
+            SshKey.query.with_entities(SshKey.fingerprint)
+            .group_by(SshKey.fingerprint)
+            .having(func.count(SshKey.fingerprint) > 1)
+        ).all()
+        try:
+            sshkey_fingerprint_index.create()
+        except IntegrityError:
+            msg = (
+                'Failure to upgrade your database to make SSH Keys unique. '
+                'You must downgrade and deleted duplicate SSH Keys. '
+                '%s' % '\n'.join([str(k) for k in duplicate_sshkeys]),
+            )
+            logger.error(msg)
+            print(msg, file=sys.stderr)
+            raise SystemExit(12)

programming_language: Python
filename: _sshkey.py
change_type: ModificationType.MODIFY
diff: @@ -16,7 +16,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import cherrypy
-from sqlalchemy import Column, Integer, Text
+from sqlalchemy import Column, Index, Integer, Text
 
 Base = cherrypy.tools.db.get_base()
 
@@ -27,3 +27,7 @@ class SshKey(Base):
     fingerprint = Column('Fingerprint', Text)
     key = Column('Key', Text, unique=True, primary_key=True)
     userid = Column('UserID', Integer, nullable=False)
+
+
+# Make finger print unique
+sshkey_fingerprint_index = Index('sshkey_fingerprint_index', SshKey.fingerprint, unique=True)

programming_language: Python
filename: _user.py
change_type: ModificationType.MODIFY
diff: @@ -159,7 +159,7 @@ class UserObject(Base):
         assert key
         key = authorizedkeys.check_publickey(key)
 
-        # Remove option, replace comments.
+        # Remove option & Remove comment for SQL storage
         key = authorizedkeys.AuthorizedKey(
             options=None, keytype=key.keytype, key=key.key, comment=comment or key.comment
         )
@@ -176,7 +176,8 @@ class UserObject(Base):
             # Also look in database.
             logger.info("add key [%s] to [%s] database", key, self.username)
             try:
-                SshKey(userid=self.userid, fingerprint=key.fingerprint, key=key.getvalue()).add().flush()
+                sshkey = SshKey(userid=self.userid, fingerprint=key.fingerprint, key=key.getvalue())
+                sshkey.add().flush()
             except IntegrityError:
                 raise DuplicateSSHKeyError(
                     _("Duplicate key. This key already exists or is associated to another user.")

programming_language: Python
filename: test_user.py
change_type: ModificationType.MODIFY
diff: @@ -325,15 +325,47 @@ class UserObjectTest(rdiffweb.test.WebCase):
     def test_add_authorizedkey_duplicate(self):
         # Read the pub key
         key = self._read_ssh_key()
-        # Add the key to the user
+        # Given a user with a SSH Key
         userobj = UserObject.get_user(self.USERNAME)
         userobj.add_authorizedkey(key)
         userobj.commit()
-        # Add the same key
+
+        # When adding the same identical key.
+        # Then an error is raised
         with self.assertRaises(DuplicateSSHKeyError):
             userobj.add_authorizedkey(key)
             userobj.commit()
 
+    def test_add_authorizedkey_duplicate_new_comment(self):
+        # Read the pub key
+        key = self._read_ssh_key()
+        # Given a user with a SSH Key
+        userobj = UserObject.get_user(self.USERNAME)
+        userobj.add_authorizedkey(key)
+        userobj.commit()
+
+        # When adding the same key with a different comment
+        # Then an error is raised
+        with self.assertRaises(DuplicateSSHKeyError):
+            userobj.add_authorizedkey(key, comment="new comment")
+            userobj.commit()
+
+    def test_add_authorizedkey_duplicate_new_user(self):
+        # Read the pub key
+        key = self._read_ssh_key()
+        # Given a user with a SSH Key
+        userobj = UserObject.get_user(self.USERNAME)
+        userobj.add_authorizedkey(key)
+        userobj.commit()
+
+        # When adding the same key to a different user
+        # Then an error is raised
+        newuser = UserObject.add_user("newuser")
+        newuser.commit()
+        with self.assertRaises(DuplicateSSHKeyError):
+            newuser.add_authorizedkey(key, comment="new comment")
+            newuser.commit()
+
     def test_add_authorizedkey_with_file(self):
         """
         Add an ssh key for a user with an authorizedkey file.

programming_language: Python