From 72aa77d43817247635952c48a95cf802dbe17b4b Mon Sep 17 00:00:00 2001 From: Ernesto Puerta Date: Fri, 7 Jun 2024 11:41:26 +0200 Subject: [PATCH] mgr: fix pyO3 import issues The latest versions of pyO3 (a Python binding for Rust) explicitly added a check to detect multiple imports. In subinterpreter environments, this leads to an ImportError: "PyO3 modules may only be initialized once per interpreter process" (it has been recenlty replaced with a more specific: "PyO3 modules may only be initialized once per interpreter process". This is only a workaround while the root cause is fixed (see https://github.com/PyO3/pyo3/pull/4162). Fixes: https://tracker.ceph.com/issues/64213 Signed-off-by: Ernesto Puerta --- .../mgr/dashboard/services/access_control.py | 46 ++++++++----------- src/pybind/mgr/mgr_util.py | 3 +- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/pybind/mgr/dashboard/services/access_control.py b/src/pybind/mgr/dashboard/services/access_control.py index 6319802b6cc7ba..94398ca76130db 100644 --- a/src/pybind/mgr/dashboard/services/access_control.py +++ b/src/pybind/mgr/dashboard/services/access_control.py @@ -2,9 +2,13 @@ # pylint: disable=too-many-arguments,too-many-return-statements # pylint: disable=too-many-branches, too-many-locals, too-many-statements +import base64 import errno +import hashlib +import hmac import json import logging +import os import re import threading import time @@ -12,9 +16,7 @@ from string import ascii_lowercase, ascii_uppercase, digits, punctuation from typing import List, Optional, Sequence -import bcrypt from mgr_module import CLICheckNonemptyFileInput, CLIReadCommand, CLIWriteCommand -from mgr_util import password_hash from .. import mgr from ..exceptions import PasswordPolicyException, PermissionNotValid, \ @@ -26,7 +28,7 @@ logger = logging.getLogger('access_control') DEFAULT_FILE_DESC = 'password/secret' - +SCRYPT_SALT_LEN = 29 _P = Permission # short alias @@ -353,8 +355,17 @@ def enabled(self, value): self._enabled = value self.refresh_last_update() + @staticmethod + def calculate_password_hash(password: str, input_salt: Optional[str] = None) -> str: + if input_salt is None: + salt = os.urandom(SCRYPT_SALT_LEN) + else: + salt = base64.b64decode(salt)[:SCRYPT_SALT_LEN] + hash = hashlib.scrypt(password.encode('utf8'), salt=salt, n=2**14, r=8, p=1) + return base64.b64encode(salt + hash).decode('utf8') + def set_password(self, password): - self.set_password_hash(password_hash(password)) + self.set_password_hash(self.calculate_password_hash(password)) def set_password_hash(self, hashed_password): self.invalid_auth_attempt = 0 @@ -371,8 +382,8 @@ def compare_password(self, password): :return: `True` if the passwords are equal, otherwise `False`. :rtype: bool """ - pass_hash = password_hash(password, salt_password=self.password) - return pass_hash == self.password + pass_hash = self.calculate_password_hash(password, salt_password=self.password) + return hmac.compare_digest(pass_hash, self.password) def is_pwd_expired(self): if self.pwd_expiration_date: @@ -501,7 +512,7 @@ def create_user(self, username, password, name, email, enabled=True, if pwd_expiration_date and \ (pwd_expiration_date < int(time.mktime(datetime.utcnow().timetuple()))): raise PwdExpirationDateNotValid() - user = User(username, password_hash(password), name, email, enabled=enabled, + user = User(username, User.calculate_password_hash(password), name, email, enabled=enabled, pwd_expiration_date=pwd_expiration_date, pwd_update_required=pwd_update_required) self.users[username] = user @@ -906,27 +917,6 @@ def ac_user_set_password(_, username: str, inbuf: str, return -errno.ENOENT, '', str(ex) -@CLIWriteCommand('dashboard ac-user-set-password-hash') -@CLICheckNonemptyFileInput(desc=DEFAULT_FILE_DESC) -def ac_user_set_password_hash(_, username: str, inbuf: str): - ''' - Set user password bcrypt hash from -i - ''' - hashed_password = inbuf - try: - # make sure the hashed_password is actually a bcrypt hash - bcrypt.checkpw(b'', hashed_password.encode('utf-8')) - user = mgr.ACCESS_CTRL_DB.get_user(username) - user.set_password_hash(hashed_password) - - mgr.ACCESS_CTRL_DB.save() - return 0, json.dumps(user.to_dict()), '' - except ValueError: - return -errno.EINVAL, '', 'Invalid password hash' - except UserDoesNotExist as ex: - return -errno.ENOENT, '', str(ex) - - @CLIWriteCommand('dashboard ac-user-set-info') def ac_user_set_info(_, username: str, name: str, email: str): ''' diff --git a/src/pybind/mgr/mgr_util.py b/src/pybind/mgr/mgr_util.py index 5d37d478de7b1e..055702714e135b 100644 --- a/src/pybind/mgr/mgr_util.py +++ b/src/pybind/mgr/mgr_util.py @@ -10,7 +10,6 @@ if 'UNITTEST' in os.environ: import tests # noqa -import bcrypt import cephfs import contextlib import datetime @@ -969,6 +968,8 @@ def wrapper(*args: Any, **kwargs: Any) -> T: def password_hash(password: Optional[str], salt_password: Optional[str] = None) -> Optional[str]: + import bcrypt + if not password: return None if not salt_password: