"""
Authentication module that uses /etc/cobbler/auth.conf
Choice of authentication module is in /etc/cobbler/modules.conf
"""
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: Copyright 2007-2009, Red Hat, Inc and Others
# SPDX-FileCopyrightText: Michael DeHaan <michael.dehaan AT gmail>
import hashlib
import os
from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
from cobbler.api import CobblerAPI
[docs]def hashfun(api: "CobblerAPI", text: str) -> str:
"""
Converts a str object to a hash which was configured in modules.conf of the Cobbler settings.
:param api: CobblerAPI
:param text: The text to hash.
:return: The hash of the text. This should output the same hash when entered the same text.
"""
hashfunction = (
api.settings()
.modules.get("authentication", {})
.get("hash_algorithm", "sha3_512")
)
if hashfunction == "sha3_224":
hashalgorithm = hashlib.sha3_224(text.encode("utf-8"))
elif hashfunction == "sha3_384":
hashalgorithm = hashlib.sha3_384(text.encode("utf-8"))
elif hashfunction == "sha3_256":
hashalgorithm = hashlib.sha3_256(text.encode("utf-8"))
elif hashfunction == "sha3_512":
hashalgorithm = hashlib.sha3_512(text.encode("utf-8"))
elif hashfunction == "blake2b":
hashalgorithm = hashlib.blake2b(text.encode("utf-8"))
elif hashfunction == "blake2s":
hashalgorithm = hashlib.blake2s(text.encode("utf-8"))
elif hashfunction == "shake_128":
hashalgorithm = hashlib.shake_128(text.encode("utf-8"))
elif hashfunction == "shake_256":
hashalgorithm = hashlib.shake_256(text.encode("utf-8"))
else:
errortext = f"The hashfunction (Currently: {hashfunction}) must be one of the defined in the settings!"
raise ValueError(errortext)
# FIXME: Add case for SHAKE
return hashalgorithm.hexdigest() # type: ignore
[docs]def register() -> str:
"""
The mandatory Cobbler module registration hook.
"""
return "authn"
def __parse_storage() -> List[List[str]]:
"""
Parse the users.digest file and return all users.
:return: A list of all users. A user is a sublist which has three elements: username, realm and passwordhash.
"""
if not os.path.exists("/etc/cobbler/users.digest"):
return []
with open("/etc/cobbler/users.digest", encoding="utf-8") as users_digest_fd:
data = users_digest_fd.read()
results: List[List[str]] = []
lines = data.split("\n")
for line in lines:
try:
line = line.strip()
tokens = line.split(":")
results.append([tokens[0], tokens[1], tokens[2]])
except Exception:
pass
return results
[docs]def authenticate(api_handle: "CobblerAPI", username: str, password: str) -> bool:
"""
Validate a username/password combo.
Thanks to https://trac.edgewall.org/ticket/845 for supplying the algorithm info.
:param api_handle: Unused in this implementation.
:param username: The username to log in with. Must be contained in /etc/cobbler/users.digest
:param password: The password to log in with. Must be contained hashed in /etc/cobbler/users.digest
:return: A boolean which contains the information if the username/password combination is correct.
"""
userlist = __parse_storage()
for (user, realm, passwordhash) in userlist:
if user == username and realm == "Cobbler":
calculated_passwordhash = hashfun(api_handle, password)
if calculated_passwordhash == passwordhash:
return True
return False