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("&", "&").replace("<", "<").replace(""", "\"", ) \ .replace(">", ">").replace(''', "'") # 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
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
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
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)}
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)}
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
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("&", "&").replace("<", "<").replace(""", "\"", ) \ .replace(">", ">").replace(''', "'") # 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))
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