# # Copyright (C) 2020-2024 Soleta Networks # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Affero General Public License as published by the # Free Software Foundation; either version 3 of the License, or # (at your option) any later version. import os import subprocess import platform import logging import sys from src.utils.winreg import * from enum import Enum from subprocess import PIPE from src.utils.fs import find_mountpoint OSFamily = Enum('OSFamily', ['LINUX', 'WINDOWS', 'UNKNOWN']) def getlinuxversion(osrelease): """ Parses a os-release file to fetch 'PRETTY_NAME' key. If file or key are not found, then returns generic 'Linux' string. """ mountpoint = find_mountpoint(osrelease) try: with open(osrelease, 'r') as f: for line in f: if line.find('=') == -1: continue key, value = line.split('=') if key == 'PRETTY_NAME': value = value.replace('\n', '') value = value.strip('"') bits = ' 64 bits' if linux_is64bit(mountpoint) else '' return f'{value}{bits}' except FileNotFoundError as e: logging.error(f'os-release file not found at "{osrelease}"') return 'Linux' def getwindowsversion(winreghives): """ Try to obtain windows version information by querying the SOFTWARE registry hive to fetch ProductName and ReleaseId. Return a generic "Microsoft Windows" string if something fails. """ try: hivepath = f'{winreghives}/SOFTWARE' hive = hive_handler_open(hivepath, write = False) root_node = hive.root() version_node = get_node_child_from_path(hive, root_node, 'Microsoft/Windows NT/CurrentVersion') prodname = get_value_from_node(hive, version_node, 'ProductName') releaseid = get_value_from_node(hive, version_node, 'ReleaseId') return f'{prodname} {releaseid}' except (RuntimeError, OgError) as e: logging.error(f'Hivex was not able to operate over {hivepath}. Reported: {e}') return 'Microsoft Windows' def interpreter_is64bit(): return sys.maxsize > 2**32 def linux_is64bit(mountpoint): """ If /sbin/init is detected, check if compiled for 64-bit machine. If init executable is not found, check for /lib64. If /lib64 is found returns True, otherwise False. """ init_path = f'{mountpoint}/sbin/init' lib64_path = f'{mountpoint}/lib64' if os.path.exists(init_path): bits, linkage = platform.architecture(executable=init_path) return True if bits == '64bit' else False return os.path.exists(lib64_path) def get_linux_distro_id(mountpoint): """ Parses a os-release file and fetches the 'ID' key. Check repository documentation at ./ogclient/os-release-ids.txt for a list of the potential strings it might return. If file or key are not found, then returns generic 'linux' string. """ osrelease = f'{mountpoint}/etc/os-release' try: with open(osrelease, 'r') as f: for line in f: if line.find('=') == -1: continue key, value = line.split('=') if key == 'ID': value = value.replace('\n', '') value = value.strip('"') return value except FileNotFoundError as e: logging.error(f'os-release file not found at "{osrelease}"') return 'linux' def get_cache_dev_path(): """ Runs 'blkid -L CACHE' and returns stripped path to device, eg. /dev/sda3 """ proc_blkid = subprocess.run(['blkid', '-L', 'CACHE'], stdout=subprocess.PIPE) if proc_blkid.returncode != 0: return '' return proc_blkid.stdout.decode().strip() def get_os_family(mountpoint): winreghives = f'{mountpoint}/Windows/System32/' osrelease = f'{mountpoint}/etc/os-release' if os.path.exists(osrelease): return OSFamily.LINUX elif os.path.exists(winreghives): return OSFamily.WINDOWS else: return OSFamily.UNKNOWN def os_probe(mountpoint): """ Probes mountpoint for typical OS dependant files. Windows: Tries finding and querying the software registry hive. Linux: Looks for /etc/os-release file. Returns a string depending on the OS it detects. """ winreghives = f'{mountpoint}{WINDOWS_HIVES_PATH}' osrelease = f'{mountpoint}/etc/os-release' if os.path.exists(osrelease): return getlinuxversion(osrelease) elif os.path.exists(winreghives): return getwindowsversion(winreghives) else: return 'unknown' def is_hibernation_enabled(mountpoint): if get_os_family(mountpoint) != OSFamily.WINDOWS: return False hiberfile_path = f'{mountpoint}/hiberfil.sys' return os.path.exists(hiberfile_path)