#!/bin/bash
#
# Reproducer for SELinux denial: systemd-hostnamed cannot create Varlink socket
#
# Bug: systemd 257+ introduced Varlink socket /run/systemd/io.systemd.Hostname
#      but SELinux policy doesn't allow systemd_hostnamed_t to create sock_file
#      in init_var_run_t context.
#
# Affected: RHEL 10 / CentOS Stream 10 with systemd 257+ and selinux-policy < fix
#
# Related upstream issue: https://github.com/fedora-selinux/selinux-policy/issues/2947
#
# Usage: ./reproduce-hostnamed-selinux-denial.sh
#
# Requirements:
#   - SELinux in enforcing mode
#   - systemd 257+
#   - python3-selinux package
#

set -euo pipefail

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo "=============================================="
echo "SELinux Denial Reproducer: systemd-hostnamed"
echo "=============================================="
echo ""

# Check SELinux status
echo -n "Checking SELinux status... "
SELINUX_MODE=$(getenforce 2>/dev/null || echo "Unknown")
if [[ "$SELINUX_MODE" == "Enforcing" ]]; then
    echo -e "${GREEN}Enforcing${NC}"
elif [[ "$SELINUX_MODE" == "Permissive" ]]; then
    echo -e "${YELLOW}Permissive (denials will be logged but not enforced)${NC}"
else
    echo -e "${RED}$SELINUX_MODE - SELinux must be enabled to reproduce${NC}"
    exit 1
fi

# Check systemd version
echo -n "Checking systemd version... "
SYSTEMD_VERSION=$(rpm -q systemd 2>/dev/null || echo "unknown")
echo "$SYSTEMD_VERSION"

# Check selinux-policy version
echo -n "Checking selinux-policy version... "
POLICY_VERSION=$(rpm -q selinux-policy 2>/dev/null || echo "unknown")
echo "$POLICY_VERSION"

echo ""
echo "----------------------------------------------"
echo "Testing SELinux access permissions..."
echo "----------------------------------------------"
echo ""
echo "Context: systemd_hostnamed_t -> init_var_run_t (sock_file)"
echo ""

# Test using python3-selinux
python3 << 'PYTHON_SCRIPT'
import sys

try:
    import selinux
except ImportError:
    print("ERROR: python3-selinux not installed")
    print("Install with: dnf install python3-libselinux")
    sys.exit(1)

scon = "system_u:system_r:systemd_hostnamed_t:s0"
tcon = "system_u:object_r:init_var_run_t:s0"
tclass = "sock_file"

# Permissions needed for Varlink socket
perms_to_test = [
    "create",    # Create the socket file
    "write",     # Write to the socket
    "unlink",    # Remove the socket
    "getattr",   # Get attributes
    "setattr",   # Set attributes
]

print(f"Source context:  {scon}")
print(f"Target context:  {tcon}")
print(f"Target class:    {tclass}")
print("")
print("Permission      Status")
print("-" * 30)

denied_perms = []
allowed_perms = []

for perm in perms_to_test:
    try:
        # selinux_check_access returns 0 on success, raises PermissionError on denial
        result = selinux.selinux_check_access(scon, tcon, tclass, perm)
        print(f"{perm:<15} \033[0;32mALLOWED\033[0m")
        allowed_perms.append(perm)
    except PermissionError:
        print(f"{perm:<15} \033[0;31mDENIED\033[0m")
        denied_perms.append(perm)
    except Exception as e:
        print(f"{perm:<15} \033[1;33mERROR: {e}\033[0m")

print("")
print("=" * 50)

if denied_perms:
    print("\033[0;31mBUG REPRODUCED!\033[0m")
    print("")
    print("The following permissions are denied:")
    for perm in denied_perms:
        print(f"  - {perm}")
    print("")
    print("This causes systemd-hostnamed to fail creating its Varlink socket:")
    print("  /run/systemd/io.systemd.Hostname")
    print("")
    print("AVC denial that would be logged (if audit is enabled):")
    print("")
    for perm in denied_perms:
        print(f'  avc: denied {{ {perm} }} for comm="systemd-hostnam" \\')
        print(f'       name="io.systemd.Hostname" \\')
        print(f'       scontext={scon} \\')
        print(f'       tcontext={tcon} \\')
        print(f'       tclass={tclass}')
        print("")
    print("Upstream issue: https://github.com/fedora-selinux/selinux-policy/issues/2947")
    sys.exit(1)
else:
    print("\033[0;32mNo denial detected - policy may have been fixed.\033[0m")
    sys.exit(0)
PYTHON_SCRIPT

RESULT=$?

echo ""
echo "----------------------------------------------"
echo "Additional diagnostic information"
echo "----------------------------------------------"

# Check if the socket exists
echo ""
echo -n "Varlink socket status: "
if [[ -S /run/systemd/io.systemd.Hostname ]]; then
    echo -e "${GREEN}EXISTS${NC}"
    ls -laZ /run/systemd/io.systemd.Hostname
else
    echo -e "${RED}DOES NOT EXIST${NC}"
fi

# Check systemd-hostnamed status
echo ""
echo "systemd-hostnamed service status:"
systemctl status systemd-hostnamed --no-pager 2>&1 | head -10 || true

# Check for recent AVC denials (if audit is available)
echo ""
echo "Recent AVC denials for hostnamed (if any):"
if command -v ausearch &>/dev/null; then
    ausearch -m avc -ts recent 2>/dev/null | grep -E "hostnam|Hostname" | tail -5 || echo "  (none found or ausearch not available)"
else
    journalctl -k -b 2>/dev/null | grep -E "avc.*hostnam|avc.*Hostname" | tail -5 || echo "  (none found in journal)"
fi

exit $RESULT
