File xmlrpc_privilege_escalation_prevention.patch of Package cobbler
diff --git a/cobbler/cobblerd.py b/cobbler/cobblerd.py
index 9a9537b..b9e918c 100644
--- a/cobbler/cobblerd.py
+++ b/cobbler/cobblerd.py
@@ -57,11 +57,10 @@ def regen_ss_file():
cleared by Kerberos.
"""
ssfile = "/var/lib/cobbler/web.ss"
- with open("/dev/urandom", 'rb') as fd:
- data = fd.read(512)
+ data = os.urandom(512)
- with open(ssfile, 'wb', 0o660) as fd:
- fd.write(binascii.hexlify(data))
+ with open(ssfile, 'w', 0o660, encoding="UTF-8") as fd:
+ fd.write(str(binascii.hexlify(data)))
http_user = "apache"
family = utils.get_family()
diff --git a/cobbler/remote.py b/cobbler/remote.py
index 4856997..497bc1e 100644
--- a/cobbler/remote.py
+++ b/cobbler/remote.py
@@ -3514,6 +3514,8 @@ class CobblerXMLRPCInterface:
"""
# if shared secret access is requested, don't bother hitting the auth plugin
if login_user == "":
+ if self.shared_secret == -1:
+ raise ValueError("login failed(<DIRECT>)")
if login_password == self.shared_secret:
return self.__make_token("<DIRECT>")
else:
diff --git a/cobbler/utils.py b/cobbler/utils.py
index e2b8e74..f78a8a4 100644
--- a/cobbler/utils.py
+++ b/cobbler/utils.py
@@ -1806,13 +1806,12 @@ def get_shared_secret() -> Union[str, int]:
:return: The Cobbler secret which enables full access to Cobbler.
"""
-
try:
- with open("/var/lib/cobbler/web.ss", 'rb', encoding='utf-8') as fd:
- data = fd.read()
- except:
+ with open("/var/lib/cobbler/web.ss", "r", encoding="UTF-8") as web_secret_fd:
+ data = web_secret_fd.read()
+ except Exception:
return -1
- return str(data).strip()
+ return data
def local_get_cobbler_api_url() -> str:
diff --git a/tests/utils_test.py b/tests/utils_test.py
index f6277fa..d8da16b 100644
--- a/tests/utils_test.py
+++ b/tests/utils_test.py
@@ -1,3 +1,4 @@
+import binascii
import datetime
import os
import re
@@ -5,6 +6,7 @@ import shutil
import time
from threading import Thread
from pathlib import Path
+from typing import Any, TYPE_CHECKING
import pytest
from netaddr.ip import IPAddress
@@ -14,6 +16,9 @@ from cobbler.cexceptions import CX
from cobbler.items.distro import Distro
from tests.conftest import does_not_raise
+if TYPE_CHECKING:
+ from pytest_mock import MockerFixture
+
def test_pretty_hex():
# Arrange
@@ -746,15 +751,31 @@ def test_load_signatures():
assert old_cache != utils.SIGNATURE_CACHE
-def test_get_shared_secret():
+@pytest.mark.parametrize("web_ss_exists", [True, False])
+def test_get_shared_secret(mocker: "MockerFixture", web_ss_exists: bool):
# Arrange
- # TODO: Test the case where the file is there.
+ open_mock = mocker.mock_open()
+ random_data = binascii.hexlify(os.urandom(512)).decode()
+ mock_web_ss = mocker.mock_open(read_data=random_data)
+
+ def mock_open(*args: Any, **kwargs: Any):
+ if not web_ss_exists:
+ open_mock.side_effect = FileNotFoundError
+ return open_mock(*args, **kwargs)
+ if args[0] == "/var/lib/cobbler/web.ss":
+ return mock_web_ss(*args, **kwargs)
+ return open_mock(*args, **kwargs)
+
+ mocker.patch("builtins.open", mock_open)
# Act
result = utils.get_shared_secret()
# Assert
- assert result == -1
+ if web_ss_exists:
+ assert result == random_data
+ else:
+ assert result == -1
def test_local_get_cobbler_api_url():
diff --git a/tests/xmlrpcapi/miscellaneous_test.py b/tests/xmlrpcapi/miscellaneous_test.py
index e7b8dda..56f2206 100644
--- a/tests/xmlrpcapi/miscellaneous_test.py
+++ b/tests/xmlrpcapi/miscellaneous_test.py
@@ -2,11 +2,16 @@ import json
import os
import pathlib
import time
+from typing import Any
import pytest
+from cobbler.cexceptions import CX
+from cobbler.remote import CobblerXMLRPCInterface
from cobbler.utils import get_shared_secret
+from tests.conftest import does_not_raise
+
@pytest.fixture(autouse=True)
def cleanup_clear_system_logs(remove_distro, remove_profile, remove_system):
@@ -535,6 +540,41 @@ class TestMiscellaneous:
# Assert
assert not result
+ @pytest.mark.parametrize(
+ "input_username,input_password,expected_result,expected_exception,web_ss_exists",
+ [
+ ("cobbler", "cobbler", True, does_not_raise(), True),
+ ("cobbler", "incorrect-password", True, pytest.raises(CX), True),
+ ("", "doesnt-matter", True, pytest.raises(CX), True),
+ ("", "my-random-web-ss", True, does_not_raise(), True),
+ ("", "my-random-web-ss", True, pytest.raises(ValueError), False),
+ ],
+ )
+ def test_login(
+ self,
+ remote: CobblerXMLRPCInterface,
+ input_username: str,
+ input_password: str,
+ expected_result: Any,
+ expected_exception: Any,
+ web_ss_exists: bool
+ ):
+ """
+ Assert that the login is working successfully with correct and incorrect credentials.
+ """
+ # Arrange
+ if web_ss_exists:
+ remote.shared_secret = "my-random-web-ss"
+ else:
+ remote.shared_secret = -1
+
+ # Act
+ with expected_exception:
+ token = remote.login(input_username, input_password)
+
+ # Assert
+ assert remote.token_check(token) == expected_result
+
def test_logout(self, remote):
# Arrange
shared_secret = get_shared_secret()