Exemple #1
0
def upload_xml(xml_file, path):
    """
    Upload XML files (rules and decoders)
    :param xml_file: content of the XML file
    :param path: Destination of the new XML file
    :return: Confirmation message
    """
    # -- characters are not allowed in XML comments
    xml_file = replace_in_comments(xml_file, '--', '%wildcard%')

    # path of temporary files for parsing xml input
    tmp_file_path = '{}/tmp/api_tmp_file_{}_{}.xml'.format(common.ossec_path, time.time(), random.randint(0, 1000))

    # create temporary file for parsing xml input
    try:
        with open(tmp_file_path, 'w') as tmp_file:
            # beauty xml file
            xml = parseString('<root>' + xml_file + '</root>')
            # remove first line (XML specification: <? xmlversion="1.0" ?>), <root> and </root> tags, and empty lines
            indent = '  '  # indent parameter for toprettyxml function
            pretty_xml = '\n'.join(filter(lambda x: x.strip(), xml.toprettyxml(indent=indent).split('\n')[2:-2])) + '\n'
            # revert xml.dom replacings
            # (https://github.com/python/cpython/blob/8e0418688906206fe59bd26344320c0fc026849e/Lib/xml/dom/minidom.py#L305)
            pretty_xml = pretty_xml.replace("&amp;", "&").replace("&lt;", "<").replace("&quot;", "\"", ) \
                .replace("&gt;", ">").replace('&apos;', "'")
            # delete two first spaces of each line
            final_xml = re.sub(fr'^{indent}', '', pretty_xml, flags=re.MULTILINE)
            final_xml = replace_in_comments(final_xml, '%wildcard%', '--')
            tmp_file.write(final_xml)
        chmod(tmp_file_path, 0o660)
    except IOError:
        raise WazuhInternalError(1005)
    except ExpatError:
        raise WazuhError(1113)

    try:
        # check xml format
        try:
            load_wazuh_xml(tmp_file_path)
        except Exception as e:
            raise WazuhError(1113, str(e))

        # move temporary file to group folder
        try:
            new_conf_path = join(common.ossec_path, path)
            safe_move(tmp_file_path, new_conf_path, permissions=0o660)
        except Error:
            raise WazuhInternalError(1016)

        return WazuhResult({'message': 'File updated successfully'})

    except Exception as e:
        # remove created temporary file if an exception happens
        remove(tmp_file_path)
        raise e
Exemple #2
0
def load_decoders_from_file(decoder_file, decoder_path, decoder_status):
    try:
        decoders = list()
        position = 0
        root = load_wazuh_xml(os.path.join(common.wazuh_path, decoder_path, decoder_file))

        for xml_decoder in list(root):
            # New decoder
            if xml_decoder.tag.lower() == "decoder":
                decoder = {'filename': decoder_file, 'relative_dirname': decoder_path, 'status': decoder_status,
                           'name': xml_decoder.attrib['name'], 'position': position, 'details': dict()}
                position += 1

                for k in xml_decoder.attrib:
                    if k != 'name':
                        decoder['details'][k] = xml_decoder.attrib[k]

                for xml_decoder_tags in list(xml_decoder):
                    tag = xml_decoder_tags.tag.lower()
                    value = xml_decoder_tags.text
                    attribs = xml_decoder_tags.attrib
                    if tag in DYNAMIC_OPTIONS:
                        add_dynamic_detail(tag, value, attribs, decoder['details'])
                    else:
                        decoder['details'][tag] = value
                decoders.append(decoder)
    except OSError:
        raise WazuhError(1502, extra_message=os.path.join('WAZUH_HOME', decoder_path, decoder_file))
    except Exception:
        raise WazuhInternalError(1501, extra_message=os.path.join('WAZUH_HOME', decoder_path, decoder_file))

    return decoders
Exemple #3
0
def get_ossec_conf(section=None,
                   field=None,
                   conf_file=common.ossec_conf,
                   from_import=False):
    """Returns ossec.conf (manager) as dictionary.

    Parameters
    ----------
    section : str
        Filters by section (i.e. rules).
    field : str
        Filters by field in section (i.e. included).
    conf_file : str
        Path of the configuration file to read.
    from_import : bool
        This flag indicates whether this function has been called from a module load (True) or from a function (False).

    Returns
    -------
    dict
        ossec.conf (manager) as dictionary.
    """
    try:
        # Read XML
        xml_data = load_wazuh_xml(conf_file)

        # Parse XML to JSON
        data = _ossecconf2json(xml_data)
    except Exception as e:
        if not from_import:
            raise WazuhError(1101, extra_message=str(e))
        else:
            print(
                f"wazuh-apid: There is an error in the ossec.conf file: {str(e)}"
            )
            sys.exit(0)

    if section:
        try:
            data = {section: data[section]}
        except KeyError as e:
            if section not in conf_sections.keys():
                raise WazuhError(1102, extra_message=e.args[0])
            else:
                raise WazuhError(1106, extra_message=e.args[0])

    if section and field:
        try:
            if isinstance(data[section], list):
                data = {
                    section: [{
                        field: item[field]
                    } for item in data[section]]
                }
            else:
                data = {section: {field: data[section][field]}}
        except KeyError:
            raise WazuhError(1103)

    return data
Exemple #4
0
def get_agent_conf_multigroup(multigroup_id=None, offset=0, limit=common.database_limit, filename=None):
    """
    Returns agent.conf as dictionary.

    :return: agent.conf as dictionary.
    """
    # Check if a multigroup_id is provided and it exists
    if multigroup_id and not os_path.exists(os_path.join(common.multi_groups_path, multigroup_id)) or not multigroup_id:
        raise WazuhResourceNotFound(1710, extra_message=multigroup_id if multigroup_id else "No multigroup provided")

    agent_conf_name = filename if filename else 'agent.conf'
    agent_conf = os_path.join(common.multi_groups_path, multigroup_id, agent_conf_name)

    if not os_path.exists(agent_conf):
        raise WazuhError(1006, extra_message=os_path.join("WAZUH_PATH", "var", "multigroups", agent_conf))

    try:
        # Read XML
        xml_data = load_wazuh_xml(agent_conf)

        # Parse XML to JSON
        data = _agentconf2json(xml_data)
    except Exception:
        raise WazuhError(1101)

    return {'totalItems': len(data), 'items': cut_array(data, offset=offset, limit=limit)}
Exemple #5
0
def get_agent_conf(group_id=None, offset=0, limit=common.database_limit, filename='agent.conf', return_format=None):
    """
    Returns agent.conf as dictionary.

    :return: agent.conf as dictionary.
    """
    if not os_path.exists(os_path.join(common.shared_path, group_id)):
        raise WazuhResourceNotFound(1710, group_id)
    agent_conf = os_path.join(common.shared_path, group_id if group_id is not None else '', filename)

    if not os_path.exists(agent_conf):
        raise WazuhError(1006, agent_conf)

    try:
        # Read RAW file
        if filename == 'agent.conf' and return_format and 'xml' == return_format.lower():
            with open(agent_conf, 'r') as xml_data:
                data = xml_data.read()
                return data
        # Parse XML to JSON
        else:
            # Read XML
            xml_data = load_wazuh_xml(agent_conf)

            data = _agentconf2json(xml_data)
    except Exception as e:
        raise WazuhError(1101, str(e))

    return {'total_affected_items': len(data), 'affected_items': cut_array(data, offset=offset, limit=limit)}
Exemple #6
0
def load_rules_from_file(rule_filename, rule_relative_path, rule_status):
    try:
        rules = list()
        root = load_wazuh_xml(os.path.join(common.ossec_path, rule_relative_path, rule_filename))

        for xml_group in list(root):
            if xml_group.tag.lower() == "group":
                general_groups = xml_group.attrib['name'].split(',')
                for xml_rule in list(xml_group):
                    # New rule
                    if xml_rule.tag.lower() == "rule":
                        groups = list()
                        rule = {'filename': rule_filename, 'relative_dirname': rule_relative_path,
                                'id': int(xml_rule.attrib['id']), 'level': int(xml_rule.attrib['level']),
                                'status': rule_status, 'details': dict(), 'pci_dss': list(), 'gpg13': list(),
                                'gdpr': list(), 'hipaa': list(), 'nist_800_53': list(), 'tsc': list(), 'mitre': list(),
                                'groups': list(), 'description': ''}
                        for k in xml_rule.attrib:
                            if k != 'id' and k != 'level':
                                rule['details'][k] = xml_rule.attrib[k]

                        for xml_rule_tags in list(xml_rule):
                            tag = xml_rule_tags.tag.lower()
                            value = xml_rule_tags.text
                            if value is None:
                                value = ''
                            if tag == "group":
                                groups.extend(value.split(","))
                            elif tag == "mitre":
                                for mitre_id in list(xml_rule_tags):
                                    groups.append(f'mitre_{mitre_id.text}')
                            elif tag == "description":
                                rule['description'] += value
                            elif tag == "field":
                                add_detail(xml_rule_tags.attrib['name'], value, rule['details'])
                            elif tag in ("list", "info"):
                                list_detail = {'name': value}
                                for attrib, attrib_value in xml_rule_tags.attrib.items():
                                    list_detail[attrib] = attrib_value
                                add_detail(tag, list_detail, rule['details'])
                            # show rule variables
                            elif tag in {'regex', 'match', 'user', 'id'} and value != '' and value[0] == "$":
                                for variable in filter(lambda x: x.get('name') == value[1:], root.findall('var')):
                                    add_detail(tag, variable.text, rule['details'])
                            else:
                                add_detail(tag, value, rule['details'])

                        # Set groups
                        set_groups(groups=groups, general_groups=general_groups, rule=rule)
                        rules.append(rule)
    except OSError as e:
        if e.errno == 2:
            raise WazuhError(1201)
        elif e.errno == 13:
            raise WazuhError(1207)
        else:
            raise e

    return rules
Exemple #7
0
def prettify_xml(xml_file):
    """Prettify XML files (rules, decoders and ossec.conf)

    Parameters
    ----------
    xml_file : str
        Content of the XML file

    Returns
    -------
    Checked XML content
    """
    # -- characters are not allowed in XML comments
    xml_file = replace_in_comments(xml_file, '--', '%wildcard%')

    # create temporary file for parsing xml input
    try:
        # beauty xml file
        xml = parseString('<root>' + xml_file + '</root>')
        # remove first line (XML specification: <? xmlversion="1.0" ?>), <root> and </root> tags, and empty lines
        indent = '  '  # indent parameter for toprettyxml function
        pretty_xml = '\n'.join(
            filter(lambda x: x.strip(),
                   xml.toprettyxml(indent=indent).split('\n')[2:-2])) + '\n'
        # revert xml.dom replacings
        # (https://github.com/python/cpython/blob/8e0418688906206fe59bd26344320c0fc026849e/Lib/xml/dom/minidom.py#L305)
        pretty_xml = pretty_xml.replace("&amp;", "&").replace("&lt;", "<").replace("&quot;", "\"", ) \
            .replace("&gt;", ">").replace('&apos;', "'")
        # delete two first spaces of each line
        final_xml = re.sub(fr'^{indent}', '', pretty_xml, flags=re.MULTILINE)
        final_xml = replace_in_comments(final_xml, '%wildcard%', '--')

        # Check if remote commands are allowed
        check_remote_commands(final_xml)
        # Check xml format
        load_wazuh_xml(xml_path='', data=final_xml)

        return final_xml
    except ExpatError:
        raise WazuhError(1113)
    except WazuhError as e:
        raise e
    except Exception as e:
        raise WazuhError(1113, str(e))
Exemple #8
0
def get_ossec_conf(section=None, field=None, conf_file=common.ossec_conf):
    """
    Returns ossec.conf (manager) as dictionary.

    :param section: Filters by section (i.e. rules).
    :param field: Filters by field in section (i.e. included).
    :param conf_file: Path of the configuration file to read.
    :return: ossec.conf (manager) as dictionary.
    """
    try:
        # Read XML
        xml_data = load_wazuh_xml(conf_file)

        # Parse XML to JSON
        data = _ossecconf2json(xml_data)
    except Exception as e:
        raise WazuhError(1101, extra_message=str(e))

    if section:
        try:
            data = {section: data[section]}
        except KeyError as e:
            if section not in conf_sections.keys():
                raise WazuhError(1102, extra_message=e.args[0])
            else:
                raise WazuhError(1106, extra_message=e.args[0])

    if section and field:
        try:
            if isinstance(data[section], list):
                data = {
                    section: [{
                        field: item[field]
                    } for item in data[section]]
                }
            else:
                data = {section: {field: data[section][field]}}
        except KeyError:
            raise WazuhError(1103)

    return data