Skip to content

Commit

Permalink
Merge pull request #1728 from pbiering/catch-invalid-salt
Browse files Browse the repository at this point in the history
Catch invalid salt
  • Loading branch information
pbiering authored Mar 8, 2025
2 parents 30664f9 + 2f1db01 commit 4f0e607
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Add: on-the-fly link activation and default content adjustment in case of bundled InfCloud (tested with 0.13.1)
* Adjust: [auth] imap: use AUTHENTICATE PLAIN instead of LOGIN towards remote IMAP server
* Improve: log client IP on SSL error and SSL protocol+cipher if successful
* Improve: catch htpasswd hash verification errors

## 3.4.1
* Add: option [auth] dovecot_connection_type / dovecot_host / dovecot_port
Expand Down
39 changes: 22 additions & 17 deletions radicale/auth/htpasswd.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class Auth(auth.BaseAuth):
_htpasswd_bcrypt_use: int
_htpasswd_cache: bool
_has_bcrypt: bool
_encryption: str
_lock: threading.Lock

def __init__(self, configuration: config.Configuration) -> None:
Expand All @@ -83,48 +84,48 @@ def __init__(self, configuration: config.Configuration) -> None:
logger.info("auth htpasswd file encoding: %r", self._encoding)
self._htpasswd_cache = configuration.get("auth", "htpasswd_cache")
logger.info("auth htpasswd cache: %s", self._htpasswd_cache)
encryption: str = configuration.get("auth", "htpasswd_encryption")
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s'", encryption)
self._encryption: str = configuration.get("auth", "htpasswd_encryption")
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s'", self._encryption)

self._has_bcrypt = False
self._htpasswd_ok = False
self._htpasswd_not_ok_reminder_seconds = 60 # currently hardcoded
(self._htpasswd_ok, self._htpasswd_bcrypt_use, self._htpasswd, self._htpasswd_size, self._htpasswd_mtime_ns) = self._read_htpasswd(True, False)
self._lock = threading.Lock()

if encryption == "plain":
if self._encryption == "plain":
self._verify = self._plain
elif encryption == "md5":
elif self._encryption == "md5":
self._verify = self._md5apr1
elif encryption == "sha256":
elif self._encryption == "sha256":
self._verify = self._sha256
elif encryption == "sha512":
elif self._encryption == "sha512":
self._verify = self._sha512
elif encryption == "bcrypt" or encryption == "autodetect":
elif self._encryption == "bcrypt" or self._encryption == "autodetect":
try:
import bcrypt
except ImportError as e:
if (encryption == "autodetect") and (self._htpasswd_bcrypt_use == 0):
logger.warning("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' which can require bycrypt module, but currently no entries found", encryption)
if (self._encryption == "autodetect") and (self._htpasswd_bcrypt_use == 0):
logger.warning("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' which can require bycrypt module, but currently no entries found", self._encryption)
else:
raise RuntimeError(
"The htpasswd encryption method 'bcrypt' or 'autodetect' requires "
"the bcrypt module (entries found: %d)." % self._htpasswd_bcrypt_use) from e
else:
if encryption == "autodetect":
if self._encryption == "autodetect":
if self._htpasswd_bcrypt_use == 0:
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' and bycrypt module found, but currently not required", encryption)
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' and bycrypt module found, but currently not required", self._encryption)
else:
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' and bycrypt module found (bcrypt entries found: %d)", encryption, self._htpasswd_bcrypt_use)
if encryption == "bcrypt":
logger.info("auth htpasswd encryption is 'radicale.auth.htpasswd_encryption.%s' and bycrypt module found (bcrypt entries found: %d)", self._encryption, self._htpasswd_bcrypt_use)
if self._encryption == "bcrypt":
self._verify = functools.partial(self._bcrypt, bcrypt)
else:
self._verify = self._autodetect
self._verify_bcrypt = functools.partial(self._bcrypt, bcrypt)
self._has_bcrypt = True
else:
raise RuntimeError("The htpasswd encryption method %r is not "
"supported." % encryption)
"supported." % self._encryption)

def _plain(self, hash_value: str, password: str) -> tuple[str, bool]:
"""Check if ``hash_value`` and ``password`` match, plain method."""
Expand Down Expand Up @@ -285,12 +286,16 @@ def _login(self, login: str, password: str) -> str:
login_ok = True

if login_ok is True:
(method, password_ok) = self._verify(digest, password)
try:
(method, password_ok) = self._verify(digest, password)
except ValueError as e:
logger.warning("Login verification failed for user: '%s' (method '%s') '%s'", login, self._encryption, e)
return ""
logger.debug("Login verification successful for user: '%s' (method '%s')", login, method)
if password_ok:
return login
else:
logger.debug("Login verification failed for user: '%s' ( method '%s')", login, method)
logger.warning("Login verification failed for user: '%s' (method '%s')", login, method)
else:
logger.debug("Login verification user not found: '%s'", login)
logger.warning("Login verification user not found: '%s'", login)
return ""

0 comments on commit 4f0e607

Please sign in to comment.