|
@@ -51,6 +51,7 @@ When bcrypt is installed:
|
|
|
import functools
|
|
import functools
|
|
|
import hmac
|
|
import hmac
|
|
|
import os
|
|
import os
|
|
|
|
|
+import re
|
|
|
import threading
|
|
import threading
|
|
|
import time
|
|
import time
|
|
|
from typing import Any, Tuple
|
|
from typing import Any, Tuple
|
|
@@ -131,33 +132,49 @@ class Auth(auth.BaseAuth):
|
|
|
"""Check if ``hash_value`` and ``password`` match, plain method."""
|
|
"""Check if ``hash_value`` and ``password`` match, plain method."""
|
|
|
return ("PLAIN", hmac.compare_digest(hash_value.encode(), password.encode()))
|
|
return ("PLAIN", hmac.compare_digest(hash_value.encode(), password.encode()))
|
|
|
|
|
|
|
|
|
|
+ def _plain_fallback(self, method_orig, hash_value: str, password: str) -> tuple[str, bool]:
|
|
|
|
|
+ """Check if ``hash_value`` and ``password`` match, plain method / fallback in case of hash length is not matching on autodetection."""
|
|
|
|
|
+ info = "PLAIN/fallback as hash length not matching for " + method_orig + ": " + str(len(hash_value))
|
|
|
|
|
+ return (info, hmac.compare_digest(hash_value.encode(), password.encode()))
|
|
|
|
|
+
|
|
|
def _bcrypt(self, bcrypt: Any, hash_value: str, password: str) -> tuple[str, bool]:
|
|
def _bcrypt(self, bcrypt: Any, hash_value: str, password: str) -> tuple[str, bool]:
|
|
|
- return ("BCRYPT", bcrypt.checkpw(password=password.encode('utf-8'), hashed_password=hash_value.encode()))
|
|
|
|
|
|
|
+ if self._encryption == "autodetect" and len(hash_value) != 60:
|
|
|
|
|
+ return self._plain_fallback("BCRYPT", hash_value, password)
|
|
|
|
|
+ else:
|
|
|
|
|
+ return ("BCRYPT", bcrypt.checkpw(password=password.encode('utf-8'), hashed_password=hash_value.encode()))
|
|
|
|
|
|
|
|
def _md5apr1(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
def _md5apr1(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
|
- return ("MD5-APR1", apr_md5_crypt.verify(password, hash_value.strip()))
|
|
|
|
|
|
|
+ if self._encryption == "autodetect" and len(hash_value) != 37:
|
|
|
|
|
+ return self._plain_fallback("MD5-APR1", hash_value, password)
|
|
|
|
|
+ else:
|
|
|
|
|
+ return ("MD5-APR1", apr_md5_crypt.verify(password, hash_value.strip()))
|
|
|
|
|
|
|
|
def _sha256(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
def _sha256(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
|
- return ("SHA-256", sha256_crypt.verify(password, hash_value.strip()))
|
|
|
|
|
|
|
+ if self._encryption == "autodetect" and len(hash_value) != 63:
|
|
|
|
|
+ return self._plain_fallback("SHA-256", hash_value, password)
|
|
|
|
|
+ else:
|
|
|
|
|
+ return ("SHA-256", sha256_crypt.verify(password, hash_value.strip()))
|
|
|
|
|
|
|
|
def _sha512(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
def _sha512(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
|
- return ("SHA-512", sha512_crypt.verify(password, hash_value.strip()))
|
|
|
|
|
|
|
+ if self._encryption == "autodetect" and len(hash_value) != 106:
|
|
|
|
|
+ return self._plain_fallback("SHA-512", hash_value, password)
|
|
|
|
|
+ else:
|
|
|
|
|
+ return ("SHA-512", sha512_crypt.verify(password, hash_value.strip()))
|
|
|
|
|
|
|
|
def _autodetect(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
def _autodetect(self, hash_value: str, password: str) -> tuple[str, bool]:
|
|
|
- if hash_value.startswith("$apr1$", 0, 6) and len(hash_value) == 37:
|
|
|
|
|
|
|
+ if hash_value.startswith("$apr1$", 0, 6):
|
|
|
# MD5-APR1
|
|
# MD5-APR1
|
|
|
return self._md5apr1(hash_value, password)
|
|
return self._md5apr1(hash_value, password)
|
|
|
- elif hash_value.startswith("$2y$", 0, 4) and len(hash_value) == 60:
|
|
|
|
|
|
|
+ elif re.match(r"^\$2(a|b|x|y)?\$", hash_value):
|
|
|
# BCRYPT
|
|
# BCRYPT
|
|
|
return self._verify_bcrypt(hash_value, password)
|
|
return self._verify_bcrypt(hash_value, password)
|
|
|
- elif hash_value.startswith("$5$", 0, 3) and len(hash_value) == 63:
|
|
|
|
|
|
|
+ elif hash_value.startswith("$5$", 0, 3):
|
|
|
# SHA-256
|
|
# SHA-256
|
|
|
return self._sha256(hash_value, password)
|
|
return self._sha256(hash_value, password)
|
|
|
- elif hash_value.startswith("$6$", 0, 3) and len(hash_value) == 106:
|
|
|
|
|
|
|
+ elif hash_value.startswith("$6$", 0, 3):
|
|
|
# SHA-512
|
|
# SHA-512
|
|
|
return self._sha512(hash_value, password)
|
|
return self._sha512(hash_value, password)
|
|
|
else:
|
|
else:
|
|
|
- # assumed plaintext
|
|
|
|
|
return self._plain(hash_value, password)
|
|
return self._plain(hash_value, password)
|
|
|
|
|
|
|
|
def _read_htpasswd(self, init: bool, suppress: bool) -> Tuple[bool, int, dict, int, int]:
|
|
def _read_htpasswd(self, init: bool, suppress: bool) -> Tuple[bool, int, dict, int, int]:
|