Example #1
0
 async def start(self):
     """
     Connects to the server
     """
     # Get a reference to the event loop as we plan to use
     # low-level APIs.
     asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
     loop = asyncio.get_running_loop()
     on_con_lost = loop.create_future()
     try:
         self.transport, self.protocol = await loop.create_unix_connection(
             protocol_factory=lambda: LocalClientHandler(
                 loop=loop,
                 on_con_lost=on_con_lost,
                 name=self.name,
                 logger=self.logger,
                 fernet_key='',
                 manager=self,
                 cluster_items=self.cluster_items),
             path='{}/queue/cluster/c-internal.sock'.format(
                 common.ossec_path))
     except (ConnectionRefusedError, FileNotFoundError):
         raise exception.WazuhInternalError(3012)
     except MemoryError:
         raise exception.WazuhInternalError(1119)
     except Exception as e:
         raise exception.WazuhInternalError(3009, str(e))
Example #2
0
def test_execute_pagination(socket_send_mock, connect_mock):
    mywdb = WazuhDBConnection()

    # Test pagination
    with patch("wazuh.core.wdb.WazuhDBConnection._send",
               side_effect=[[{'total': 5}], exception.WazuhInternalError(2009), ['ok', '{"total": 5}'],
                            ['ok', '{"total": 5}']]):
        mywdb.execute("agent 000 sql select test from test offset 1 limit 500")

    # Test pagination error
    with patch("wazuh.core.wdb.WazuhDBConnection._send",
               side_effect=[[{'total': 5}], exception.WazuhInternalError(2009)]):
        with pytest.raises(exception.WazuhInternalError, match=".* 2009 .*"):
            mywdb.execute("agent 000 sql select test from test offset 1 limit 1")
Example #3
0
    def dispatch(self, command: bytes, counter: int, payload: bytes) -> None:
        """Process a received message and send a response.

        Parameters
        ----------
        command : bytes
            Command received.
        counter : int
            Message ID.
        payload : bytes
            Data received.
        """
        try:
            command, payload = self.process_request(command, payload)
        except exception.WazuhException as e:
            self.logger.error(
                f"Internal error processing request '{command}': {e}")
            command, payload = b'err', json.dumps(
                e, cls=WazuhJSONEncoder).encode()
        except Exception as e:
            self.logger.error(
                f"Unhandled error processing request '{command}': {e}",
                exc_info=True)
            command, payload = b'err', json.dumps(
                exception.WazuhInternalError(1000, extra_message=str(e)),
                cls=WazuhJSONEncoder).encode()
        if command is not None:
            self.push(self.msg_build(command, counter, payload))
Example #4
0
    def __or__(self, other):
        """
        | operator used to merge two AbstractWazuhResult objects. When merged with a WazuhException,
         the result is always a WazuhException
        :param other: AbstractWazuhResult or WazuhException
        :return: a new AbstractWazuhResult or WazuhException
        """
        if isinstance(other, wexception.WazuhException):
            return other
        elif not isinstance(other, (dict, AbstractWazuhResult)):
            raise wexception.WazuhInternalError(
                1000,
                extra_message=f"Cannot be merged with {type(other)} object")

        result = deepcopy(self)

        for key, field in other.items():
            if key not in result:
                result[key] = field
            elif isinstance(field, dict):
                result[key] = self._merge_dict(result[key], field, key=key)
            elif isinstance(field, list):
                self_field = result[key]
                result[key] = self._merge_list(self_field, field, key=key)
            elif isinstance(field, Number):
                result[key] = self._merge_number(result[key], field, key=key)
            elif isinstance(field, str):  # str
                result[key] = self._merge_str(result[key], field, key=key)

        return result
Example #5
0
    def __or__(self, other):
        result = super().__or__(other)
        if isinstance(other, wexception.WazuhError):
            if len(other.ids) > 0:
                for id_ in other.ids:
                    self.add_failed_item(id_=id_, error=other)
                return self
            else:
                return other
        elif isinstance(result, wexception.WazuhException):
            return result
        elif not isinstance(other, AffectedItemsWazuhResult):
            raise wexception.WazuhInternalError(
                1000,
                extra_message=f"Cannot be merged with {type(other)} object")

        result.add_failed_items_from(other)
        result.affected_items = merge(result.affected_items,
                                      other.affected_items,
                                      criteria=self.sort_fields,
                                      ascending=self.sort_ascending,
                                      types=self.sort_casting)
        result.total_affected_items = result.total_affected_items + other.total_affected_items

        return result
Example #6
0
def as_wazuh_object(dct: Dict):
    try:
        if '__callable__' in dct:
            encoded_callable = dct['__callable__']
            funcname = encoded_callable['__name__']
            if '__wazuh__' in encoded_callable:
                # Encoded Wazuh instance method
                wazuh_dict = encoded_callable['__wazuh__']
                wazuh = Wazuh()
                return getattr(wazuh, funcname)
            else:
                # Encoded function or static method
                qualname = encoded_callable['__qualname__'].split('.')
                classname = qualname[0] if len(qualname) > 1 else None
                module_path = encoded_callable['__module__']
                module = import_module(module_path)
                if classname is None:
                    return getattr(module, funcname)
                else:
                    return getattr(getattr(module, classname), funcname)
        elif '__wazuh_exception__' in dct:
            wazuh_exception = dct['__wazuh_exception__']
            return getattr(exception, wazuh_exception['__class__']).from_dict(wazuh_exception['__object__'])
        elif '__wazuh_result__' in dct:
            wazuh_result = dct['__wazuh_result__']
            return getattr(wresults, wazuh_result['__class__']).decode_json(wazuh_result['__object__'])
        elif '__wazuh_datetime__' in dct:
            return datetime.datetime.fromisoformat(dct['__wazuh_datetime__'])
        return dct

    except (KeyError, AttributeError):
        raise exception.WazuhInternalError(1000,
                                           extra_message=f"Wazuh object cannot be decoded from JSON {dct}",
                                           cmd_error=True)
Example #7
0
    async def send_api_request(self, command: bytes, data: bytes,
                               wait_for_complete: bool) -> str:
        """
        Sends a command to the server and waits for the response

        :param command: Command to execute
        :param data: Payload
        :param wait_for_complete: Whether to enable timeout waiting for the response or not
        :return: Response from the server
        """
        result = (await self.protocol.send_request(command, data)).decode()
        if result == 'There are no connected worker nodes':
            request_result = {}
        else:
            if command == b'dapi' or command == b'dapi_forward' or command == b'send_file' or \
                    result == 'Sent request to master node':
                try:
                    timeout = None if wait_for_complete \
                        else self.cluster_items['intervals']['communication']['timeout_api_request']
                    await asyncio.wait_for(
                        self.protocol.response_available.wait(),
                        timeout=timeout)
                    request_result = self.protocol.response.decode()
                except asyncio.TimeoutError:
                    raise exception.WazuhInternalError(3020)
            else:
                request_result = result
        return request_result
Example #8
0
    def dispatch(self, command: bytes, counter: int, payload: bytes) -> None:
        """
        Processes a received message and sends a response

        :param command: command received
        :param counter: message id
        :param payload: data received
        """
        try:
            command, payload = self.process_request(command, payload)
        except exception.WazuhException as e:
            self.logger.error(
                "Internal error processing request '{}': {}".format(
                    command, e))
            command, payload = b'err', json.dumps(
                e, cls=WazuhJSONEncoder).encode()
        except Exception as e:
            self.logger.error(
                "Unhandled error processing request '{}': {}".format(
                    command, e),
                exc_info=True)
            command, payload = b'err', json.dumps(
                exception.WazuhInternalError(1000, extra_message=str(e)),
                cls=WazuhJSONEncoder).encode()
        if command is not None:
            self.push(self.msg_build(command, counter, payload))
Example #9
0
    def add_failed_items_from(self, other):
        """Adds all failed items from other into the caller object

        :param other: AffectedItemsWazuhResult instance
        :return:
        """
        if not isinstance(other, AffectedItemsWazuhResult):
            raise wexception.WazuhInternalError(
                1000,
                extra_message=
                f"Failed items cannot be taken from {type(other)} object")

        for error, ids in other._failed_items.items():
            for id_ in ids:
                self.add_failed_item(id_=id_, error=error)
Example #10
0
    def __init__(self, dct):
        """
        Initializes an instance

        :param dct: map to take key-values from
        """
        if isinstance(dct, dict):
            self.dikt = dct
        elif isinstance(dct, AbstractWazuhResult):
            self.dikt = dct.dikt
        else:
            raise wexception.WazuhInternalError(
                1000,
                extra_message=f"dct param must be a dict or "
                f"an AbstractWazuhResult subclass, "
                f"not a {type(dct)}")
Example #11
0
    async def send_api_request(self, command: bytes, data: bytes,
                               wait_for_complete: bool) -> str:
        """Send DAPI request to the server and wait for response.

        Parameters
        ----------
        command : bytes
            Command to execute.
        data : bytes
            Data to send.
        wait_for_complete : bool
            Whether to raise a timeout exception or not.

        Returns
        -------
        request_result : dict
            API response.
        """
        result = (await self.protocol.send_request(command, data)).decode()
        if result == 'There are no connected worker nodes':
            request_result = {}
        else:
            # Wait for expected data if it is not returned by send_request(),
            # which occurs when the following commands are used.
            if command == b'dapi' or command == b'dapi_forward' or command == b'send_file' or command == b'sendasync' \
                    or result == 'Sent request to master node':
                try:
                    timeout = None if wait_for_complete \
                        else self.cluster_items['intervals']['communication']['timeout_api_request']
                    await asyncio.wait_for(
                        self.protocol.response_available.wait(),
                        timeout=timeout)
                    request_result = self.protocol.response.decode()
                except asyncio.TimeoutError:
                    raise exception.WazuhInternalError(3020)
            # If no data is expected (only the send_request() result), immediately return the output of send_request.
            else:
                request_result = result
        return request_result
Example #12
0
    async def execute_local_request(self) -> str:
        """Execute an API request locally.

        Returns
        -------
        str
            JSON response.
        """
        def run_local():
            self.debug_log("Starting to execute request locally")
            common.rbac.set(self.rbac_permissions)
            common.broadcast.set(self.broadcasting)
            common.cluster_nodes.set(self.nodes)
            common.current_user.set(self.current_user)
            common.origin_module.set(self.origin_module)
            data = self.f(**self.f_kwargs)
            common.reset_context_cache()
            self.debug_log("Finished executing request locally")
            return data

        try:
            if self.f_kwargs.get('agent_list') == '*':
                del self.f_kwargs['agent_list']

            before = time.time()
            self.check_wazuh_status()

            timeout = self.api_request_timeout if not self.wait_for_complete else None

            # LocalClient only for control functions
            if self.local_client_arg is not None:
                lc = local_client.LocalClient()
                self.f_kwargs[self.local_client_arg] = lc

            try:
                if self.is_async:
                    task = run_local()
                else:
                    loop = asyncio.get_running_loop()
                    task = loop.run_in_executor(threadpool, run_local)
                try:
                    data = await asyncio.wait_for(task, timeout=timeout)
                except asyncio.TimeoutError:
                    raise exception.WazuhInternalError(3021)
                except OperationalError:
                    raise exception.WazuhInternalError(2008)
            except json.decoder.JSONDecodeError:
                raise exception.WazuhInternalError(3036)

            self.debug_log(
                f"Time calculating request result: {time.time() - before:.3f}s"
            )
            return data
        except (exception.WazuhError, exception.WazuhResourceNotFound) as e:
            e.dapi_errors = self.get_error_info(e)
            if self.debug:
                raise
            return json.dumps(e, cls=c_common.WazuhJSONEncoder)
        except exception.WazuhInternalError as e:
            e.dapi_errors = self.get_error_info(e)
            # Avoid exception info if it is an asyncio timeout or JSONDecodeError
            self.logger.error(f"{e.message}",
                              exc_info=e.code not in {3021, 3036})
            if self.debug:
                raise
            return json.dumps(e, cls=c_common.WazuhJSONEncoder)
        except Exception as e:
            self.logger.error(f'Error executing API request locally: {str(e)}',
                              exc_info=True)
            if self.debug:
                raise
            return json.dumps(exception.WazuhInternalError(
                1000, dapi_errors=self.get_error_info(e)),
                              cls=c_common.WazuhJSONEncoder)
Example #13
0
    async def distribute_function(self) -> [Dict, exception.WazuhException]:
        """
        Distribute an API call.

        Returns
        -------
        dict or WazuhException
            Dictionary with API response or WazuhException in case of error.
        """
        try:
            if 'password' in self.f_kwargs:
                self.debug_log(
                    f"Receiving parameters { {**self.f_kwargs, 'password': '******'} }"
                )
            elif 'token_nbf_time' in self.f_kwargs:
                self.logger.debug(f"Decoded token {self.f_kwargs}")
            else:
                self.debug_log(f"Receiving parameters {self.f_kwargs}")

            is_dapi_enabled = self.cluster_items['distributed_api']['enabled']
            is_cluster_disabled = self.node == local_client and not check_cluster_status(
            )

            # First case: execute the request locally.
            # If the distributed api is not enabled
            # If the cluster is disabled or the request type is local_any
            # if the request was made in the master node and the request type is local_master
            # if the request came forwarded from the master node and its type is distributed_master
            if not is_dapi_enabled or is_cluster_disabled or self.request_type == 'local_any' or \
                    (self.request_type == 'local_master' and self.node_info['type'] == 'master') or \
                    (self.request_type == 'distributed_master' and self.from_cluster):

                response = await self.execute_local_request()

            # Second case: forward the request
            # Only the master node will forward a request, and it will only be forwarded if its type is distributed_
            # master
            elif self.request_type == 'distributed_master' and self.node_info[
                    'type'] == 'master':
                response = await self.forward_request()

            # Last case: execute the request remotely.
            # A request will only be executed remotely if it was made in a worker node and its type isn't local_any
            else:
                response = await self.execute_remote_request()

            try:
                response = json.loads(response, object_hook=c_common.as_wazuh_object) \
                    if isinstance(response, str) else response
            except json.decoder.JSONDecodeError:
                response = {'message': response}

            return response if isinstance(response, (wresults.AbstractWazuhResult, exception.WazuhException)) \
                else wresults.WazuhResult(response)

        except json.decoder.JSONDecodeError:
            e = exception.WazuhInternalError(3036)
            e.dapi_errors = self.get_error_info(e)
            if self.debug:
                raise
            self.logger.error(f"{e.message}")
            return e
        except exception.WazuhError as e:
            e.dapi_errors = self.get_error_info(e)
            return e
        except exception.WazuhInternalError as e:
            e.dapi_errors = self.get_error_info(e)
            if self.debug:
                raise
            self.logger.error(f"{e.message}", exc_info=True)
            return e
        except Exception as e:
            if self.debug:
                raise

            self.logger.error(f'Unhandled exception: {str(e)}', exc_info=True)
            return exception.WazuhInternalError(
                1000, dapi_errors=self.get_error_info(e))
Example #14
0
    async def execute_local_request(self) -> str:
        """Execute an API request locally.

        Returns
        -------
        str
            JSON response.
        """
        def run_local():
            self.logger.debug("Starting to execute request locally")
            common.rbac.set(self.rbac_permissions)
            common.broadcast.set(self.broadcasting)
            common.cluster_nodes.set(self.nodes)
            common.current_user.set(self.current_user)
            data = self.f(**self.f_kwargs)
            common.reset_context_cache()
            self.logger.debug("Finished executing request locally")
            return data

        try:
            before = time.time()
            self.check_wazuh_status()

            timeout = None if self.wait_for_complete \
                else self.cluster_items['intervals']['communication']['timeout_api_exe']

            # LocalClient only for control functions
            if self.local_client_arg is not None:
                lc = local_client.LocalClient()
                self.f_kwargs[self.local_client_arg] = lc
            else:
                lc = None

            if self.is_async:
                task = run_local()
            else:
                loop = asyncio.get_running_loop()
                task = loop.run_in_executor(self.threadpool, run_local)

            try:
                data = await asyncio.wait_for(task, timeout=timeout)
            except asyncio.TimeoutError:
                raise exception.WazuhInternalError(3021)

            self.logger.debug(
                f"Time calculating request result: {time.time() - before}s")
            return data
        except (exception.WazuhError, exception.WazuhResourceNotFound) as e:
            e.dapi_errors = self.get_error_info(e)
            if self.debug:
                raise
            return json.dumps(e, cls=c_common.WazuhJSONEncoder)
        except exception.WazuhInternalError as e:
            e.dapi_errors = self.get_error_info(e)
            self.logger.error(f"{e.message}", exc_info=True)
            if self.debug:
                raise
            return json.dumps(e, cls=c_common.WazuhJSONEncoder)
        except Exception as e:
            self.logger.error(f'Error executing API request locally: {str(e)}',
                              exc_info=True)
            if self.debug:
                raise
            return json.dumps(exception.WazuhInternalError(
                1000, dapi_errors=self.get_error_info(e)),
                              cls=c_common.WazuhJSONEncoder)
Example #15
0
    async def execute_local_request(self) -> str:
        """Execute an API request locally.

        Returns
        -------
        str
            JSON response.
        """
        try:
            if self.f_kwargs.get('agent_list') == '*':
                del self.f_kwargs['agent_list']

            before = time.time()
            self.check_wazuh_status()

            timeout = self.api_request_timeout if not self.wait_for_complete else None

            # LocalClient only for control functions
            if self.local_client_arg is not None:
                lc = local_client.LocalClient()
                self.f_kwargs[self.local_client_arg] = lc
            try:
                if self.is_async:
                    task = self.run_local(self.f, self.f_kwargs, self.logger,
                                          self.rbac_permissions,
                                          self.broadcasting, self.nodes,
                                          self.current_user)

                else:
                    loop = asyncio.get_event_loop()
                    if 'thread_pool' in pools:
                        pool = pools.get('thread_pool')
                    elif self.f.__name__ in authentication_funcs:
                        pool = pools.get('authentication_pool')
                    else:
                        pool = pools.get('process_pool')

                    task = loop.run_in_executor(
                        pool,
                        partial(self.run_local, self.f, self.f_kwargs,
                                self.logger, self.rbac_permissions,
                                self.broadcasting, self.nodes,
                                self.current_user))
                try:
                    data = await asyncio.wait_for(task, timeout=timeout)
                except asyncio.TimeoutError:
                    raise exception.WazuhInternalError(3021)
                except OperationalError:
                    raise exception.WazuhInternalError(2008)
                except process.BrokenProcessPool:
                    raise exception.WazuhInternalError(901)
            except json.decoder.JSONDecodeError:
                raise exception.WazuhInternalError(3036)
            except process.BrokenProcessPool:
                raise exception.WazuhInternalError(900)

            self.debug_log(
                f"Time calculating request result: {time.time() - before:.3f}s"
            )
            return data
        except (exception.WazuhError, exception.WazuhResourceNotFound) as e:
            e.dapi_errors = self.get_error_info(e)
            if self.debug:
                raise
            return json.dumps(e, cls=c_common.WazuhJSONEncoder)
        except exception.WazuhInternalError as e:
            e.dapi_errors = self.get_error_info(e)
            # Avoid exception info if it is an asyncio timeout or JSONDecodeError
            self.logger.error(f"{e.message}",
                              exc_info=e.code not in {3021, 3036})
            if self.debug:
                raise
            return json.dumps(e, cls=c_common.WazuhJSONEncoder)
        except Exception as e:
            self.logger.error(f'Error executing API request locally: {str(e)}',
                              exc_info=True)
            if self.debug:
                raise
            return json.dumps(exception.WazuhInternalError(
                1000, dapi_errors=self.get_error_info(e)),
                              cls=c_common.WazuhJSONEncoder)