summaryrefslogtreecommitdiffstats
path: root/src/utils/probe.py
blob: c8218f266680be3cac77e7b69d307fc445d98827 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#
# Copyright (C) 2020-2024 Soleta Networks <info@soleta.eu>
#
# 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)