Просмотр исходного кода

Merge branch 'v3.2-devel' of https://github.com/MatthewHana/Radicale-newwebui into v3.2-devel

MatthewHana 1 год назад
Родитель
Сommit
b945749d1b
5 измененных файлов с 64 добавлено и 16 удалено
  1. 1 0
      CHANGELOG.md
  2. 11 2
      DOCUMENTATION.md
  3. 8 0
      README.md
  4. 1 1
      config
  5. 43 13
      radicale/auth/htpasswd.py

+ 1 - 0
CHANGELOG.md

@@ -14,6 +14,7 @@
 * Enhancement: autodetect logging to systemd journal
 * Enhancement: test code
 * Enhancement: option for global permit to delete collection
+* Enhancement: auth type 'htpasswd' supports now 'htpasswd_encryption' sha256/sha512 and "autodetect" for smooth transition
 * Improve: Dockerfiles
 * Update: documentations + examples
 * Dependency: limit typegard version < 3

+ 11 - 2
DOCUMENTATION.md

@@ -758,10 +758,19 @@ Available methods:
 
 `bcrypt`
 : This uses a modified version of the Blowfish stream cipher. It's very secure.
-  The installation of **radicale[bcrypt]** is required for this.
+  The installation of **bcrypt** is required for this.
 
 `md5`
-: This uses an iterated md5 digest of the password with a salt.
+: This uses an iterated MD5 digest of the password with a salt.
+
+`sha256`
+: This uses an iterated SHA-256 digest of the password with a salt.
+
+`sha512`
+: This uses an iterated SHA-512 digest of the password with a salt.
+
+`autodetect`
+: This selects autodetection of method per entry.
 
 Default: `md5`
 

+ 8 - 0
README.md

@@ -18,3 +18,11 @@ Radicale is a small but powerful CalDAV (calendars, to-do lists) and CardDAV
 
 For the complete documentation, please visit
 [Radicale master Documentation](https://radicale.org/master.html).
+
+Additional hints can be found
+* [Radicale Wiki](https://github.com/Kozea/Radicale/wiki)
+* [Radicale Issues](https://github.com/Kozea/Radicale/issues)
+* [Radicale Discussions](https://github.com/Kozea/Radicale/discussions)
+
+Before reporting an issue, please check
+* [Radicale Wiki / Reporting Issues](https://github.com/Kozea/Radicale/wiki/Reporting-Issues)

+ 1 - 1
config

@@ -59,7 +59,7 @@
 #htpasswd_filename = /etc/radicale/users
 
 # Htpasswd encryption method
-# Value: plain | bcrypt | md5
+# Value: plain | bcrypt | md5 | sha256 | sha512 | autodetect
 # bcrypt requires the installation of 'bcrypt' module.
 #htpasswd_encryption = md5
 

+ 43 - 13
radicale/auth/htpasswd.py

@@ -3,6 +3,7 @@
 # Copyright © 2008 Pascal Halter
 # Copyright © 2008-2017 Guillaume Ayoub
 # Copyright © 2017-2019 Unrud <unrud@outlook.com>
+# Copyright © 2024 Peter Bieringer <pb@bieringer.de>
 #
 # This library is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -22,12 +23,12 @@ Authentication backend that checks credentials with a htpasswd file.
 
 Apache's htpasswd command (httpd.apache.org/docs/programs/htpasswd.html)
 manages a file for storing user credentials. It can encrypt passwords using
-different the methods BCRYPT or MD5-APR1 (a version of MD5 modified for
-Apache). MD5-APR1 provides medium security as of 2015. Only BCRYPT can be
+different the methods BCRYPT/SHA256/SHA512 or MD5-APR1 (a version of MD5 modified for
+Apache). MD5-APR1 provides medium security as of 2015. Only BCRYPT/SHA256/SHA512 can be
 considered secure by current standards.
 
 MD5-APR1-encrypted credentials can be written by all versions of htpasswd (it
-is the default, in fact), whereas BCRYPT requires htpasswd 2.4.x or newer.
+is the default, in fact), whereas BCRYPT/SHA256/SHA512 requires htpasswd 2.4.x or newer.
 
 The `is_authenticated(user, password)` function provided by this module
 verifies the user-given credentials by parsing the htpasswd credential file
@@ -37,13 +38,13 @@ configuration value.
 
 The following htpasswd password encrpytion methods are supported by Radicale
 out-of-the-box:
-
-    - plain-text (created by htpasswd -p...) -- INSECURE
-    - MD5-APR1   (htpasswd -m...) -- htpasswd's default method
+    - plain-text (created by htpasswd -p ...) -- INSECURE
+    - MD5-APR1   (htpasswd -m ...) -- htpasswd's default method, INSECURE
+    - SHA256     (htpasswd -2 ...)
+    - SHA512     (htpasswd -5 ...)
 
 When bcrypt is installed:
-
-    - BCRYPT     (htpasswd -B...) -- Requires htpasswd 2.4.x
+    - BCRYPT     (htpasswd -B ...) -- Requires htpasswd 2.4.x
 
 """
 
@@ -51,9 +52,9 @@ import functools
 import hmac
 from typing import Any
 
-from passlib.hash import apr_md5_crypt
+from passlib.hash import apr_md5_crypt, sha256_crypt, sha512_crypt
 
-from radicale import auth, config
+from radicale import auth, config, logger
 
 
 class Auth(auth.BaseAuth):
@@ -67,18 +68,24 @@ class Auth(auth.BaseAuth):
         self._encoding = configuration.get("encoding", "stock")
         encryption: str = configuration.get("auth", "htpasswd_encryption")
 
+        logger.info("auth password encryption: %s", encryption)
+
         if encryption == "plain":
             self._verify = self._plain
         elif encryption == "md5":
             self._verify = self._md5apr1
-        elif encryption == "bcrypt":
+        elif encryption == "bcrypt" or encryption == "autodetect":
             try:
                 import bcrypt
             except ImportError as e:
                 raise RuntimeError(
-                    "The htpasswd encryption method 'bcrypt' requires "
+                    "The htpasswd encryption method 'bcrypt' or 'auto' requires "
                     "the bcrypt module.") from e
-            self._verify = functools.partial(self._bcrypt, bcrypt)
+            if encryption == "bcrypt":
+                self._verify = functools.partial(self._bcrypt, bcrypt)
+            else:
+                self._verify = self._autodetect
+                self._verify_bcrypt = functools.partial(self._bcrypt, bcrypt)
         else:
             raise RuntimeError("The htpasswd encryption method %r is not "
                                "supported." % encryption)
@@ -93,6 +100,29 @@ class Auth(auth.BaseAuth):
     def _md5apr1(self, hash_value: str, password: str) -> bool:
         return apr_md5_crypt.verify(password, hash_value.strip())
 
+    def _sha256(self, hash_value: str, password: str) -> bool:
+        return sha256_crypt.verify(password, hash_value.strip())
+
+    def _sha512(self, hash_value: str, password: str) -> bool:
+        return sha512_crypt.verify(password, hash_value.strip())
+
+    def _autodetect(self, hash_value: str, password: str) -> bool:
+        if hash_value.startswith("$apr1$", 0, 6) and len(hash_value) == 37:
+            # MD5-APR1
+            return self._md5apr1(hash_value, password)
+        elif hash_value.startswith("$2y$", 0, 4) and len(hash_value) == 60:
+            # BCRYPT
+            return self._verify_bcrypt(hash_value, password)
+        elif hash_value.startswith("$5$", 0, 3) and len(hash_value) == 63:
+            # SHA-256
+            return self._sha256(hash_value, password)
+        elif hash_value.startswith("$6$", 0, 3) and len(hash_value) == 106:
+            # SHA-512
+            return self._sha512(hash_value, password)
+        else:
+            # assumed plaintext
+            return self._plain(hash_value, password)
+
     def login(self, login: str, password: str) -> str:
         """Validate credentials.