Esempio n. 1
0
def get_permissions(user_id=None, auth_context=None):
    with AuthenticationManager() as auth:
        if auth.user_auth_context(user_id):
            # Add dummy rbac_policies for developing here
            if auth_context:
                return WazuhResult(
                    optimize_resources(auth_context=auth_context))

    return WazuhResult(optimize_resources(user_id=user_id))
Esempio n. 2
0
def test_results_WazuhResult__merge_str(dikt, priority, get_wazuh_affected_item):
    """Test method `_merge_str` from `WazuhResult`.

        Parameters
        ----------
        dikt : dict
            Dict with basic information for the class declaration.
        priority : list
            Used to set the WazuhResult priority.
        """
    wazuh_result = WazuhResult(deepcopy(dikt), str_priority=priority)
    assert isinstance(wazuh_result, WazuhResult)
    item2 = wazuh_result.dikt['data']['items'][1]['item2']
    merge_result = wazuh_result._merge_str(item2, 'KO')
    assert merge_result == priority[0] if priority else '{}|{}'.format(item2, 'KO')
Esempio n. 3
0
def upload_xml(xml_file, path):
    """
    Upload XML files (rules, decoders and ossec.conf)
    :param xml_file: content of the XML file
    :param path: Destination of the new XML file
    :return: Confirmation message
    """
    # 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))
    try:
        with open(tmp_file_path, 'w') as tmp_file:
            final_xml = prettify_xml(xml_file)
            tmp_file.write(final_xml)
        chmod(tmp_file_path, 0o660)
    except IOError:
        raise WazuhInternalError(1005)

    # 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 was successfully updated'})
Esempio n. 4
0
def manager_restart():
    """
    Restart Wazuh manager.

    :return: Confirmation message.
    """
    lock_file = open(execq_lockfile, 'a+')
    fcntl.lockf(lock_file, fcntl.LOCK_EX)
    try:
        # execq socket path
        socket_path = common.EXECQ
        # msg for restarting Wazuh manager
        msg = 'restart-wazuh '
        # initialize socket
        if exists(socket_path):
            try:
                conn = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
                conn.connect(socket_path)
            except socket.error:
                raise WazuhInternalError(1902)
        else:
            raise WazuhInternalError(1901)

        try:
            conn.send(msg.encode())
            conn.close()
        except socket.error as e:
            raise WazuhInternalError(1014, extra_message=str(e))
    finally:
        fcntl.lockf(lock_file, fcntl.LOCK_UN)
        lock_file.close()
        read_config.cache_clear()

    return WazuhResult({'message': 'Restart request sent'})
Esempio n. 5
0
def test_decode_token(mock_raise_if_exc, mock_distribute_function, mock_dapi, mock_generate_secret, mock_decode):
    mock_decode.return_value = payload
    mock_raise_if_exc.side_effect = [WazuhResult({'valid': True}), WazuhResult(security_conf)]

    result = authentication.decode_token('test_token')
    assert result == payload

    # Check all functions are called with expected params
    calls = [call(f=ANY, f_kwargs={'username': payload['sub'], 'token_iat_time': payload['iat']},
                  request_type='local_master', is_async=False, wait_for_complete=True, logger=ANY),
             call(f=ANY, request_type='local_master', is_async=False, wait_for_complete=True, logger=ANY)]
    mock_dapi.assert_has_calls(calls)
    mock_generate_secret.assert_called_once()
    mock_decode.assert_called_once_with('test_token', 'test_secret_token', algorithms=['HS256'])
    assert mock_distribute_function.call_count == 2
    assert mock_raise_if_exc.call_count == 2
Esempio n. 6
0
def get_agents_summary_status(agent_list=None):
    """Counts the number of agents by status.

    :param agent_list: List of agents ID's.
    :return: WazuhResult.
    """
    summary = {
        'active': 0,
        'disconnected': 0,
        'never_connected': 0,
        'pending': 0,
        'total': 0
    }
    if len(agent_list) != 0:
        rbac_filters = get_rbac_filters(system_resources=get_agents_info(),
                                        permitted_resources=agent_list)

        db_query = WazuhDBQueryAgents(limit=None,
                                      select=['status'],
                                      **rbac_filters)
        data = db_query.run()

        for agent in data['items']:
            summary[agent['status']] += 1
            summary['total'] += 1

    return WazuhResult({'data': summary})
Esempio n. 7
0
def upload_list(list_file, path):
    """
    Updates CDB lists
    :param list_file: content of the list
    :param path: Destination of the new list file
    :return: Confirmation message.
    """
    # path of temporary file
    tmp_file_path = '{}/tmp/api_tmp_file_{}_{}.txt'.format(common.ossec_path, time.time(), random.randint(0, 1000))

    try:
        # create temporary file
        with open(tmp_file_path, 'w') as tmp_file:
            # write json in tmp_file_path
            for element in list_file.splitlines():
                # skip empty lines
                if not element:
                    continue
                tmp_file.write(element.strip() + '\n')
        chmod(tmp_file_path, 0o640)
    except IOError:
        raise WazuhInternalError(1005)

    # validate CDB list
    if not validate_cdb_list(tmp_file_path):
        raise WazuhError(1800)

    # 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'})
Esempio n. 8
0
def revoke_tokens():
    """ Revoke all tokens """
    change_secret()
    with TokenManager() as tm:
        tm.delete_all_rules()

    return WazuhResult({'msg': 'Tokens revoked successfully'})
Esempio n. 9
0
def weekly():
    """
    Returns the weekly averages.
    :return: A dictionary for each week day.
    """

    response = {}

    # 0..6 => Sunday..Saturday
    for i in range(7):
        hours = []
        interactions = 0

        for j in range(25):
            try:
                wfile = open(common.stats_path + '/weekly-average/' + str(i) +
                             '/' + str(j))
                data = wfile.read()

                if j == 24:
                    interactions = int(data)
                else:
                    hours.append(int(data))

                wfile.close()
            except IOError:
                if i < 24:
                    hours.append(0)

        response[DAYS[i]] = {'hours': hours, 'interactions': interactions}

    return WazuhResult(response)
Esempio n. 10
0
def create_group(group_id):
    """Creates a group.

    :param group_id: Group ID.
    :return: Confirmation message.
    """
    # Input Validation of group_id
    if not InputValidator().group(group_id):
        raise WazuhError(1722)

    group_path = path.join(common.shared_path, group_id)

    if group_id.lower() == "default" or path.exists(group_path):
        raise WazuhError(1711, extra_message=group_id)

    # Create group in /etc/shared
    group_def_path = path.join(common.shared_path, 'agent-template.conf')
    try:
        mkdir_with_mode(group_path)
        copyfile(group_def_path, path.join(group_path, 'agent.conf'))
        chown_r(group_path, common.wazuh_uid(), common.wazuh_gid())
        chmod_r(group_path, 0o660)
        chmod(group_path, 0o770)
        msg = f"Group '{group_id}' created."
    except Exception as e:
        raise WazuhInternalError(1005, extra_message=str(e))

    return WazuhResult({'message': msg})
Esempio n. 11
0
async def run_as_login(request, user: str, raw: bool = False) -> web.Response:
    """User/password authentication to get an access token.
    This method should be called to get an API token using an authorization context body. This token will expire at some time. # noqa: E501

    Parameters
    ----------
    request : connexion.request
    user : str
        Name of the user who wants to be authenticated.
    raw : bool, optional
        Respond in raw format. Default `False`

    Returns
    -------
    web.Response
        Raw or JSON response with the generated access token.
    """
    f_kwargs = {'user_id': user, 'auth_context': await request.json()}

    dapi = DistributedAPI(f=preprocessor.get_permissions,
                          f_kwargs=remove_nones_to_dict(f_kwargs),
                          request_type='local_master',
                          is_async=False,
                          logger=logger
                          )
    data = raise_if_exc(await dapi.distribute_function())

    token = None
    try:
        token = generate_token(user_id=user, data=data.dikt, run_as=True)
    except WazuhException as e:
        raise_if_exc(e)

    return web.Response(text=token, content_type='text/plain', status=200) if raw \
        else web.json_response(data=WazuhResult({'data': TokenResponseModel(token=token)}), status=200, dumps=dumps)
Esempio n. 12
0
def get_agents_summary_status(agent_list=None):
    """Count the number of agents by status.

    Parameters
    ----------
    agent_list : list[str]
       List of agents ID's

    Returns
    -------
    WazuhResult
    """
    summary = {
        'active': 0,
        'disconnected': 0,
        'never_connected': 0,
        'pending': 0,
        'total': 0
    }
    if agent_list:
        rbac_filters = get_rbac_filters(system_resources=get_agents_info(),
                                        permitted_resources=agent_list)

        # We don't consider agent 000 in order to get the summary
        db_query = WazuhDBQueryAgents(limit=None,
                                      select=['status'],
                                      query="id!=000",
                                      **rbac_filters)
        data = db_query.run()

        for agent in data['items']:
            summary[agent['status']] += 1
            summary['total'] += 1

    return WazuhResult({'data': summary})
Esempio n. 13
0
async def get_user_me_policies(request, pretty=False, wait_for_complete=False):
    """Return processed RBAC policies and rbac_mode for the current user.

    Parameters
    ----------
    request : connexion.request
    pretty : bool, optional
        Show results in human-readable format
    wait_for_complete : bool, optional
        Disable timeout response

    Returns
    -------
    Users information
    """
    data = WazuhResult({
        'data':
        request['token_info']['rbac_policies'],
        'message':
        "Current user processed policies information was returned"
    })

    return web.json_response(data=data,
                             status=200,
                             dumps=prettify if pretty else dumps)
Esempio n. 14
0
def revoke_current_user_tokens():
    """Revoke all current user's tokens"""
    with TokenManager() as tm:
        with AuthenticationManager() as am:
            tm.add_user_roles_rules(users={am.get_user(common.current_user.get())['id']})

    return WazuhResult({'message': f'User {common.current_user.get()} was successfully logged out'})
Esempio n. 15
0
def hourly():
    """
    Returns the hourly averages.
    :return: Dictionary: averages and interactions.
    """

    averages = []
    interactions = 0

    # What's the 24 for?
    for i in range(25):
        try:
            hfile = open(common.stats_path + '/hourly-average/' + str(i))
            data = hfile.read()

            if i == 24:
                interactions = int(data)
            else:
                averages.append(int(data))

            hfile.close()
        except IOError:
            if i < 24:
                averages.append(0)

    return WazuhResult({'averages': averages, 'interactions': interactions})
Esempio n. 16
0
def get_file(path, validate=False):
    """Returns the content of a file.

    :param path: Relative path of file from origin
    :param validate: Whether to validate file content or not
    :return: WazuhResult
    """
    full_path = join(common.ossec_path, path[0])

    # check if file exists
    if not exists(full_path):
        raise WazuhError(1906)

    # validate CDB lists files
    if validate and re.match(r'^etc/lists',
                             path[0]) and not validate_cdb_list(path[0]):
        raise WazuhError(1800, {'path': path[0]})

    # validate XML files
    if validate and not validate_xml(path[0]):
        raise WazuhError(1113)

    try:
        with open(full_path) as f:
            output = f.read()
    except IOError:
        raise WazuhInternalError(1005)

    return WazuhResult({'contents': output})
Esempio n. 17
0
def custom_hook(dct):
    if 'key' in dct:
        return HTTPSModel.from_dict(dct)
    elif 'error' in dct:
        return WazuhResult.decode_json({'result': dct, 'str_priority': 'v2'})
    else:
        return dct
Esempio n. 18
0
def get_status_json():
    """
    Returns the cluster status

    :return: Dictionary with the cluster status.
    """
    return WazuhResult({'data': get_cluster_status()})
Esempio n. 19
0
def add_agent(name=None,
              agent_id=None,
              key=None,
              ip='any',
              force_time=-1,
              use_only_authd=False):
    """Adds a new Wazuh agent.

    :param name: name of the new agent.
    :param agent_id: id of the new agent.
    :param ip: IP of the new agent. It can be an IP, IP/NET or ANY.
    :param key: key of the new agent.
    :param force_time: Remove old agent with same IP if disconnected since <force_time> seconds.
    :param use_only_authd: Force the use of authd when adding and removing agents.
    :return: Agent ID and Agent key.
    """
    # Check length of agent name
    if len(name) > 128:
        raise WazuhError(1738)

    new_agent = Agent(name=name,
                      ip=ip,
                      id=agent_id,
                      key=key,
                      force=force_time,
                      use_only_authd=use_only_authd)

    return WazuhResult({'data': {'id': new_agent.id, 'key': new_agent.key}})
Esempio n. 20
0
def get_full_overview() -> WazuhResult:
    """Get information about agents.

    Returns
    -------
    Dictionary with information about agents
    """
    # We don't consider agent 000 in order to get the summary
    q = "id!=000"

    # Get information from different methods of Agent class
    stats_distinct_node = get_distinct_agents(fields=['node_name'], q=q).affected_items
    groups = get_agent_groups().affected_items
    stats_distinct_os = get_distinct_agents(fields=['os.name',
                                                    'os.platform', 'os.version'], q=q).affected_items
    stats_version = get_distinct_agents(fields=['version'], q=q).affected_items
    agent_summary_status = get_agents_summary_status()
    summary = agent_summary_status['data'] if 'data' in agent_summary_status else dict()
    try:
        last_registered_agent = [get_agents(limit=1,
                                            sort={'fields': ['dateAdd'], 'order': 'desc'},
                                            q=q).affected_items[0]]
    except IndexError:  # an IndexError could happen if there are not registered agents
        last_registered_agent = []
    # combine results in an unique dictionary
    result = {'nodes': stats_distinct_node, 'groups': groups, 'agent_os': stats_distinct_os, 'agent_status': summary,
              'agent_version': stats_version, 'last_registered_agent': last_registered_agent}

    return WazuhResult({'data': result})
Esempio n. 21
0
def test_decode_token(mock_raise_if_exc, mock_submit, mock_distribute_function,
                      mock_dapi, mock_generate_secret, mock_decode):
    mock_decode.return_value = deepcopy(original_payload)
    mock_raise_if_exc.side_effect = [
        WazuhResult({
            'valid': True,
            'policies': {
                'value': 'test'
            }
        }),
        WazuhResult(security_conf)
    ]

    result = authentication.decode_token('test_token')
    assert result == decoded_payload

    # Check all functions are called with expected params
    calls = [
        call(f=ANY,
             f_kwargs={
                 'username': original_payload['sub'],
                 'token_nbf_time': original_payload['nbf'],
                 'run_as': False,
                 'roles': tuple(original_payload['rbac_roles']),
                 'origin_node_type': 'master'
             },
             request_type='local_master',
             is_async=False,
             wait_for_complete=False,
             logger=ANY),
        call(f=ANY,
             request_type='local_master',
             is_async=False,
             wait_for_complete=False,
             logger=ANY)
    ]
    mock_dapi.assert_has_calls(calls)
    mock_generate_secret.assert_called_once()
    mock_decode.assert_called_once_with('test_token',
                                        'test_secret_token',
                                        algorithms=['HS256'],
                                        audience='Wazuh API REST')
    assert mock_distribute_function.call_count == 2
    assert mock_raise_if_exc.call_count == 2
Esempio n. 22
0
def revoke_current_user_tokens():
    """Revoke all current user's tokens"""
    with AuthenticationManager() as am:
        invalid_users_tokens(
            users=[am.get_user(common.current_user.get())['id']])

    return WazuhResult({
        'message':
        f'User {common.current_user.get()} was successfully logged out'
    })
Esempio n. 23
0
def test_get_status_json():
    """Verify that get_status_json returns the default status information."""
    result = cluster.get_status_json()
    expected = WazuhResult({
        'data': {
            "enabled": "no" if default_config['disabled'] else "yes",
            "running": "no"
        }
    })
    assert result == expected
Esempio n. 24
0
def get_rbac_resources(resource: str = None):
    """Get the RBAC resources from the catalog

    Parameters
    ----------
    resource : str
        Show the information of the specified resource. Ex: agent:id

    Returns
    -------
    dict
        RBAC resources
    """
    if not resource:
        return WazuhResult({'data': load_spec()['x-rbac-catalog']['resources']})
    else:
        if resource not in load_spec()['x-rbac-catalog']['resources'].keys():
            raise WazuhError(4019)
        return WazuhResult({'data': {resource: load_spec()['x-rbac-catalog']['resources'][resource]}})
Esempio n. 25
0
def totals(date):
    """
    Returns the totals file.
    :param date: date object with the date value of the stats
    :return: Array of dictionaries. Each dictionary represents an hour.
    """

    stat_filename = ""
    try:
        stat_filename = os.path.join(
            common.stats_path, "totals", str(date.year),
            MONTHS[date.month - 1], f"ossec-totals-{date.strftime('%d')}.log")
        stats = open(stat_filename, 'r')
    except IOError:
        raise WazuhError(1308, extra_message=stat_filename)

    response = []
    alerts = []

    for line in stats:
        data = line.split('-')

        if len(data) == 4:
            sigid = int(data[1])
            level = int(data[2])
            times = int(data[3])

            alert = {'sigid': sigid, 'level': level, 'times': times}
            alerts.append(alert)
        else:
            data = line.split('--')

            if len(data) != 5:
                if len(data) in (0, 1):
                    continue
                else:
                    raise WazuhInternalError(1309)

            hour = int(data[0])
            total_alerts = int(data[1])
            events = int(data[2])
            syscheck = int(data[3])
            firewall = int(data[4])

            response.append({
                'hour': hour,
                'alerts': alerts,
                'totalAlerts': total_alerts,
                'events': events,
                'syscheck': syscheck,
                'firewall': firewall
            })
            alerts = []

    return WazuhResult({'data': response})
Esempio n. 26
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
Esempio n. 27
0
def manager_restart() -> WazuhResult:
    """Restart Wazuh manager.

    Send JSON message with the 'restart-wazuh' command to common.EXECQ socket.

    Raises
    ------
    WazuhInternalError(1901)
        If the socket path doesn't exist.
    WazuhInternalError(1902)
        If there is a socket connection error.
    WazuhInternalError(1014)
        If there is a socket communication error.

    Returns
    -------
    WazuhResult
        Confirmation message.
    """
    lock_file = open(execq_lockfile, 'a+')
    fcntl.lockf(lock_file, fcntl.LOCK_EX)
    try:
        # execq socket path
        socket_path = common.EXECQ
        # json msg for restarting Wazuh manager
        msg = json.dumps(
            create_wazuh_socket_message(
                origin={'module': common.origin_module.get()},
                command=common.RESTART_WAZUH_COMMAND,
                parameters={
                    'extra_args': [],
                    'alert': {}
                }))
        # initialize socket
        if exists(socket_path):
            try:
                conn = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
                conn.connect(socket_path)
            except socket.error:
                raise WazuhInternalError(1902)
        else:
            raise WazuhInternalError(1901)

        try:
            conn.send(msg.encode())
            conn.close()
        except socket.error as e:
            raise WazuhInternalError(1014, extra_message=str(e))
    finally:
        fcntl.lockf(lock_file, fcntl.LOCK_UN)
        lock_file.close()
        read_config.cache_clear()

    return WazuhResult({'message': 'Restart request sent'})
Esempio n. 28
0
def get_wazuh_result():
    return WazuhResult(dct={
        "data": {
            "items": [{
                "item1": "data1"
            }, {
                "item2": "OK"
            }],
            "message": "Everything ok"
        }
    },
                       str_priority=['KO', 'OK'])
Esempio n. 29
0
def test_decode_token_ko(mock_generate_secret, mock_raise_if_exc, mock_submit, mock_distribute_function):
    """Assert exceptions are handled as expected inside decode_token()"""
    with pytest.raises(Unauthorized):
        authentication.decode_token(token='test_token')

    with patch('api.authentication.jwt.decode') as mock_decode:
        with patch('api.authentication.generate_secret', return_value='test_secret_token'):
            with patch('wazuh.core.cluster.dapi.dapi.DistributedAPI.__init__', return_value=None):
                with patch('wazuh.core.cluster.dapi.dapi.DistributedAPI.distribute_function'):
                    with patch('api.authentication.raise_if_exc') as mock_raise_if_exc:
                        mock_decode.return_value = deepcopy(original_payload)

                        with pytest.raises(Unauthorized):
                            mock_raise_if_exc.side_effect = [WazuhResult({'valid': False})]
                            authentication.decode_token(token='test_token')

                        with pytest.raises(Unauthorized):
                            mock_raise_if_exc.side_effect = [WazuhResult({'valid': True, 'policies': {'value': 'test'}}),
                                                             WazuhResult({'auth_token_exp_timeout': 3600,
                                                                          'rbac_mode': 'white'})]
                            authentication.decode_token(token='test_token')
Esempio n. 30
0
def upload_group_file(group_list=None, file_data=None, file_name='agent.conf'):
    """Updates a group file.

    :param group_list: List of Group names.
    :param file_data: Relative path of temporary file to upload.
    :param file_name: File name to update.
    :return: Confirmation message.
    """
    # We access unique group_id from list, this may change if and when we decide to add option to update files for
    # a list of groups
    group_id = group_list[0]

    return WazuhResult({'message': configuration.upload_group_file(group_id, file_data, file_name=file_name)})