diff --git a/scripts/lib/kdbx_parser.py b/scripts/lib/kdbx_parser.py index 6a2a354..4d026e3 100644 --- a/scripts/lib/kdbx_parser.py +++ b/scripts/lib/kdbx_parser.py @@ -1,9 +1,6 @@ import sys import xml.etree.ElementTree as ET -import base64 import os -import gzip -import zlib dump_file = sys.argv[1] cache_file = sys.argv[2] @@ -15,9 +12,16 @@ vault_addr = sys.argv[6] tree = ET.parse(dump_file) root = tree.getroot() +# Исключаем чтение старых версий паролей и удаленных записей +for h in root.findall(".//History"): + h.clear() +for g in root.findall(".//Group"): + name = g.find("./Name") + if name is not None and name.text == "Recycle Bin": + g.clear() + entries = {} -# Чтение актуальных записей (первое вхождение отсекает старый мусор из History) for entry in root.findall(".//Entry"): title = "" username = "" @@ -42,64 +46,23 @@ for entry in root.findall(".//Entry"): "notes": notes } -# Извлечение приватного SSH-ключа в RAM tmpfs +# Прямое извлечение SSH-ключа из текстового поля записи Atlas Core Key ssh_loaded = False - -# 1. Поиск во вложениях KeePass XML -for b in root.findall(".//Binary"): - raw_text = (b.text or "").strip() - if len(raw_text) < 50: - continue - try: - raw_bytes = base64.b64decode(raw_text) - key_bytes = None - - if raw_bytes.startswith(b"\x1f\x8b"): - key_bytes = gzip.decompress(raw_bytes) - elif b"-----BEGIN" in raw_bytes: - key_bytes = raw_bytes - elif len(raw_bytes) > 4: - # Срез 4-байтового заголовка длины Qt (qCompress) - try: - key_bytes = zlib.decompress(raw_bytes[4:]) - except Exception: - try: - key_bytes = zlib.decompress(raw_bytes) - except Exception: - key_bytes = raw_bytes - else: - key_bytes = raw_bytes - - if key_bytes and (b"PRIVATE KEY" in key_bytes or b"-----BEGIN" in key_bytes): - with open(key_file, "wb") as kf: - kf.write(key_bytes.strip() + b"\n") +for title in ("Atlas Core Key", "AtlasCoreKey", "Atlas Core"): + entry_data = entries.get(title, {}) + for field in ("password", "notes"): + val = entry_data.get(field, "") + if "-----BEGIN" in val or "PRIVATE KEY" in val: + with open(key_file, "w") as kf: + kf.write(val.strip() + "\n") os.chmod(key_file, 0o600) ssh_loaded = True break - except Exception: - pass + if ssh_loaded: + break -# 2. Поиск в текстовых полях записей Atlas Core if not ssh_loaded: - for title, data in entries.items(): - if "atlas" in title.lower() and "key" in title.lower(): - for field in ("password", "notes"): - val = data.get(field, "") - if "-----BEGIN" in val or "PRIVATE KEY" in val: - with open(key_file, "w") as kf: - kf.write(val.strip() + "\n") - os.chmod(key_file, 0o600) - ssh_loaded = True - break - if ssh_loaded: - break - -# Гарантия наличия файла для блокировки повторных запросов пароля -if not ssh_loaded: - sys.stderr.write("Внимание: приватный SSH-ключ не найден среди вложений KDBX.\n") - with open(key_file, "wb") as kf: - pass - os.chmod(key_file, 0o600) + sys.stderr.write("Внимание: приватный SSH-ключ не найден в полях Password/Notes записи Atlas Core Key.\n") with open(cache_file, "w") as f: f.write(f"# TTL_SECONDS={ttl_sec}\n") diff --git a/scripts/lib/session.sh b/scripts/lib/session.sh index 6fb62d0..1dafd45 100644 --- a/scripts/lib/session.sh +++ b/scripts/lib/session.sh @@ -8,8 +8,8 @@ load_secrets_to_ram() { ensure_prerequisites - # Валидация сессии строго по кэшу в RAM - if [[ -f "${RAM_CACHE}" ]]; then + # Валидация сессии по наличию непустых кэша секретов и SSH-ключа + if [[ -f "${RAM_CACHE}" ]] && [[ -f "${RAM_KEY}" ]] && [[ -s "${RAM_KEY}" ]]; then local created age cached_ttl created=$(stat -c %Y "${RAM_CACHE}" 2>/dev/null || echo 0) age=$(( now - created )) @@ -40,6 +40,7 @@ load_secrets_to_ram() { log_info "YubiKey обнаружен. Приготовьтесь к аппаратному подтверждению (1 касание)..." fi + # Атомарный 1-Touch экспорт всей базы KDBX в RAM (ровно 1 касание YubiKey на всю сессию) local tmp_dump="/dev/shm/.kdbx_dump_${now}_$$.xml" rm -f "${tmp_dump}" touch "${tmp_dump}"