Example #1
0
def get_ossec_conf(section=None, field=None, filename=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).
    :return: ossec.conf (manager) as dictionary.
    """

    try:
        # Read XML
        xml_data = load_ossec_xml(filename)

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

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

    if section and field:
        try:
            data = data[field]  # data[section][field]
        except:
            raise OssecAPIException(1103)

    return data
Example #2
0
def cut_array(array, offset, limit):
    """
    Returns a part of the array: from offset to offset + limit.
    :param array: Array to cut.
    :param offset: First element to return.
    :param limit: Maximum number of elements to return. 0 means no cut array.
    :return: cut array.
    """

    if limit is not None:
        if limit > common.maximum_database_limit:
            raise OssecAPIException(1405, str(limit))
        elif limit == 0:
            raise OssecAPIException(1406)

    elif not array or limit is None:
        return array

    offset = int(offset)
    limit = int(limit)

    if offset < 0:
        raise OssecAPIException(1400)
    elif limit < 1:
        raise OssecAPIException(1401)
    else:
        return array[offset:offset + limit]
Example #3
0
def get_agent_conf(group_id=None,
                   offset=0,
                   limit=common.database_limit,
                   filename=None):
    """
    Returns agent.conf as dictionary.

    :return: agent.conf as dictionary.
    """
    if group_id:
        if not Agent.group_exists(group_id):
            raise OssecAPIException(1710, group_id)

        agent_conf = "{0}/{1}".format(common.shared_path, group_id)

    if filename:
        agent_conf_name = filename
    else:
        agent_conf_name = 'agent.conf'

    agent_conf += "/{0}".format(agent_conf_name)

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

    try:
        # Read XML
        xml_data = load_ossec_xml(agent_conf)

        # Parse XML to JSON
        data = _agentconf2json(xml_data)
    except Exception as e:
        raise OssecAPIException(1101, str(e))

    return {'totalItems': len(data), 'items': cut_array(data, offset, limit)}
Example #4
0
def parse_internal_options(high_name, low_name):
    def get_config(config_path):
        with open(config_path) as f:
            str_config = StringIO('[root]\n' + f.read())

        config = RawConfigParser()
        config.readfp(str_config)

        return config

    if not os_path.exists(common.internal_options):
        raise OssecAPIException(1107)

    # Check if the option exists at local internal options
    if os_path.exists(common.local_internal_options):
        try:
            return get_config(common.local_internal_options).get(
                'root', '{0}.{1}'.format(high_name, low_name))
        except NoOptionError:
            pass

    try:
        return get_config(common.internal_options).get(
            'root', '{0}.{1}'.format(high_name, low_name))
    except NoOptionError as e:
        raise OssecAPIException(1108, e.args[0])
Example #5
0
def execute(command):
    """
    Executes a command. It is used to execute ossec commands.
    """

    try:
        output = check_output(command)
    except CalledProcessError as error:
        output = error.output
    except Exception as e:
        raise OssecAPIException(1002, "{0}: {1}".format(
            command, e))  # Error executing command

    try:
        output_json = json.loads(output)
    except Exception as e:
        raise OssecAPIException(1003, command)  # Command output not in json

    keys = output_json.keys()  # error and (data or message)
    if 'error' not in keys or ('data' not in keys and 'message' not in keys):
        raise OssecAPIException(1004, command)  # Malformed command output

    if output_json['error'] != 0:
        raise OssecAPIException(output_json['error'], output_json['message'],
                                True)
    else:
        return output_json['data']
Example #6
0
    def _send(self, msg):
        try:
            sent = self.socket.send(msg)

            if sent == 0:
                raise OssecAPIException(1011, self.path)
        except:
            raise OssecAPIException(1011, self.path)
Example #7
0
 def send(self, msg):
     try:
         sent = self.s.send(dumps(msg))
         if sent == 0:
             raise OssecAPIException(1014, self.path)
         return sent
     except:
         raise OssecAPIException(1014, self.path)
Example #8
0
def sort_array(array, sort_by=None, order='asc', allowed_sort_fields=None):
    """
    Sorts an array.
    :param array: Array to sort.
    :param sort_by: Array of fields.
    :param order: asc or desc.
    :param allowed_sort_fields: Check sort_by with allowed_sort_fields (array).
    :return: sorted array.
    """
    def check_sort_fields(allowed_sort_fields, sort_by):
        # Check if every element in sort['fields'] is in allowed_sort_fields
        if not sort_by.issubset(allowed_sort_fields):
            incorrect_fields = ', '.join(sort_by - allowed_sort_fields)
            raise OssecAPIException(
                1403, 'Allowed sort fields: {0}. Fields: {1}'.format(
                    ', '.join(allowed_sort_fields), incorrect_fields))

    if not array:
        return array

    if order.lower() == 'desc':
        order_desc = True
    elif order.lower() == 'asc':
        order_desc = False
    else:
        raise OssecAPIException(1402)

    if allowed_sort_fields:
        check_sort_fields(set(allowed_sort_fields), set(sort_by))

    if sort_by:  # array should be a dictionary or a Class
        if type(array[0]) is dict:
            check_sort_fields(set(array[0].keys()), set(sort_by))

            return sorted(array,
                          key=lambda o: tuple(
                              o.get(a).lower()
                              if type(o.get(a)) in (str, unicode) else o.get(a)
                              for a in sort_by),
                          reverse=order_desc)
        else:
            return sorted(array,
                          key=lambda o: tuple(
                              getattr(o, a).lower() if type(getattr(o, a)) in
                              (str, unicode) else getattr(o, a)
                              for a in sort_by),
                          reverse=order_desc)
    else:
        if type(array) is set or (type(array[0]) is not dict and
                                  'class \'wazuh' not in str(type(array[0]))):
            return sorted(array, reverse=order_desc)
        else:
            raise OssecAPIException(1404)
Example #9
0
def get_file_conf(filename, group_id=None, type_conf=None):
    """
    Returns the configuration file as dictionary.

    :return: configuration file as dictionary.
    """

    if group_id:
        if not Agent.group_exists(group_id):
            raise OssecAPIException(1710, group_id)

        file_path = "{0}/{1}".format(common.shared_path, filename) \
                    if filename == 'ar.conf' else \
                    "{0}/{1}/{2}".format(common.shared_path, group_id, filename)
    else:
        file_path = "{0}/{1}".format(common.shared_path, filename)

    if not os_path.exists(file_path):
        raise OssecAPIException(1006, file_path)

    types = {
        'conf': get_agent_conf,
        'rootkit_files': _rootkit_files2json,
        'rootkit_trojans': _rootkit_trojans2json,
        'rcl': _rcl2json
    }

    data = {}
    if type_conf:
        if type_conf in types:
            if type_conf == 'conf':
                data = types[type_conf](group_id,
                                        limit=None,
                                        filename=filename)
            else:
                data = types[type_conf](file_path)
        else:
            raise OssecAPIException(
                1104, "{0}. Valid types: {1}".format(type_conf, types.keys()))
    else:
        if filename == "agent.conf":
            data = get_agent_conf(group_id, limit=None, filename=filename)
        elif filename == "rootkit_files.txt":
            data = _rootkit_files2json(file_path)
        elif filename == "rootkit_trojans.txt":
            data = _rootkit_trojans2json(file_path)
        elif filename == "ar.conf":
            data = _ar_conf2json(file_path)
        else:
            data = _rcl2json(file_path)

    return data
Example #10
0
def get_internal_options_value(high_name, low_name, max, min):
    option = parse_internal_options(high_name, low_name)
    if not option.isdigit():
        raise OssecAPIException(
            1109, 'Option: {}.{}. Value: {}'.format(high_name, low_name,
                                                    option))

    option = int(option)
    if option < min or option > max:
        raise OssecAPIException(
            1110, 'Max value: {}. Min value: {}. Found: {}.'.format(
                max, min, option))

    return option
Example #11
0
def run_command(agent_id=None, command=None, arguments=[], custom=False):
    """
    Run AR command.

    :param agent_id: Run AR command in the agent.
    :return: Message.
    """
    if not command:
        raise OssecAPIException(1650, "Command not specified")

    if not agent_id:
        raise OssecAPIException(1650, "Agent ID not specified")

    commands = get_commands()
    if not custom and command not in commands:
        raise OssecAPIException(1650, "Command not available")

    # Create message
    msg_queue = command
    if custom:
        msg_queue = "!{}".format(command)

    if arguments:
        msg_queue += " " + " ".join(shell_escape(str(x)) for x in arguments)
    else:
        msg_queue += " - -"

    # Send
    if agent_id == "000" or agent_id == "all":
        oq = OssecQueue(common.EXECQ)
        ret_msg = oq.send_msg_to_agent(msg=msg_queue, agent_id=agent_id, msg_type=OssecQueue.AR_TYPE)
        oq.close()

    if agent_id != "000" or agent_id == "all":

        if agent_id != "all":
            # Check if agent exists and it is active
            agent_info = Agent(agent_id).get_basic_information()

            if agent_info['status'].lower() != 'active':
                raise OssecAPIException(1651)

        if agent_id == "all":
            agent_id = None

        oq = OssecQueue(common.ARQUEUE)
        ret_msg = oq.send_msg_to_agent(msg=msg_queue, agent_id=agent_id, msg_type=OssecQueue.AR_TYPE)
        oq.close()

    return ret_msg
Example #12
0
    def receive(self):

        try:
            chunk = self.s.recv(OssecSocket.MAX_SIZE)
            response = loads(chunk)
        except:
            raise OssecAPIException(1014, self.path)

        if 'error' in response.keys():
            if response['error'] != 0:
                raise OssecAPIException(response['error'],
                                        response['message'],
                                        cmd_error=True)
            else:
                return response['data']
Example #13
0
    def __load_decoders_from_file(decoder_file, decoder_path, decoder_status):
        try:
            decoders = []
            position = 0

            # root = load_ossec_xml("{}/{}".format(decoder_path, decoder_file))
            root = load_ossec_xml("{}".format(decoder_path))
            for xml_decoder in root.getchildren():
                # New decoder
                if xml_decoder.tag.lower() == "decoder":
                    decoder = Decoder()
                    decoder.path = decoder_path
                    decoder.file = decoder_file
                    decoder.status = decoder_status
                    decoder.name = xml_decoder.attrib['name']
                    decoder.position = position
                    position += 1

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

                    for xml_decoder_tags in xml_decoder.getchildren():
                        decoder.add_detail(xml_decoder_tags.tag.lower(),
                                           xml_decoder_tags.text)

                    decoders.append(decoder)
        except Exception as e:
            raise OssecAPIException(
                1501, "{0}. Error: {1}".format(decoder_file, str(e)))

        return decoders
Example #14
0
    def _get_requirement(offset, limit, sort, search, requirement):
        """
        Get the requirements used in the rules

        :param offset: First item to return.
        :param limit: Maximum number of items to return.
        :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}.
        :param search: Looks for items with the specified string.
        :param requirement: requirement to get (pci or dgpr)
        :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)}
        """
        if requirement != 'pci' and requirement != 'gdpr':
            raise OssecAPIException(1205, requirement)

        req = list({req for rule in Rule.get_rules(limit=None)['items'] for req in rule.to_dict()[requirement]})

        if search:
            req = search_array(req, search['value'], search['negation'])

        if sort:
            req = sort_array(req, order=sort['order'])
        else:
            req = sort_array(req)

        return {'items': cut_array(req, offset, limit), 'totalItems': len(req)}
Example #15
0
 def __check_status(status):
     if status is None:
         return Rule.S_ALL
     elif status in [Rule.S_ALL, Rule.S_ENABLED, Rule.S_DISABLED]:
         return status
     else:
         raise OssecAPIException(1202)
Example #16
0
def _rootkit_trojans2json(filepath):
    """
    Returns the rootkit trojans file as dictionary.

    :return: rootkit trojans file as dictionary.
    """

    data = []

    # file_name !string_to_search!Description
    regex_comment = re.compile(r"^\s*#")
    regex_check = re.compile(r"^\s*(.+)\s+!\s*(.+)\s*!\s*(.+)")

    try:
        with open(filepath) as f:
            for line in f:
                if re.search(regex_comment, line):
                    continue

                match_check = re.search(regex_check, line)
                if match_check:
                    new_check = {
                        'filename': match_check.group(1).strip(),
                        'name': match_check.group(2).strip(),
                        'description': match_check.group(3).strip()
                    }
                    data.append(new_check)

    except Exception as e:
        raise OssecAPIException(1101, str(e))

    return data
Example #17
0
 def __check_status(status):
     if status is None:
         return Decoder.S_ALL
     elif status in [Decoder.S_ALL, Decoder.S_ENABLED, Decoder.S_DISABLED]:
         return status
     else:
         raise OssecAPIException(1202)
Example #18
0
 def check_sort_fields(allowed_sort_fields, sort_by):
     # Check if every element in sort['fields'] is in allowed_sort_fields
     if not sort_by.issubset(allowed_sort_fields):
         incorrect_fields = ', '.join(sort_by - allowed_sort_fields)
         raise OssecAPIException(
             1403, 'Allowed sort fields: {0}. Fields: {1}'.format(
                 ', '.join(allowed_sort_fields), incorrect_fields))
Example #19
0
def delete_pid(name, pid):
    filename = "{0}{1}/{2}-{3}.pid".format(common.ossec_path, common.os_pidfile, name, pid)
    try:
        if os.path.exists(filename):
            os.unlink(filename)
    except OSError as e:
        raise OssecAPIException(3003, str(e))
Example #20
0
def clear(agent_id=None, all_agents=False):
    """
    Clears the database.

    :param agent_id: For an agent.
    :param all_agents: For all agents.
    :return: Message.
    """

    # Clear DB
    conn = Connection(common.database_path)

    regex = re.compile(r'^\d{,3}-\S+$')
    db_agents_list = []

    if not int(all_agents):
        raw_str = r'^' + "{}".format(int(agent_id)).zfill(3) + r'-\S+$'
        regex = re.compile(raw_str)

    for db_agent in conn.getDbsName():
        if (regex.search(db_agent) != None):
            db_agents_list.append(db_agent)

    if (db_agents_list.count() <= 0):
        raise OssecAPIException(1600)

    for db_agent in db_agents_list:
        conn.connect(db_agent)
        if conn.getDb() != None:
            doc = conn.getDb()['pm_event']
            if doc != None:
                doc.drop()
                conn.vacuum()
            doc = conn.getDb()['pmCounterInfo']
            if doc != None:
                doc.drop()
                conn.vacuum()

    # Clear OSSEC info
    if int(all_agents):
        rootcheck_files = glob('{0}/queue/rootcheck/*'.format(
            common.ossec_path))
    else:
        if agent_id == "000":
            rootcheck_files = [
                '{0}/queue/rootcheck/rootcheck'.format(common.ossec_path)
            ]
        else:
            agent_info = Agent(agent_id).get_basic_information()
            rootcheck_files = glob(
                '{0}/queue/rootcheck/({1}) {2}->rootcheck'.format(
                    common.ossec_path, agent_info['name'], agent_info['ip']))

    for rootcheck_file in rootcheck_files:
        if path.exists(rootcheck_file):
            remove(rootcheck_file)

    return "Rootcheck database deleted"
Example #21
0
def create_pid(name, pid):
    filename = "{0}{1}/{2}-{3}.pid".format(common.ossec_path, common.os_pidfile, name, pid)

    with open(filename, 'a') as fp:
        try:
            fp.write("{0}\n".format(pid))
            os.chmod(filename, 0o640)
        except OSError as e:
            raise OssecAPIException(3002, str(e))
Example #22
0
def run(agent_id=None, all_agents=False):
    """
    Runs rootcheck and syscheck.

    :param agent_id: Run rootcheck/syscheck in the agent.
    :param all_agents: Run rootcheck/syscheck in all agents.
    :return: Message.
    """

    if agent_id == "000" or all_agents:
        try:
            SYSCHECK_RESTART = "{0}/var/run/.syscheck_run".format(
                common.ossec_path)

            fp = open(SYSCHECK_RESTART, 'w')
            fp.write('{0}\n'.format(SYSCHECK_RESTART))
            fp.close()
            ret_msg = "Restarting Syscheck/Rootcheck locally"
        except:
            raise OssecAPIException(1601, "locally")

        if all_agents:
            oq = OssecQueue(common.ARQUEUE)
            ret_msg = oq.send_msg_to_agent(OssecQueue.HC_SK_RESTART)
            oq.close()
    else:
        # Check if agent exists and it is active
        agent_info = Agent(agent_id).get_basic_information()
        if 'status' in agent_info:
            agent_status = agent_info['status']
        else:
            agent_status = "N/A"

        if agent_status.lower() != 'active':
            raise OssecAPIException(1602,
                                    '{0} - {1}'.format(agent_id, agent_status))

        oq = OssecQueue(common.ARQUEUE)
        ret_msg = oq.send_msg_to_agent(OssecQueue.HC_SK_RESTART, agent_id)
        oq.close()

    return ret_msg
Example #23
0
 def _connect(self):
     try:
         self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
         self.socket.connect(self.path)
         length_send_buffer = self.socket.getsockopt(
             socket.SOL_SOCKET, socket.SO_SNDBUF)
         if length_send_buffer < OssecQueue.MAX_MSG_SIZE:
             self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF,
                                    OssecQueue.MAX_MSG_SIZE)
     except:
         raise OssecAPIException(1010, self.path)
Example #24
0
def last_scan(agent_id):
    """
    Gets the last scan of the agent.

    :param agent_id: Agent ID.
    :return: Dictionary: end, start.
    """

    # Connection
    db_url = common.database_path
    conn = Connection(db_url)
    conn.connect(conn.getDbById(str(agent_id).zfill(3)))
    if (conn.getDb() == None):
        raise OssecAPIException(1600)

    data = {}

    lastSyscheckEndTime = None
    lastSyscheckEndTimeObj = list((conn.getDb()['pm_event'].find({
        "log":
        'Ending syscheck scan.'
    }).sort([('date_last', -1)]).limit(1)))[0]
    if (lastSyscheckEndTimeObj != None):
        lastSyscheckEndTime = lastSyscheckEndTimeObj.get('date_last')

    if lastSyscheckEndTime != None:
        data['end'] = (lastSyscheckEndTime +
                       timedelta(seconds=timeoffset)).__str__()
    else:
        data['end'] = lastSyscheckEndTime.__str__()

    lastSyscheckStartTime = None
    lastSyscheckStartTimeObj = list((conn.getDb()['pm_event'].find({
        "log":
        'Starting syscheck scan.'
    }).sort([('date_last', -1)]).limit(1)))[0]
    if (lastSyscheckStartTimeObj != None):
        lastSyscheckStartTime = lastSyscheckStartTimeObj.get('date_last')

    if lastSyscheckStartTime != None:
        data['start'] = (lastSyscheckStartTime +
                         timedelta(seconds=timeoffset)).__str__()
    else:
        data['start'] = lastSyscheckStartTime.__str__()

    return data
Example #25
0
def _rcl2json(filepath):
    """
    Returns the RCL file as dictionary.

    :return: rcl file (system_audit, windows_audit) as dictionary.
    """

    data = {'vars': {}, 'controls': []}
    # [Application name] [any or all] [reference]
    # type:<entry name>;
    regex_comment = re.compile(r"^\s*#")
    regex_title = re.compile(r"^\s*\[(.*)\]\s*\[(.*)\]\s*\[(.*)\]\s*")
    regex_name_groups = re.compile(r"(\{\w+:\s+\S+\s*\S*\})")
    regex_check = re.compile(r"^\s*(\w:.+)")
    regex_var = re.compile(r"^\s*\$(\w+)=(.+)")

    try:
        item = {}

        with open(filepath) as f:
            for line in f:
                if re.search(regex_comment, line):
                    continue

                match_title = re.search(regex_title, line)
                if match_title:
                    # Previous
                    if item:
                        data['controls'].append(item)

                    # New
                    name = match_title.group(1)
                    condition = match_title.group(2)
                    reference = match_title.group(3)

                    item = {}

                    # Name
                    end_name = name.find('{')
                    item['name'] = name[:end_name].strip()

                    # Extract PCI and CIS from name
                    name_groups = re.findall(regex_name_groups, name)

                    cis = []
                    pci = []
                    if name_groups:

                        for group in name_groups:
                            # {CIS: 1.1.2 RHEL7}
                            g_value = group.split(':')[-1][:-1].strip()
                            if 'CIS' in group:
                                cis.append(g_value)
                            elif 'PCI' in group:
                                pci.append(g_value)

                    if cis:
                        item['cis'] = cis
                    if pci:
                        item['pci'] = pci

                    # Conditions
                    if condition:
                        item['condition'] = condition
                    if reference:
                        item['reference'] = reference
                    item['checks'] = []

                    continue

                match_checks = re.search(regex_check, line)
                if match_checks:
                    item['checks'].append(match_checks.group(1))
                    continue

                match_var = re.search(regex_var, line)
                if match_var:
                    data['vars'][match_var.group(1)] = match_var.group(2)
                    continue

            # Last item
            data['controls'].append(item)

    except Exception as e:
        raise OssecAPIException(1101, str(e))

    return data
Example #26
0
def print_db(agent_id=None,
             status='all',
             pci=None,
             offset=0,
             limit=common.database_limit,
             sort=None,
             search=None):
    """
    Returns a list of events from the database.

    :param agent_id: Agent ID.
    :param status: Filters by status: outstanding, solved, all.
    :param pci: Filters by PCI DSS requirement.
    :param cis: Filters by CIS.
    :param offset: First item to return.
    :param limit: Maximum number of items to return.
    :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}.
    :param search: Looks for items with the specified string.
    :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)}
    """
    # Connection
    db_url = common.database_path
    conn = Connection(db_url)
    conn.connect(conn.getDbById(str(agent_id).zfill(3)))
    if (conn.getDb() == None):
        raise OssecAPIException(1600)

    request = {"$and": []}

    lastRootcheckEndTime = None
    lastRootcheckEndTimeObj = list((conn.getDb()['pm_event'].find({
        "log":
        'Ending rootcheck scan.'
    }).sort([('date_last', -1)]).limit(1)))[0]
    if (lastRootcheckEndTimeObj != None):
        lastRootcheckEndTime = lastRootcheckEndTimeObj.get(
            'date_last', datetime.now())

    fields = {
        'status': 'status',
        'event': 'log',
        'oldDay': 'date_first',
        'readDay': 'date_last'
    }

    request['$and'].append({
        'log': {
            '$nin': [
                'Starting rootcheck scan.', 'Ending rootcheck scan.',
                'Starting syscheck scan.', 'Ending syscheck scan.'
            ]
        }
    })
    if status == 'outstanding':
        if lastRootcheckEndTime != None:
            request['$and'].append({
                'date_last': {
                    '$gt': (lastRootcheckEndTime - timedelta(seconds=86400))
                }
            })
    elif status == 'solved':
        if lastRootcheckEndTime != None:
            request['$and'].append({
                'date_last': {
                    '$lte': (lastRootcheckEndTime - timedelta(seconds=86400))
                }
            })

    if pci:
        request["$and"].append({"pci_dss": pci})

    # search
    if search:
        regex = re.compile(".*{0}.*".format(int(search['value']) if search['value'].isdigit() \
                                                                    else search['value']), re.IGNORECASE)
        search_con = {"$or": []}

        for x in fields.values():
            search_con["$or"].append({x: regex})
        if bool(search['negation']):
            if search_con["$or"]:
                request["$and"].append({"$not": search_con})
        else:
            if search_con["$or"]:
                request["$and"].append(search_con)

    # Sorting
    sort_con = []
    if sort:
        if sort['fields']:
            allowed_sort_fields = set(fields.keys())
            # Check if every element in sort['fields'] is in allowed_sort_fields
            if not set(sort['fields']).issubset(allowed_sort_fields):
                uncorrect_fields = list(
                    map(lambda x: str(x),
                        set(sort['fields']) - set(allowed_sort_fields)))
                raise OssecAPIException(
                    1403, 'Allowed sort fields: {0}. Fields: {1}'.format(
                        allowed_sort_fields, uncorrect_fields))

            for i in sort['fields']:
                str_order = 1 if sort['order'] == 'asc' else -1
                sort_con.append((Agent.fields[i], str_order))
        else:
            sort_con.append(
                (fields["readDay"], 1 if sort['order'] == 'asc' else -1))
    else:
        sort_con.append((fields["readDay"], -1))

    if limit:
        if limit > common.maximum_database_limit:
            raise OssecAPIException(1405, str(limit))
    elif limit == 0:
        raise OssecAPIException(1406)

    select = ["status", "date_first", "date_last", "log", "pci_dss"]
    select_fields = {}
    for x in set(select):
        select_fields[x] = 1

    if not request["$and"]:
        request = {}

    data = {}
    db_data = conn.getDb()['pm_event'].find(request, select_fields)
    data['totalItems'] = db_data.count()
    db_data = db_data.sort(sort_con).skip(offset).limit(limit)

    # process get data
    data['items'] = []

    for pmEvent in db_data:
        pmEvent.pop('_id')
        if pmEvent.get("date_last") != None:
            if (pmEvent['date_last'] > lastRootcheckEndTime):
                pmEvent['status'] = 'outstanding'
            elif (pmEvent['date_last'] <= lastRootcheckEndTime):
                pmEvent['status'] = 'solved'

        if pmEvent.get("date_first") != None:
            pmEvent['date_first'] = (pmEvent.get("date_first") +
                                     timedelta(seconds=timeoffset)).__str__()
        else:
            pmEvent['date_first'] = pmEvent.get("date_first").__str__()

        if pmEvent.get("date_last") != None:
            pmEvent['date_last'] = (pmEvent.get("date_last") +
                                    timedelta(seconds=timeoffset)).__str__()
        else:
            pmEvent['date_last'] = pmEvent.get("date_last").__str__()

        data['items'].append(pmEvent)
    return data
Example #27
0
def files(agent_id=None,
          event=None,
          filename=None,
          filetype='file',
          md5=None,
          sha1=None,
          hash=None,
          summary=False,
          offset=0,
          limit=common.database_limit,
          sort=None,
          search=None):
    """
    Return a list of files from the database that match the filters

    :param agent_id: Agent ID.
    :param event: Filters by event: added, readded, modified, deleted.
    :param filename: Filters by filename.
    :param filetype: Filters by filetype: file or registry.
    :param md5: Filters by md5 hash.
    :param sha1: Filters by sha1 hash.
    :param hash: Filters by md5 or sha1 hash.
    :param summary: Returns a summary grouping by filename.
    :param offset: First item to return.
    :param limit: Maximum number of items to return.
    :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}.
    :param search: Looks for items with the specified string.
    :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)}
    """

    # Connection
    db_url = common.database_path
    conn = Connection(db_url)
    conn.connect(conn.getDbById(str(agent_id).zfill(3)))
    if (conn.getDb() == None):
        raise OssecAPIException(1600)

    agent_info = Agent(agent_id).get_basic_information()
    if 'os' in agent_info:
        if 'windows' in agent_info['os']['name'].lower():
            windows_agent = True
        else:
            windows_agent = False
    else:
        windows_agent = False

    # if 'os' in agent_info and 'platform' in agent_info['os']:
    #     if agent_info['os']['platform'].lower() == 'windows':
    #         windows_agent = True
    #     else:
    #         windows_agent = False
    # else:
    #     # We do not know if it is a windows or linux agent.
    #     # It is set to windows agent in order to avoid wrong data (uid, gid, ...)
    #     windows_agent = True

    eventRequest = {"$and": []}
    fileRequest = {"$and": []}

    eventFields = {
        'scanDate': 'date',
        'modificationDate': 'mtime',
        'size': 'size',
        'user': '******',
        'group': 'gname'
    }
    fileFields = {'file': 'path', 'filetype': 'type'}
    # Query
    # query = "SELECT {0} FROM fim_event, fim_file WHERE fim_event.id_file = fim_file.id AND fim_file.type = :filetype"

    # fileRequest['$and'].append({
    #     'type': filetype
    # })

    # if event:
    #     # query += ' AND fim_event.type = :event'
    #     # request['event'] = event
    #     eventRequest['$and'].append({
    #         'event': event
    #     })

    # if filename:
    #     # query += ' AND path = :filename'
    #     # request['filename'] = filename
    #     fileRequest['$and'].append({
    #         'path': filename
    #     })

    # if md5:
    #     # query += ' AND md5 = :md5'
    #     # request['md5'] = md5
    #     eventRequest['$and'].append({
    #         'md5': md5
    #     })

    # if sha1:
    #     # query += ' AND sha1 = :sha1'
    #     # request['sha1'] = sha1
    #     eventRequest['$and'].append({
    #         'sha1': sha1
    #     })

    # if hash:
    #     # query += ' AND (md5 = :hash OR sha1 = :hash)'
    #     # request['hash'] = hash
    #     eventRequest['$and'].append({
    #         '$or': [
    #             {
    #                 'md5': hash
    #             },
    #             {
    #                 'sha1': hash
    #             }
    #         ]
    #     })

    # if search:
    #     query += " AND NOT" if bool(search['negation']) else ' AND'
    #     query += " (" + " OR ".join(x + ' LIKE :search' for x in ('path', "date", 'size', 'md5', 'sha1', 'uname', 'gname', 'inode', 'perm')) + " )"
    #     request['search'] = '%{0}%'.format(search['value'])

    if search:
        regex = re.compile(".*{0}.*".format(int(search['value']) if search['value'].isdigit() \
                                                                    else search['value']), re.IGNORECASE)
        event_search_con = {"$or": []}
        file_search_con = {"$or": []}
        for x in [
                'path', "date", 'size', 'md5', 'sha1', 'uname', 'gname', 'perm'
        ]:
            if x == 'path':
                file_search_con["$or"].append({x: regex})
            else:
                event_search_con["$or"].append({x: regex})
        if bool(search['negation']):
            if event_search_con["$or"]:
                eventRequest["$and"].append({"$not": event_search_con})
            if file_search_con["$or"]:
                fileRequest["$and"].append({"$not": file_search_con})
        else:
            if event_search_con["$or"]:
                eventRequest["$and"].append(event_search_con)
            if file_search_con["$or"]:
                fileRequest["$and"].append(file_search_con)

    # Total items
    db_data = None

    events = []
    if summary:
        db_data = conn.getDb()['fim_file'].aggregate([
            {
                '$lookup': {
                    'from': 'fim_event',
                    'localField': '_id',
                    'foreignField': 'file_id',
                    'as': 'fim_events'
                }
            },
        ],
                                                     cursor={})
        for sysFile in db_data:
            item = sysFile
            for fEvent in item['fim_events']:
                if not item.get('fim_event'):
                    item['fim_event'] = fEvent
                else:
                    if fEvent['date'] > item['fim_event']['date']:
                        item['fim_event'] = fEvent
            item.pop('fim_events')
            if item['type'] != filetype:
                continue

            if event:
                if item['fim_event']['type'] != event:
                    continue
            if filename:
                if item['path'] != filename:
                    continue
            if md5:
                if item['fim_event']['md5'] != md5:
                    continue

            if sha1:
                if item['fim_event']['sha1'] != md5:
                    continue

            if hash:
                if (item['fim_event']['sha1'] !=
                        hash) and (item['fim_event']['md5'] != hash):
                    continue
            if search:
                search_value = int(search['value']) if search['value'].isdigit(
                ) else search['value']

                if (search_value not in item['path']) and (search_value not in item['fim_event']['date']) \
                and (search_value not in item['fim_event']['size']) and (search_value not in item['fim_event']['md5']) \
                and (search_value not in item['fim_event']['sha1']) and (search_value not in item['fim_event']['uname']) \
                and (search_value not in item['fim_event']['gname']) and (search_value not in item['fim_event']['perm']):
                    continue
            item['sha1'] = item['fim_event']['sha1']
            item['uid'] = item['fim_event']['uid']
            item['date'] = item['fim_event']['date']
            item['gid'] = item['fim_event']['gid']
            # item['mtime'] = item['fim_event']['mtime']
            item['perm'] = item['fim_event']['perm']
            item['md5'] = item['fim_event']['md5']
            item.pop('fim_event')
            item['fim_event.type'] = item['type']
            item.pop('type')
            events.append(item)

        # rFileRequest = fileRequest.copy()
        # if not rFileRequest['$and']:
        #     rFileRequest = {}
        # db_data = conn.getDb()['fim_file'].find_one(rFileRequest)
        # # list_db_data = list(db_data)
        # for eFile in db_data:
        #     rEventRequest = eventRequest.copy()
        #     rEventRequest['$and'].append({
        #         'file_id': eFile.get('_id')
        #     })
        #     event_data = conn.getDb()['fim_event'].find(rEventRequest).sort(('date', -1)).limit(1)
        #     if event_data.count() == 1:
        #         item = list(event_data)[0]
        #         item['type'] = eFile.get('type')
        #         item['type'] = eFile.get('path')
        #         print(item)
        #         events.append(item)

        # query += ' group by path'
        # conn.execute("SELECT COUNT(*) FROM ({0}) AS TEMP".format(query.format("max(date)")), request)
    else:
        db_data = conn.getDb()['fim_event'].aggregate([
            {
                '$lookup': {
                    'from': 'fim_file',
                    'localField': 'file_id',
                    'foreignField': '_id',
                    'as': 'fim_file'
                }
            },
        ],
                                                      cursor={})
        for sysEvent in db_data:
            if sysEvent['fim_file'][0]['type'] != filetype:
                continue

            if event:
                if sysEvent['type'] != event:
                    continue
            if filename:
                if sysEvent['fim_file'][0]['path'] != filename:
                    continue
            if md5:
                if sysEvent['md5'] != md5:
                    continue

            if sha1:
                if sysEvent['sha1'] != md5:
                    continue

            if hash:
                if (sysEvent['sha1'] != hash) and (sysEvent['md5'] != hash):
                    continue
            if search:
                search_value = int(search['value']) if search['value'].isdigit(
                ) else search['value']

                if (search_value not in sysEvent['fim_file'][0]['path']) and (search_value not in sysEvent['date']) \
                and (search_value not in sysEvent['size']) and (search_value not in sysEvent['md5']) \
                and (search_value not in sysEvent['sha1']) and (search_value not in sysEvent['uname']) \
                and (search_value not in sysEvent['gname']) and (search_value not in sysEvent['perm']):
                    continue
            item = sysEvent
            item['fim_event.type'] = item['fim_file'][0]['type']
            item['path'] = item['fim_file'][0]['path']
            # print(item)
            item.pop('_id')
            item.pop('fim_file')
            events.append(item)

    data = {'totalItems': len(events)}
    # Sorting
    event_sort_con = []
    file_sort_con = []
    if sort:
        if sort['fields']:
            allowed_sort_fields = set(eventFields.keys() + fileFields.keys())
            # Check if every element in sort['fields'] is in allowed_sort_fields
            if not set(sort['fields']).issubset(allowed_sort_fields):
                uncorrect_fields = list(
                    map(lambda x: str(x),
                        set(sort['fields']) - set(allowed_sort_fields)))
                raise OssecAPIException(
                    1403, 'Allowed sort fields: {0}. Fields: {1}'.format(
                        allowed_sort_fields, uncorrect_fields))

            for i in sort['fields']:
                # str_order = 1 if sort['order'] == 'asc' else -1
                sort_order = False if sort['order'] == 'asc' else True
                events.sort(key=lambda e: e[i], reverse=sort_order)
                # if i in eventFields.keys():
                #     event_sort_con.append((eventFields[i], str_order))
                #     events.sort(key=lambda e: e[i], reverse=sort_order)
                # elif i in fileFields.keys():
                #     file_sort_con.append((fileFields[i], str_order))
        else:
            # event_sort_con.append((eventFields["date"], 1 if sort['order'] == 'asc' else -1))
            sort_order = False if sort['order'] == 'asc' else True
            events.sort(key=lambda e: e['date'], reverse=sort_order)
    else:
        # event_sort_con.append((eventFields["date"], -1))
        events.sort(key=lambda e: e['date'], reverse=True)

    # if sort:
    #     if sort['fields']:
    #         allowed_sort_fields = fields.keys()
    #          # Check if every element in sort['fields'] is in allowed_sort_fields
    #         if not set(sort['fields']).issubset(allowed_sort_fields):
    #             uncorrect_fields = list(map(lambda x: str(x), set(sort['fields']) - set(allowed_sort_fields)))
    #             raise OssecAPIException(1403, 'Allowed sort fields: {0}. Fields: {1}'.format(allowed_sort_fields, uncorrect_fields))

    #         query += ' ORDER BY ' + ','.join(['{0} {1}'.format(fields[i], sort['order']) for i in sort['fields']])
    #     else:
    #         query += ' ORDER BY date {0}'.format(sort['order'])
    # else:
    #     query += ' ORDER BY date DESC'
    if limit:
        if limit > common.maximum_database_limit:
            raise OssecAPIException(1405, str(limit))
        # query += ' LIMIT :offset,:limit'
        # request['offset'] = offset
        # request['limit'] = limit
        if offset >= 0:
            events = events[int(offset):(int(offset) + int(limit))]
    elif limit == 0:
        raise OssecAPIException(1406)

    # if summary:
    #     select = ["max(date)", "mtime", "fim_event.type", "path"]
    # else:
    #     select = ["date", "mtime", "fim_event.type", "path", "size", "perm", "uid", "gid", "md5", "sha1"]

    data['items'] = []
    for fEvent in events:
        data_tuple = {}
        if fEvent.get('date') != None:
            data_tuple['scanDate'] = (fEvent.get('date') +
                                      timedelta(seconds=timeoffset)).__str__()
        else:
            data_tuple['scanDate'] = fEvent.get('date').__str__()
        # if fEvent.get('mtime') != None:
        #     data_tuple['modificationDate'] = (fEvent.get('mtime') + timedelta(seconds=timeoffset)).__str__()  # modificationDate
        # else:
        #     data_tuple['modificationDate'] = data_tuple['scanDate']  # scanDate
        if fEvent.get('fim_event.type') != None:
            data_tuple['event'] = fEvent.get('fim_event.type')
        if fEvent.get('path') != None:
            data_tuple['file'] = fEvent.get('path')

        if not summary:
            try:
                permissions = filemode(int(fEvent.get('perm'), 8))
            except TypeError:
                permissions = None

            if fEvent.get('size') != None:
                data_tuple['size'] = fEvent.get('size')
            if fEvent.get('md5') != None:
                data_tuple['md5'] = fEvent.get('md5')
            if fEvent.get('sha1') != None:
                data_tuple['sha1'] = fEvent.get('sha1')

            if not windows_agent:
                if fEvent.get('uid') != None:
                    data_tuple['uid'] = fEvent.get('uid')
                if fEvent.get('gid') != None:
                    data_tuple['gid'] = fEvent.get('gid')

                if fEvent.get('perm') != None:
                    data_tuple['octalMode'] = fEvent.get('perm')
                if permissions:
                    data_tuple['permissions'] = permissions

        data['items'].append(data_tuple)
    return data
Example #28
0
    def get_decoders_files(status=None,
                           path=None,
                           file=None,
                           offset=0,
                           limit=common.database_limit,
                           sort=None,
                           search=None):
        """
        Gets a list of the available decoder files.

        :param status: Filters by status: enabled, disabled, all.
        :param path: Filters by path.
        :param file: Filters by filename.
        :param offset: First item to return.
        :param limit: Maximum number of items to return.
        :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}.
        :param search: Looks for items with the specified string.
        :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)}
        """

        status = Decoder.__check_status(status)

        ruleset_conf = configuration.get_ossec_conf(section='rules')
        if not ruleset_conf:
            raise OssecAPIException(1500)

        tmp_data = []
        tags = ['decoder']
        exclude_filenames = []
        for tag in tags:
            if tag in ruleset_conf:
                item_status = Decoder.S_ENABLED

                if type(ruleset_conf[tag]) is list:
                    items = ruleset_conf[tag]
                else:
                    items = [ruleset_conf[tag]]

                for item in items:
                    if '/' in item:
                        item_split = item.split('/')
                        item_name = item_split[-1]
                        item_dir = "{0}/{1}".format(common.ossec_path,
                                                    "/".join(item_split[:-1]))
                    else:
                        item_name = item
                        item_dir = "{0}/{1}".format(common.ruleset_rules_path,
                                                    item)

                    tmp_data.append({
                        'file': item_name,
                        'path': item_dir,
                        'status': item_status
                    })

        tag = 'decoder_dir'
        if tag in ruleset_conf:
            if type(ruleset_conf[tag]) is list:
                items = ruleset_conf[tag]
            else:
                items = [ruleset_conf[tag]]

            for item_dir in items:
                all_decoders = "{0}/{1}/*.xml".format(common.ossec_path,
                                                      item_dir)

                for item in glob(all_decoders):
                    item_split = item.split('/')
                    item_name = item_split[-1]
                    item_dir = "/".join(item_split[:-1])
                    if item_name in exclude_filenames:
                        item_status = Decoder.S_DISABLED
                    else:
                        item_status = Decoder.S_ENABLED
                    tmp_data.append({
                        'file': item_name,
                        'path': item_dir,
                        'status': item_status
                    })

        data = list(tmp_data)
        for d in tmp_data:
            if status and status != 'all' and status != d['status']:
                data.remove(d)
                continue
            if path and path != d['path']:
                data.remove(d)
                continue
            if file and file != d['file']:
                data.remove(d)
                continue

        if search:
            data = search_array(data, search['value'], search['negation'])

        if sort:
            data = sort_array(data, sort['fields'], sort['order'])
        else:
            data = sort_array(data, ['file'], 'asc')

        return {
            'items': cut_array(data, offset, limit),
            'totalItems': len(data)
        }
Example #29
0
def get_pci(agent_id=None,
            offset=0,
            limit=common.database_limit,
            sort=None,
            search=None):
    """
    Get all the PCI requirements used in the rootchecks of the agent.

    :param agent_id: Agent ID.
    :param offset: First item to return.
    :param limit: Maximum number of items to return.
    :param sort: Sorts the items. Format: {"fields":["field1","field2"],"order":"asc|desc"}.
    :param search: Looks for items with the specified string.
    :return: Dictionary: {'items': array of items, 'totalItems': Number of items (without applying the limit)}
    """

    fields = {}
    request = {"$and": [{'pci_dss': {'$ne': None}}]}

    # Connection
    db_url = common.database_path
    conn = Connection(db_url)
    conn.connect(conn.getDbById(str(agent_id).zfill(3)))
    if (conn.getDb() == None):
        raise OssecAPIException(1600)

    # Search
    if search:
        regex = re.compile(".*{0}.*".format(int(search['value']) if search['value'].isdigit() \
                                                                    else search['value']), re.IGNORECASE)
        search_con = {"$or": []}

        search_con["$or"].append({'pci_dss': regex})

        if bool(search['negation']):
            if search_con["$or"]:
                request["$and"].append({"$not": search_con})
        else:
            if search_con["$or"]:
                request["$and"].append(search_con)

    # Total items
    # conn.execute(query.format('COUNT(DISTINCT pci_dss)'), request)
    # data = {'totalItems': conn.fetch()[0]}

    # Sorting
    sort_con = []
    if sort:
        if sort['fields']:
            allowed_sort_fields = set(fields.keys())
            # Check if every element in sort['fields'] is in allowed_sort_fields
            if not set(sort['fields']).issubset(allowed_sort_fields):
                uncorrect_fields = list(
                    map(lambda x: str(x),
                        set(sort['fields']) - set(allowed_sort_fields)))
                raise OssecAPIException(
                    1403, 'Allowed sort fields: {0}. Fields: {1}'.format(
                        allowed_sort_fields, uncorrect_fields))

            for i in sort['fields']:
                str_order = 1 if sort['order'] == 'asc' else -1
                sort_con.append((fields[i], str_order))
        else:
            sort_con.append(('pci_dss', 1 if sort['order'] == 'asc' else -1))
    else:
        sort_con.append(('pci_dss', 1))

    if limit:
        if limit > common.maximum_database_limit:
            raise OssecAPIException(1405, str(limit))
    elif limit == 0:
        raise OssecAPIException(1406)

    if not request["$and"]:
        request = {}
    db_data = conn.getDb()['pm_event'].find(request).sort(sort_con).skip(
        offset).limit(limit).distinct('pci_dss')
    data = {}
    data['items'] = []
    for pmEvent in db_data:
        data['items'].append(pmEvent)

    return data
Example #30
0
    def send_msg_to_agent(self, msg, agent_id=None, msg_type=None):
        # Active-response
        #   Agents: /var/ossec/queue/alerts/ar
        #     - Existing command:
        #       - (msg_to_agent) [] NNS 001 restart-ossec0 arg1 arg2 arg3
        #       - (msg_to_agent) [] ANN (null) restart-ossec0 arg1 arg2 arg3
        #     - Custom command:
        #       - (msg_to_agent) [] NNS 001 !test.sh arg1 arg2 arg3
        #       - (msg_to_agent) [] ANN (null) !test.sh arg1 arg2 arg3
        #   Manager: /var/ossec/queue/alerts/execq
        #     - Existing command:
        #       - restart-ossec0 arg1 arg2 arg3
        #     - Custom command:
        #       - !test.sh Hello World

        # Build message
        ALL_AGENTS_C = 'A'
        NONE_C = 'N'
        SPECIFIC_AGENT_C = 'S'
        NO_AR_C = '!'

        if agent_id:
            str_all_agents = NONE_C
            str_agent = SPECIFIC_AGENT_C
            str_agent_id = agent_id
        else:
            str_all_agents = ALL_AGENTS_C
            str_agent = NONE_C
            str_agent_id = "(null)"

        # AR
        if msg_type == OssecQueue.AR_TYPE:

            if not agent_id:
                raise OssecAPIException(1653)

            if agent_id != "000":
                # Example restart 'msg': restart-ossec0 - null (from_the_server) (no_rule_id)
                socket_msg = "{0} {1}{2}{3} {4} {5}".format(
                    "(msg_to_agent) []", str_all_agents, NONE_C, str_agent,
                    str_agent_id, msg)
            elif agent_id == "000":
                socket_msg = msg

            # Send message
            try:
                self._send(socket_msg.encode())
            except:
                raise OssecAPIException(1652)

            return "Command sent."

        # Legacy: Restart syscheck, restart agents
        else:
            if msg == OssecQueue.HC_SK_RESTART:
                socket_msg = "{0} {1}{2}{3} {4} {5}".format(
                    "(msg_to_agent) []", str_all_agents, NO_AR_C, str_agent,
                    str_agent_id, OssecQueue.HC_SK_RESTART)
            elif msg == OssecQueue.RESTART_AGENTS:
                socket_msg = "{0} {1}{2}{3} {4} {5} - {6} (from_the_server) (no_rule_id)".format(
                    "(msg_to_agent) []", str_all_agents, NONE_C, str_agent,
                    str_agent_id, OssecQueue.RESTART_AGENTS, "null")
            else:
                raise OssecAPIException(1012, msg)

            # Send message
            try:
                self._send(socket_msg.encode())
            except:
                if msg == OssecQueue.HC_SK_RESTART:
                    if agent_id:
                        raise OssecAPIException(1601, "on agent")
                    else:
                        raise OssecAPIException(1601, "on all agents")
                elif msg == OssecQueue.RESTART_AGENTS:
                    raise OssecAPIException(1702)

            # Return message
            if msg == OssecQueue.HC_SK_RESTART:
                return "Restarting Syscheck/Rootcheck on agent" if agent_id else "Restarting Syscheck/Rootcheck on all agents"
            elif msg == OssecQueue.RESTART_AGENTS:
                return "Restarting agent" if agent_id else "Restarting all agents"