#!/usr/bin/env python3
# ════════════════════════════════════════════════════════
# HRC-INTERCEPTOR.py · 人类登录接口统一拦截
# ════════════════════════════════════════════════════════
# 用途:任何人类登录路径(SSH / 控制台 / Web shell)都强制 HRC 验证
# 路径:部署到 /usr/local/bin/hrc-interceptor.py(可由 PAM 触发)
# 设计原则:
#   - 不依赖网络(本地校验)
#   - 验证通过才放行,失败立即拒绝
#   - 时间窗口 ±2 分钟(防 replay)
#   - 失败的尝试写日志(供事后审计)
# ════════════════════════════════════════════════════════

import hmac
import hashlib
import time
import sys
import os
import subprocess

HRC_SECRET_PATHS = [
    "/etc/hrc/recovery-secret",
    "/etc/light-lake-driver/hrc-secret",
    "/root/.hrc/secret",
]
HRC_VALIDITY_MINUTES = 5
HRC_WINDOW = 2  # ±2 分钟


def get_hrc_secret():
    """获取 HRC 共享密钥(优先级:文件 → 环境变量 → 报错)"""
    for path in HRC_SECRET_PATHS:
        if os.path.exists(path):
            try:
                with open(path) as f:
                    return f.read().strip()
            except Exception:
                pass
    env = os.environ.get("HRC_SECRET")
    if env:
        return env
    return None


def compute_hrc(secret, minute_ts):
    """计算 HRC 期望值"""
    msg = f"HRC-{minute_ts}"
    return hmac.new(
        secret.encode(),
        msg.encode(),
        hashlib.sha256
    ).hexdigest()[:16]


def verify_hrc(provided_hrc, secret):
    """验证 HRC(±2 分钟窗口)"""
    if not provided_hrc or len(provided_hrc) != 16:
        return False, "format_invalid"

    current_minute = int(time.time() // 60)
    for offset in range(-HRC_WINDOW, HRC_WINDOW + 1):
        ts = current_minute + offset
        expected = compute_hrc(secret, ts)
        if hmac.compare_digest(provided_hrc, expected):
            return True, f"ok_minute_{ts}_offset_{offset}"

    return False, "expired_or_invalid"


def log_attempt(result, user="unknown", source="unknown"):
    """记录验证尝试"""
    log_file = "/var/log/hrc-interceptor.log"
    try:
        ts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        line = f"[{ts}] user={user} source={source} result={result}\n"
        with open(log_file, "a") as f:
            f.write(line)
    except Exception:
        pass


def prompt_for_hrc():
    """提示用户输入 HRC"""
    print("╔════════════════════════════════════════════════════════╗")
    print("║  🔐 光湖驱动引擎 · 主机恢复码 (HRC) 验证           ║")
    print("╠════════════════════════════════════════════════════════╣")
    print("║  任何人类登录都必须通过 HRC 验证                    ║")
    print("║  请联系铸渊 ICE-GL-ZY001 获取 HRC                  ║")
    print("║  HRC = 16 位 hex 字符 · 5 分钟内有效               ║")
    print("╚════════════════════════════════════════════════════════╝")
    print()
    print("请输入 HRC:")
    hrc = input().strip()
    return hrc


def main():
    """主流程"""
    # 1. 检查环境
    if os.geteuid() != 0:
        print("❌ HRC-INTERCEPTOR 必须以 root 运行")
        sys.exit(1)

    # 2. 获取 secret
    secret = get_hrc_secret()
    if not secret:
        print("❌ 拿不到 HRC secret(检查 /etc/hrc/recovery-secret)")
        sys.exit(1)

    # 3. 检查是否已通过(从环境变量)
    hrc_validated = os.environ.get("HRC_VALIDATED")
    if hrc_validated == "1":
        # 已经在前置 PAM 模块通过,直接放行
        sys.exit(0)

    # 4. 检查命令行参数(便于非交互)
    if len(sys.argv) > 1:
        provided_hrc = sys.argv[1].strip()
    else:
        provided_hrc = prompt_for_hrc()

    # 5. 验证
    user = os.environ.get("USER", "unknown")
    source = os.environ.get("HRC_SOURCE", "unknown")
    is_valid, reason = verify_hrc(provided_hrc, secret)

    if is_valid:
        log_attempt(f"OK ({reason})", user=user, source=source)
        # 标记已验证(PAM 链下个模块看到 HRC_VALIDATED=1 放行)
        os.environ["HRC_VALIDATED"] = "1"
        # 启动真实 shell
        user_shell = os.environ.get("SHELL", "/bin/bash")
        os.execvpe(user_shell, [user_shell, "-l"], os.environ)
    else:
        log_attempt(f"FAIL ({reason})", user=user, source=source)
        print()
        print("❌ HRC 验证失败")
        print(f"   原因:{reason}")
        print(f"   时间窗口:±{HRC_WINDOW} 分钟")
        print()
        print("   请联系铸渊 ICE-GL-ZY001 获取最新 HRC")
        print("   或者从腾讯云 web 控制台 → VPS → 终端(绕过此拦截)")
        print()
        sys.exit(1)


if __name__ == "__main__":
    main()