diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7ec0d31028..6ebb8de838 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,7 +44,7 @@ if(ENABLE_AGENTS) pkg_check_modules( LIBGVM_AGENT_CONTROLLER REQUIRED - libgvm_agent_controller>=22.23 + libgvm_agent_controller>=22.26 ) else(ENABLE_AGENTS) message(STATUS "ENABLE_AGENTS flag is not enabled") @@ -159,6 +159,7 @@ set( manage_agent_groups.c manage_agents.c manage_agent_installers.c + manage_agent_control_scan_config.c manage_alerts.c manage_configs.c manage_events.c @@ -212,6 +213,7 @@ set( set( ALL_GMP_SRC gmp.c + gmp_agent_control_scan_agent_config.c gmp_agent_groups.c gmp_agents.c gmp_agent_installers.c diff --git a/src/gmp.c b/src/gmp.c index 9a0e7fe2bd..17b1d1e2d7 100644 --- a/src/gmp.c +++ b/src/gmp.c @@ -84,6 +84,7 @@ */ #include "gmp.h" +#include "gmp_agent_control_scan_agent_config.h" #include "gmp_agent_groups.h" #include "gmp_agents.h" #include "gmp_base.h" @@ -4610,6 +4611,7 @@ typedef enum CLIENT_MODIFY_TARGET_SSH_LSC_CREDENTIAL_PORT, CLIENT_MODIFY_TASK, #if ENABLE_AGENTS + CLIENT_MODIFY_AGENT_CONTROL_SCAN_CONFIG, CLIENT_MODIFY_TASK_AGENT_GROUP, #endif /* ENABLE_AGENTS */ CLIENT_MODIFY_TASK_ALERT, @@ -5965,6 +5967,14 @@ gmp_xml_handle_start_element (/* unused */ GMarkupParseContext* context, set_client_state (CLIENT_LOGOUT); } #if ENABLE_AGENTS + else if (strcasecmp ("MODIFY_AGENT_CONTROL_SCAN_CONFIG", element_name) + == 0) + { + modify_agent_control_scan_config_start (gmp_parser, + attribute_names, + attribute_values); + set_client_state (CLIENT_MODIFY_AGENT_CONTROL_SCAN_CONFIG); + } else if (strcasecmp ("MODIFY_AGENT_GROUP", element_name) == 0) { modify_agent_group_start (gmp_parser, attribute_names, @@ -6329,6 +6339,13 @@ gmp_xml_handle_start_element (/* unused */ GMarkupParseContext* context, ELSE_READ_OVER; #if ENABLE_AGENTS + case CLIENT_MODIFY_AGENT_CONTROL_SCAN_CONFIG: + modify_agent_control_scan_config_element_start ( + gmp_parser, element_name, + attribute_names, + attribute_values); + break; + case CLIENT_MODIFY_AGENT_GROUP: modify_agent_group_element_start (gmp_parser, element_name, attribute_names, @@ -17477,6 +17494,89 @@ handle_get_scanners (gmp_parser_t *gmp_parser, GError **error) g_free (md5_fingerprint); g_free (issuer); } +#if ENABLE_AGENTS + int stype = scanner_iterator_type (&scanners); + if (stype == SCANNER_TYPE_AGENT_CONTROLLER + || stype == SCANNER_TYPE_AGENT_CONTROLLER_SENSOR) + { + scanner_t scanner_id = get_iterator_resource (&scanners); + agent_controller_scan_agent_config_t cfg = + get_agent_control_scan_config (scanner_id); + + if (!cfg) + { + SEND_TO_CLIENT_OR_FAIL (""); + } + else + { + SEND_TO_CLIENT_OR_FAIL (""); + + /* agent_control/retry */ + SEND_TO_CLIENT_OR_FAIL (""); + SENDF_TO_CLIENT_OR_FAIL ("%d", + cfg->agent_control.retry.attempts); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_control.retry.delay_in_seconds); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_control.retry.max_jitter_in_seconds); + SEND_TO_CLIENT_OR_FAIL (""); + + /* agent_script_executor */ + SEND_TO_CLIENT_OR_FAIL (""); + SENDF_TO_CLIENT_OR_FAIL ("%d", + cfg->agent_script_executor.bulk_size) + ; + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_script_executor.bulk_throttle_time_in_ms); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_script_executor.indexer_dir_depth); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_script_executor.period_in_seconds); + + GPtrArray *cron = cfg->agent_script_executor. + scheduler_cron_time; + if (cron && cron->len > 0) + { + SEND_TO_CLIENT_OR_FAIL ( + ""); + for (guint i = 0; i < cron->len; ++i) + { + const char *item = (const char *) cron->pdata[i]; + gchar *esc = g_markup_escape_text ( + item ? item : "", -1); + SENDF_TO_CLIENT_OR_FAIL ("%s", esc); + g_free (esc); + } + SEND_TO_CLIENT_OR_FAIL (""); + } + else + { + SEND_TO_CLIENT_OR_FAIL ( + ""); + } + SEND_TO_CLIENT_OR_FAIL (""); + + /* heartbeat */ + SEND_TO_CLIENT_OR_FAIL (""); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->heartbeat.interval_in_seconds); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->heartbeat.miss_until_inactive); + SEND_TO_CLIENT_OR_FAIL (""); + + SEND_TO_CLIENT_OR_FAIL (""); + + agent_controller_scan_agent_config_free (cfg); + } + } +#endif } credential_id = credential_uuid (scanner_iterator_credential (&scanners)); @@ -24334,7 +24434,7 @@ gmp_xml_handle_end_element (/* unused */ GMarkupParseContext* context, /* Check for the right combination of target and config. */ - if (create_task_data->target_id == NULL + if (create_task_data->target_id == NULL && create_task_data->agent_group_id == NULL && create_task_data->oci_image_target_id == NULL) { @@ -24896,6 +24996,10 @@ gmp_xml_handle_end_element (/* unused */ GMarkupParseContext* context, break; } #if ENABLE_AGENTS + case CLIENT_MODIFY_AGENT_CONTROL_SCAN_CONFIG: + if (modify_agent_control_scan_config_element_end (gmp_parser, error, element_name)) + set_client_state (CLIENT_AUTHENTIC); + break; case CLIENT_MODIFY_AGENT_GROUP: if (modify_agent_group_element_end (gmp_parser, error, element_name)) set_client_state (CLIENT_AUTHENTIC); @@ -29120,6 +29224,10 @@ gmp_xml_handle_text (/* unused */ GMarkupParseContext* context, break; #if ENABLE_AGENTS + case CLIENT_MODIFY_AGENT_CONTROL_SCAN_CONFIG: + modify_agent_control_scan_config_element_text (text, text_len); + break; + case CLIENT_MODIFY_AGENT_GROUP: modify_agent_group_element_text (text, text_len); break; diff --git a/src/gmp_agent_control_scan_agent_config.c b/src/gmp_agent_control_scan_agent_config.c new file mode 100644 index 0000000000..ddce3820b8 --- /dev/null +++ b/src/gmp_agent_control_scan_agent_config.c @@ -0,0 +1,384 @@ +/* Copyright (C) 2025 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief GVM GMP layer: Modify Agent-Controller scan-agent configuration. + * + */ + +#if ENABLE_AGENTS + +#include "gmp_agent_control_scan_agent_config.h" +#include "manage_agent_control_scan_config.h" +#include "manage_sql.h" + + +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "md gmp" + +/* ---------- MODIFY_AGENT_CONTROL_SCAN_CONFIG ---------- */ +/** + * @brief The modify_agent_control_scan_config command. + */ +typedef struct +{ + context_data_t *ctx; +} modify_scan_cfg_ctx_t; + +static modify_scan_cfg_ctx_t modify_scan_cfg_ctx; + +/** + * @brief Reset command data. + */ +static void +modify_agent_control_scan_config_reset (void) +{ + if (modify_scan_cfg_ctx.ctx && modify_scan_cfg_ctx.ctx->first) + { + free_entity (modify_scan_cfg_ctx.ctx->first->data); + g_slist_free_1 (modify_scan_cfg_ctx.ctx->first); + } + g_free (modify_scan_cfg_ctx.ctx); + memset (&modify_scan_cfg_ctx, 0, sizeof (modify_scan_cfg_ctx)); +} + +/** + * @brief Handle command start. + * + * @param[in] gmp_parser Active GMP parser (unused). + * @param[in] attribute_names Null-terminated array of attribute names. + * @param[in] attribute_values Null-terminated array of attribute values. + */ +void +modify_agent_control_scan_config_start (gmp_parser_t *gmp_parser, + const gchar **attribute_names, + const gchar **attribute_values) +{ + (void) gmp_parser; + (void) attribute_names; + (void) attribute_values; + memset (&modify_scan_cfg_ctx, 0, sizeof (modify_scan_cfg_ctx)); + modify_scan_cfg_ctx.ctx = g_malloc0 (sizeof (context_data_t)); + xml_handle_start_element (modify_scan_cfg_ctx.ctx, + "modify_agent_control_scan_config", + attribute_names, + attribute_values); +} + +/** + * @brief Handle command start element. + * + * @param[in] gmp_parser Active GMP parser (unused). + * @param[in] name Element name. + * @param[in] attribute_names Null-terminated array of attribute names. + * @param[in] attribute_values Null-terminated array of attribute values. + */ +void +modify_agent_control_scan_config_element_start (gmp_parser_t *gmp_parser, + const gchar *name, + const gchar **attribute_names, + const gchar **attribute_values) +{ + xml_handle_start_element (modify_scan_cfg_ctx.ctx, name, + attribute_names, attribute_values); +} + +/** + * @brief Add text to element in the command + * + * @param[in] text the text to add. + * @param[in] len the length of the text being added. + */ +void +modify_agent_control_scan_config_element_text (const gchar *text, gsize len) +{ + xml_handle_text (modify_scan_cfg_ctx.ctx, text, len); +} + +/** + * @brief Run modify_agent_control_scan_config command + * + * @param[in] gmp_parser current instance of GMP parser. + * @param[in] error the errors, if any. + */ +void +modify_agent_control_scan_config_run (gmp_parser_t *gmp_parser, GError **error) +{ + const char *scanner_uuid; + entity_t root = (entity_t) modify_scan_cfg_ctx.ctx->first->data; + + scanner_uuid = entity_attribute (root, "agent_control_id"); + + if (!scanner_uuid || !is_uuid (scanner_uuid)) + { + SENDF_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("modify_agent_control_scan_config", + "Missing or invalid scanner UUID")); + modify_agent_control_scan_config_reset (); + return; + } + + scanner_t scanner = 0; + if (find_scanner_with_permission (scanner_uuid, &scanner, "get_scanners")) + { + if (send_find_error_to_client ("modify_agent_control_scan_config", + "scanner", NULL, gmp_parser)) + error_send_to_client (error); + modify_agent_control_scan_config_reset (); + return; + } + if (scanner == 0) + { + if (send_find_error_to_client ("modify_agent_control_scan_config", + "scanner", NULL, gmp_parser)) + error_send_to_client (error); + modify_agent_control_scan_config_reset (); + return; + } + + int type = scanner_type (scanner); + if (type != SCANNER_TYPE_AGENT_CONTROLLER && + type != SCANNER_TYPE_AGENT_CONTROLLER_SENSOR) + { + SENDF_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("modify_agent_control_scan_config", + "Scanner is not an Agent Controller")); + modify_agent_control_scan_config_reset (); + return; + } + + /* … */ + entity_t cfg_e = entity_child (root, "config"); + if (!cfg_e) + { + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("modify_agent_control_scan_config", + "Missing ")); + modify_agent_control_scan_config_reset (); + return; + } + + agent_controller_scan_agent_config_t cfg = + agent_controller_scan_agent_config_new (); + build_scan_agent_config_from_entity (cfg_e, cfg); + + GPtrArray *errs = NULL; + int rc = modify_agent_control_scan_config (scanner, cfg, &errs); + + switch (rc) + { + case 0: + /* Success */ + SENDF_TO_CLIENT_OR_FAIL (XML_OK ("modify_agent_control_scan_config")); + log_event ("agent_control_scan_config", "Agent Control Scan Config", + scanner_uuid, "modified"); + modify_agent_control_scan_config_reset (); + return; + + case -1: + /* Invalid arguments (scanner == 0 or cfg == NULL) */ + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("modify_agent_control_scan_config", + "Invalid arguments: missing scanner or ")); + log_event_fail ("agent_control_scan_config", "Agent Control Scan Config", + scanner_uuid, "modified"); + modify_agent_control_scan_config_reset (); + return; + + case -2: + /* Connector creation failed */ + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_UNAVAILABLE ("modify_agent_control_scan_config", + "Could not connect to Agent-Controller")); + log_event_fail ("agent_control_scan_config", "Agent Control Scan Config", + scanner_uuid, "modified"); + modify_agent_control_scan_config_reset (); + return; + + case -3: + gchar *status_text = concat_error_messages ( + errs, "; ", "Validation failed for config: "); + if (!status_text) + status_text = g_markup_escape_text ("Validation failed for config.", + -1); + + gchar *xml = g_markup_printf_escaped ( + "", + status_text ? status_text : "Validation failed for ." + ); + + if (send_to_client (xml, gmp_parser->client_writer, + gmp_parser->client_writer_data)) + error_send_to_client (error); + + g_free (xml); + g_free (status_text); + if (errs) + g_ptr_array_free (errs, TRUE); + log_event_fail ("agent_control_scan_config", "Agent Control Scan Config", + scanner_uuid, "modified"); + modify_agent_control_scan_config_reset (); + return; + + case -4: + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_UNAVAILABLE ("modify_agent_control_scan_config", + "Agent-Controller update failed")); + log_event_fail ("agent_control_scan_config", "Agent Control Scan Config", + scanner_uuid, "modified"); + modify_agent_control_scan_config_reset (); + return; + + default: + SEND_TO_CLIENT_OR_FAIL ( + XML_INTERNAL_ERROR ("modify_agent_control_scan_config")); + log_event_fail ("agent_control_scan_config", "Agent Control Scan Config", + scanner_uuid, "modified"); + modify_agent_control_scan_config_reset (); + return; + } + + SENDF_TO_CLIENT_OR_FAIL (XML_OK ("modify_agent_control_scan_config")); + modify_agent_control_scan_config_reset (); +} + +/** + * @brief End element in the command + * + * + * @param[in] gmp_parser current instance of GMP parser. + * @param[in] error the errors, if any. + * @param[in] name name of element. + * + * @return 1 if the command ran successfully, 0 otherwise. + */ +int +modify_agent_control_scan_config_element_end (gmp_parser_t *gmp_parser, + GError **error, + const gchar *name) +{ + xml_handle_end_element (modify_scan_cfg_ctx.ctx, name); + if (modify_scan_cfg_ctx.ctx->done) + { + modify_agent_control_scan_config_run (gmp_parser, error); + return 1; + } + return 0; +} + +/** + * @brief Populate an Agent-Controller scan config from a subtree. + * + * @param[in] root Entity node representing the subtree + * (i.e., the parent of , , + * and elements). + * @param[in,out] out_cfg Pre-allocated config object to populate. Must not be NULL. + * + * @return 0 on success; -1 if @p out_cfg is NULL. + */ +int +build_scan_agent_config_from_entity ( + entity_t root, + agent_controller_scan_agent_config_t out_cfg) +{ + if (!out_cfg) + return -1; + + entity_t e = NULL; + + /* … */ + entity_t ac = entity_child (root, "agent_control"); + if (ac) + { + entity_t retry = entity_child (ac, "retry"); + if (retry) + { + e = entity_child (retry, "attempts"); + if (e) + out_cfg->agent_control.retry.attempts = + atoi (entity_text (e) ? entity_text (e) : "0"); + + e = entity_child (retry, "delay_in_seconds"); + if (e) + out_cfg->agent_control.retry.delay_in_seconds = + atoi (entity_text (e) ? entity_text (e) : "0"); + + e = entity_child (retry, "max_jitter_in_seconds"); + if (e) + out_cfg->agent_control.retry.max_jitter_in_seconds = + atoi (entity_text (e) ? entity_text (e) : "0"); + } + } + + /* … */ + entity_t se = entity_child (root, "agent_script_executor"); + if (se) + { + e = entity_child (se, "bulk_size"); + if (e) + out_cfg->agent_script_executor.bulk_size = + atoi (entity_text (e) ? entity_text (e) : "0"); + + e = entity_child (se, "bulk_throttle_time_in_ms"); + if (e) + out_cfg->agent_script_executor.bulk_throttle_time_in_ms = + atoi (entity_text (e) ? entity_text (e) : "0"); + + e = entity_child (se, "indexer_dir_depth"); + if (e) + out_cfg->agent_script_executor.indexer_dir_depth = + atoi (entity_text (e) ? entity_text (e) : "0"); + + e = entity_child (se, "period_in_seconds"); + if (e) + out_cfg->agent_script_executor.period_in_seconds = + atoi (entity_text (e) ? entity_text (e) : "0"); + + /* ...... */ + entity_t sct = entity_child (se, "scheduler_cron_time"); + if (sct) + { + GPtrArray *arr = g_ptr_array_new_with_free_func (g_free); + for (GSList *n = sct->entities; n; n = n->next) + { + entity_t it = n->data; + if (it && strcmp (entity_name (it), "item") == 0) + { + const gchar *txt = entity_text (it); + g_ptr_array_add (arr, g_strdup (txt ? txt : "")); + } + } + if (arr->len == 0) + g_ptr_array_free (arr, TRUE); + else + out_cfg->agent_script_executor.scheduler_cron_time = arr; + } + } + + /* … */ + entity_t hb = entity_child (root, "heartbeat"); + if (hb) + { + e = entity_child (hb, "interval_in_seconds"); + if (e) + out_cfg->heartbeat.interval_in_seconds = + atoi (entity_text (e) ? entity_text (e) : "0"); + + e = entity_child (hb, "miss_until_inactive"); + if (e) + out_cfg->heartbeat.miss_until_inactive = + atoi (entity_text (e) ? entity_text (e) : "0"); + } + + return 0; +} + + +#endif /* ENABLE_AGENTS */ \ No newline at end of file diff --git a/src/gmp_agent_control_scan_agent_config.h b/src/gmp_agent_control_scan_agent_config.h new file mode 100644 index 0000000000..73c4849a69 --- /dev/null +++ b/src/gmp_agent_control_scan_agent_config.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2025 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief GVM GMP layer: Modify Agent-Controller scan-agent configuration. + * + */ + +#ifndef _GVMD_GMP_AGENT_CONTROL_SCAN_AGENT_CONFIG_H +#define _GVMD_GMP_AGENT_CONTROL_SCAN_AGENT_CONFIG_H + +#include "gmp_base.h" + +#include +#include + +/* -------- MODIFY_AGENT_CONTROL_SCAN_CONFIG -------- */ + +void +modify_agent_control_scan_config_start (gmp_parser_t *gmp_parser, + const gchar **attribute_names, + const gchar **attribute_values); + +void +modify_agent_control_scan_config_element_start (gmp_parser_t *gmp_parser, + const gchar *name, + const gchar **attribute_names, + const gchar **attribute_values); + +void +modify_agent_control_scan_config_element_text (const gchar *text, gsize len); + +int +modify_agent_control_scan_config_element_end (gmp_parser_t *gmp_parser, + GError **error, + const gchar *name); + +void +modify_agent_control_scan_config_run (gmp_parser_t *gmp_parser, GError **error); + +int +build_scan_agent_config_from_entity ( + entity_t root, + agent_controller_scan_agent_config_t out_cfg); + +#endif /* _GVMD_GMP_AGENT_CONTROL_SCAN_AGENT_CONFIG_H */ diff --git a/src/gmp_agents.c b/src/gmp_agents.c index 0f88e3dd22..215672ff63 100644 --- a/src/gmp_agents.c +++ b/src/gmp_agents.c @@ -14,6 +14,8 @@ */ #include "gmp_agents.h" + +#include "gmp_agent_control_scan_agent_config.h" #include "gmp_get.h" #include "manage.h" @@ -26,19 +28,23 @@ * @struct get_agents_t * @brief Structure for storing data related to the GMP command. * - * This structure holds generic data needed for handling agent retrieval operations. + * This structure holds generic data needed for handling agent retrieval + * operations. */ typedef struct { - get_data_t get; ///< Parameters and context for the get operation (e.g., filters, format). + get_data_t get; } get_agents_t; + static get_agents_t get_agents_data; /** * @struct modify_agent_data_t - * @brief Structure for storing context related to the GMP command. + * @brief Structure for storing context related to the GMP + * command. * - * The context is used to accumulate and parse XML input data for modifying agents. + * The context is used to accumulate and parse XML input data for modifying + * agents. */ typedef struct { @@ -49,7 +55,8 @@ static modify_agent_data_t modify_agent_data; /** * @struct delete_agent_data_t - * @brief Structure for storing context related to the GMP command. + * @brief Structure for storing context related to the GMP + * command. */ typedef struct { @@ -73,7 +80,8 @@ get_agents_reset () * @brief Initialize the GMP command by parsing attributes. * * @param[in] attribute_names Null-terminated array of attribute names. - * @param[in] attribute_values Null-terminated array of corresponding attribute values. + * @param[in] attribute_values Null-terminated array of corresponding attribute + * values. */ void get_agents_start (const gchar **attribute_names, const gchar **attribute_values) @@ -95,13 +103,11 @@ get_agents_run (gmp_parser_t *gmp_parser, GError **error) iterator_t agents; int count = 0, filtered, ret, first; - ret = init_get ("get_agents", - &get_agents_data.get, - "Agents", - &first); + ret = init_get ("get_agents", &get_agents_data.get, "Agents", &first); if (ret) { - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("get_agents", "Permission denied")); + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("get_agents", "Permission denied")); get_agents_reset (); return; } @@ -109,7 +115,8 @@ get_agents_run (gmp_parser_t *gmp_parser, GError **error) ret = init_agent_iterator (&agents, &get_agents_data.get); if (ret) { - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("get_agents", "Permission denied")); + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("get_agents", "Permission denied")); get_agents_reset (); return; } @@ -135,50 +142,142 @@ get_agents_run (gmp_parser_t *gmp_parser, GError **error) scanner = agent_iterator_scanner (&agents); agent_scanner_uuid = scanner_uuid (scanner); agent_scanner_name = scanner_name (scanner); - SEND_GET_COMMON_NO_TRASH (agent, - &get_agents_data.get, - &agents); + SEND_GET_COMMON_NO_TRASH (agent, &get_agents_data.get, &agents); // Remaining fields - SENDF_TO_CLIENT_OR_FAIL ("%s" - "%s" - "%i" - "%i" - "%i" - "%s" - "%s" - "%s" - "" - "%s" - "", - agent_iterator_hostname (&agents), - agent_iterator_agent_id (&agents), - agent_iterator_authorized (&agents), - agent_iterator_min_interval (&agents), - agent_iterator_heartbeat_interval (&agents), - agent_iterator_connection_status (&agents), - iso_if_time (agent_iterator_last_update (&agents)), - agent_iterator_schedule (&agents), - agent_scanner_uuid ? agent_scanner_uuid : "", - agent_scanner_name ? agent_scanner_name : ""); + SENDF_TO_CLIENT_OR_FAIL ( + "%s" + "%s" + "%i" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%s" + "%i" + "" + "%s" + "", + agent_iterator_hostname (&agents), agent_iterator_agent_id (&agents), + agent_iterator_authorized (&agents), + agent_iterator_connection_status (&agents), + iso_if_time (agent_iterator_last_update (&agents)), + iso_if_time (agent_iterator_last_updater_heartbeat (&agents)), + agent_iterator_updater_version (&agents), + agent_iterator_agent_version (&agents), + agent_iterator_operating_system (&agents), + agent_iterator_architecture (&agents), + agent_iterator_update_to_latest (&agents), + agent_scanner_uuid ? agent_scanner_uuid : "", + agent_scanner_name ? agent_scanner_name : ""); // IPs - agent_ip_data_list_t ip_list = load_agent_ip_addresses (agent_iterator_agent_id (&agents)); + agent_ip_data_list_t ip_list = + load_agent_ip_addresses (agent_iterator_agent_id (&agents)); if (ip_list) { for (int i = 0; i < ip_list->count; ++i) { - SENDF_TO_CLIENT_OR_FAIL ("%s", ip_list->items[i]->ip_address); + SENDF_TO_CLIENT_OR_FAIL ("%s", + ip_list->items[i]->ip_address); } agent_ip_data_list_free (ip_list); } + const char *cfg_json = agent_iterator_config (&agents); + + if (!cfg_json || !*cfg_json) + { + SEND_TO_CLIENT_OR_FAIL (""); + } + else + { + agent_controller_scan_agent_config_t cfg = + agent_controller_parse_scan_agent_config_string (cfg_json); + + if (!cfg) + { + SEND_TO_CLIENT_OR_FAIL (""); + } + else + { + SEND_TO_CLIENT_OR_FAIL (""); + + /* agent_control/retry */ + SEND_TO_CLIENT_OR_FAIL (""); + SENDF_TO_CLIENT_OR_FAIL ("%d", + cfg->agent_control.retry.attempts); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_control.retry.delay_in_seconds); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_control.retry.max_jitter_in_seconds); + SEND_TO_CLIENT_OR_FAIL (""); + + /* agent_script_executor */ + SEND_TO_CLIENT_OR_FAIL (""); + SENDF_TO_CLIENT_OR_FAIL ("%d", + cfg->agent_script_executor.bulk_size); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_script_executor.bulk_throttle_time_in_ms); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_script_executor.indexer_dir_depth); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->agent_script_executor.period_in_seconds); + + /* scheduler_cron_time list */ + { + GPtrArray *cron = + cfg->agent_script_executor.scheduler_cron_time; + if (cron && cron->len > 0) + { + SEND_TO_CLIENT_OR_FAIL ( + ""); + for (guint k = 0; k < cron->len; ++k) + { + const char *item = (const char *) cron->pdata[k]; + gchar *esc = + g_markup_escape_text (item ? item : "", -1); + SENDF_TO_CLIENT_OR_FAIL ("%s", esc); + g_free (esc); + } + SEND_TO_CLIENT_OR_FAIL (""); + } + else + { + SEND_TO_CLIENT_OR_FAIL ( + ""); + } + } + SEND_TO_CLIENT_OR_FAIL (""); + + /* heartbeat */ + SEND_TO_CLIENT_OR_FAIL (""); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->heartbeat.interval_in_seconds); + SENDF_TO_CLIENT_OR_FAIL ( + "%d", + cfg->heartbeat.miss_until_inactive); + SEND_TO_CLIENT_OR_FAIL (""); + + SEND_TO_CLIENT_OR_FAIL (""); + + agent_controller_scan_agent_config_free (cfg); + } + } // Close agent SEND_TO_CLIENT_OR_FAIL (""); count++; - g_free(agent_scanner_name); - g_free(agent_scanner_uuid); + g_free (agent_scanner_name); + g_free (agent_scanner_uuid); } cleanup_iterator (&agents); @@ -187,8 +286,8 @@ get_agents_run (gmp_parser_t *gmp_parser, GError **error) SEND_GET_END ("agent", &get_agents_data.get, count, filtered); #else - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ("get_agents", - "Command unavailable")); + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_UNAVAILABLE ("get_agents", "Command unavailable")); #endif get_agents_reset (); @@ -222,14 +321,11 @@ modify_agents_reset () * @param[in] attribute_values Null-terminated array of attribute values. */ void -modify_agents_element_start (gmp_parser_t *gmp_parser, - const gchar *name, +modify_agents_element_start (gmp_parser_t *gmp_parser, const gchar *name, const gchar **attribute_names, const gchar **attribute_values) { - xml_handle_start_element (modify_agent_data.context, - name, - attribute_names, + xml_handle_start_element (modify_agent_data.context, name, attribute_names, attribute_values); } @@ -238,18 +334,18 @@ modify_agents_element_start (gmp_parser_t *gmp_parser, * * @param[in] gmp_parser Pointer to the GMP parser instance. * @param[in] attribute_names Null-terminated array of attribute names. - * @param[in] attribute_values Null-terminated array of corresponding attribute values. + * @param[in] attribute_values Null-terminated array of corresponding attribute + * values. */ void -modify_agents_start (gmp_parser_t *gmp_parser, - const gchar **attribute_names, +modify_agents_start (gmp_parser_t *gmp_parser, const gchar **attribute_names, const gchar **attribute_values) { memset (&modify_agent_data, 0, sizeof (modify_agent_data_t)); modify_agent_data.context = g_malloc0 (sizeof (context_data_t)); - modify_agents_element_start (gmp_parser, "modify_agents", - attribute_names, attribute_values); + modify_agents_element_start (gmp_parser, "modify_agents", attribute_names, + attribute_values); } /** @@ -274,8 +370,7 @@ modify_agents_element_text (const gchar *text, gsize text_len) * @return 1 if the command has been fully parsed and executed, 0 otherwise. */ int -modify_agents_element_end (gmp_parser_t *gmp_parser, - GError **error, +modify_agents_element_end (gmp_parser_t *gmp_parser, GError **error, const gchar *name) { xml_handle_end_element (modify_agent_data.context, name); @@ -304,7 +399,8 @@ modify_agents_run (gmp_parser_t *gmp_parser, GError **error) entity_t agents_elem = entity_child (root, "agents"); if (!agents_elem) { - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("modify_agents", "Missing ")); + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("modify_agents", "Missing ")); log_event_fail ("agents", "Agents", NULL, "modified"); modify_agents_reset (); return; @@ -352,104 +448,135 @@ modify_agents_run (gmp_parser_t *gmp_parser, GError **error) entity_t e = NULL; if ((e = entity_child (root, "authorized"))) update->authorized = atoi (entity_text (e)); - if ((e = entity_child (root, "min_interval"))) - update->min_interval = atoi (entity_text (e)); - if ((e = entity_child (root, "heartbeat_interval"))) - update->heartbeat_interval = atoi (entity_text (e)); - if ((e = entity_child (root, "schedule"))) + entity_t cfg_e = entity_child (root, "config"); + + if (cfg_e) { - update->schedule_config = agent_controller_config_schedule_new (); - update->schedule_config->schedule = g_strdup (entity_text (e)); + agent_controller_scan_agent_config_t cfg = + agent_controller_scan_agent_config_new (); + if (!cfg) + { + agent_uuid_list_free (agent_uuids); + agent_controller_agent_update_free (update); + g_free (comment); + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); + modify_agents_reset (); + return; + } + + build_scan_agent_config_from_entity (cfg_e, cfg); + update->config = cfg; } + if ((e = entity_child (root, "comment"))) comment = g_strdup (entity_text (e)); - agent_response_t response = modify_and_resync_agents (agent_uuids, update, comment); + GPtrArray *errs = NULL; + agent_response_t response = + modify_and_resync_agents (agent_uuids, update, comment, &errs); switch (response) { - case AGENT_RESPONSE_SUCCESS: - SENDF_TO_CLIENT_OR_FAIL (XML_OK ("modify_agents")); - log_event_plural ("agents", "Agents", NULL, "modified"); - break; + case AGENT_RESPONSE_SUCCESS: + SENDF_TO_CLIENT_OR_FAIL (XML_OK ("modify_agents")); + log_event_plural ("agents", "Agents", NULL, "modified"); + break; - case AGENT_RESPONSE_NO_AGENTS_PROVIDED: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("modify_agents", "No agents provided")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + case AGENT_RESPONSE_NO_AGENTS_PROVIDED: + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("modify_agents", "No agents provided")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; - case AGENT_RESPONSE_SCANNER_LOOKUP_FAILED: - if (send_find_error_to_client ("modify_agents", - "scanner", - NULL, - gmp_parser)) - { - error_send_to_client (error); - modify_agents_reset (); - return; - } - - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + case AGENT_RESPONSE_SCANNER_LOOKUP_FAILED: + if (send_find_error_to_client ("modify_agents", "scanner", NULL, + gmp_parser)) + { + error_send_to_client (error); + modify_agents_reset (); + return; + } - case AGENT_RESPONSE_AGENT_NOT_FOUND: - if (send_find_error_to_client ("modify_agents", - "agents", - NULL, - gmp_parser)) - { - error_send_to_client (error); - modify_agents_reset (); - return; - } - - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + log_event_fail ("agents", "Agents", NULL, "modified"); + break; - case AGENT_RESPONSE_INVALID_ARGUMENT: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); - break; + case AGENT_RESPONSE_AGENT_NOT_FOUND: + if (send_find_error_to_client ("modify_agents", "agents", NULL, + gmp_parser)) + { + error_send_to_client (error); + modify_agents_reset (); + return; + } - case AGENT_RESPONSE_INVALID_AGENT_OWNER: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + log_event_fail ("agents", "Agents", NULL, "modified"); + break; - case AGENT_RESPONSE_AGENT_SCANNER_MISMATCH: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("modify_agents", "Agents belong to different scanners")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + case AGENT_RESPONSE_INVALID_ARGUMENT: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); + break; - case AGENT_RESPONSE_CONNECTOR_CREATION_FAILED: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( - "modify_agents", - "Could not connect to Agent-Controller")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + case AGENT_RESPONSE_INVALID_AGENT_OWNER: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; - case AGENT_RESPONSE_CONTROLLER_UPDATE_FAILED: + case AGENT_RESPONSE_AGENT_SCANNER_MISMATCH: + SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ( + "modify_agents", "Agents belong to different scanners")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; + + case AGENT_RESPONSE_CONNECTOR_CREATION_FAILED: SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( - "modify_agents", - "Updates of Agents in Agent-Controller failed")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + "modify_agents", "Could not connect to Agent-Controller")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; - case AGENT_RESPONSE_SYNC_FAILED: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( - "modify_agents", - "Synchronization of Agents in Agent-Controller failed")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + case AGENT_RESPONSE_CONTROLLER_UPDATE_FAILED: + SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( + "modify_agents", "Updates of Agents in Agent-Controller failed")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; - case AGENT_RESPONSE_INTERNAL_ERROR: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; - case AGENT_RESPONSE_IN_USE_ERROR: - default: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); - log_event_fail ("agents", "Agents", NULL, "modified"); - break; + case AGENT_RESPONSE_SYNC_FAILED: + SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( + "modify_agents", + "Synchronization of Agents in Agent-Controller failed")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; + case AGENT_RESPONSE_CONTROLLER_UPDATE_REJECTED: + gchar *status_text = concat_error_messages ( + errs, "; ", "Validation failed for config: "); + if (!status_text) + status_text = g_markup_escape_text ("Validation failed for config.", -1); + + gchar *xml = g_markup_printf_escaped( + "", + status_text ? status_text : "Validation failed for ." + ); + + if (send_to_client(xml, gmp_parser->client_writer, gmp_parser->client_writer_data)) + error_send_to_client(error); + + g_free(xml); + g_free(status_text); + if (errs) g_ptr_array_free(errs, TRUE); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; + + case AGENT_RESPONSE_INTERNAL_ERROR: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; + + case AGENT_RESPONSE_IN_USE_ERROR: + default: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agents")); + log_event_fail ("agents", "Agents", NULL, "modified"); + break; } agent_uuid_list_free (agent_uuids); @@ -457,8 +584,8 @@ modify_agents_run (gmp_parser_t *gmp_parser, GError **error) g_free (comment); #else - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ("modify_agents", - "Command unavailable")); + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_UNAVAILABLE ("modify_agents", "Command unavailable")); #endif // ENABLE_AGENTS modify_agents_reset (); @@ -492,34 +619,32 @@ delete_agent_reset () * @param[in] attribute_values Null-terminated array of attribute values. */ void -delete_agents_element_start (gmp_parser_t *gmp_parser, - const gchar *name, +delete_agents_element_start (gmp_parser_t *gmp_parser, const gchar *name, const gchar **attribute_names, const gchar **attribute_values) { - xml_handle_start_element (delete_agent_data.context, - name, - attribute_names, + xml_handle_start_element (delete_agent_data.context, name, attribute_names, attribute_values); } /** * @brief Initialize the GMP command. * - * @param[in] gmp_parser Pointer to the GMP parser handling the current session. + * @param[in] gmp_parser Pointer to the GMP parser handling the current + * session. * @param[in] attribute_names Null-terminated array of attribute names. - * @param[in] attribute_values Null-terminated array of corresponding attribute values. + * @param[in] attribute_values Null-terminated array of corresponding attribute + * values. */ void -delete_agents_start (gmp_parser_t *gmp_parser, - const gchar **attribute_names, +delete_agents_start (gmp_parser_t *gmp_parser, const gchar **attribute_names, const gchar **attribute_values) { memset (&delete_agent_data, 0, sizeof (delete_agent_data_t)); delete_agent_data.context = g_malloc0 (sizeof (context_data_t)); - delete_agents_element_start (gmp_parser, "delete_agents", - attribute_names, attribute_values); + delete_agents_element_start (gmp_parser, "delete_agents", attribute_names, + attribute_values); } /** @@ -537,7 +662,8 @@ delete_agents_element_text (const gchar *text, gsize text_len) /** * @brief Handle the end of an XML element within the command. * - * @param[in] gmp_parser Pointer to the GMP parser handling the current session. + * @param[in] gmp_parser Pointer to the GMP parser handling the current + * session. * @param[in] error Pointer to a GError to store error details, if any. * @param[in] name Name of the XML element that just ended. * @@ -545,8 +671,7 @@ delete_agents_element_text (const gchar *text, gsize text_len) * 0 otherwise. */ int -delete_agents_element_end (gmp_parser_t *gmp_parser, - GError **error, +delete_agents_element_end (gmp_parser_t *gmp_parser, GError **error, const gchar *name) { xml_handle_end_element (delete_agent_data.context, name); @@ -564,7 +689,8 @@ delete_agents_element_end (gmp_parser_t *gmp_parser, * @brief Execute the GMP command. * * @param[in] gmp_parser Pointer to the GMP parser handling the current session. - * @param[in] error Pointer to a GError to store error information, if any occurs. + * @param[in] error Pointer to a GError to store error information, if any + * occurs. */ void delete_agents_run (gmp_parser_t *gmp_parser, GError **error) @@ -576,7 +702,8 @@ delete_agents_run (gmp_parser_t *gmp_parser, GError **error) entity_t agents_elem = entity_child (root, "agents"); if (!agents_elem) { - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("delete_agents", "Missing ")); + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("delete_agents", "Missing ")); delete_agent_reset (); return; } @@ -621,107 +748,101 @@ delete_agents_run (gmp_parser_t *gmp_parser, GError **error) switch (response) { - case AGENT_RESPONSE_SUCCESS: - SENDF_TO_CLIENT_OR_FAIL (XML_OK ("delete_agents")); - log_event_plural ("agents", "Agents", NULL, "deleted"); - break; - - case AGENT_RESPONSE_NO_AGENTS_PROVIDED: - if (send_find_error_to_client ("delete_agents", - "agents", - NULL, - gmp_parser)) - { - error_send_to_client (error); - modify_agents_reset (); - return; - } - - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; - case AGENT_RESPONSE_AGENT_NOT_FOUND: - if (send_find_error_to_client ("modify_agents", - "agents", - NULL, - gmp_parser)) - { - error_send_to_client (error); - modify_agents_reset (); - return; - } - - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; - case AGENT_RESPONSE_SCANNER_LOOKUP_FAILED: - if (send_find_error_to_client ("delete_agents", - "scanner", - NULL, - gmp_parser)) - { - error_send_to_client (error); - modify_agents_reset (); - return; - } - - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + case AGENT_RESPONSE_SUCCESS: + SENDF_TO_CLIENT_OR_FAIL (XML_OK ("delete_agents")); + log_event_plural ("agents", "Agents", NULL, "deleted"); + break; + + case AGENT_RESPONSE_NO_AGENTS_PROVIDED: + if (send_find_error_to_client ("delete_agents", "agents", NULL, + gmp_parser)) + { + error_send_to_client (error); + modify_agents_reset (); + return; + } - case AGENT_RESPONSE_INVALID_ARGUMENT: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; + case AGENT_RESPONSE_AGENT_NOT_FOUND: + if (send_find_error_to_client ("modify_agents", "agents", NULL, + gmp_parser)) + { + error_send_to_client (error); + modify_agents_reset (); + return; + } - case AGENT_RESPONSE_INVALID_AGENT_OWNER: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; + case AGENT_RESPONSE_SCANNER_LOOKUP_FAILED: + if (send_find_error_to_client ("delete_agents", "scanner", NULL, + gmp_parser)) + { + error_send_to_client (error); + modify_agents_reset (); + return; + } - case AGENT_RESPONSE_AGENT_SCANNER_MISMATCH: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("delete_agents", "Agents belong to different scanners")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; - case AGENT_RESPONSE_CONNECTOR_CREATION_FAILED: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( - "delete_agents", - "Could not connect to Agent-Controller")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + case AGENT_RESPONSE_INVALID_ARGUMENT: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; - case AGENT_RESPONSE_CONTROLLER_DELETE_FAILED: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( - "delete_agents", - "Deletion of Agents in Agent-Controller failed")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + case AGENT_RESPONSE_INVALID_AGENT_OWNER: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; - case AGENT_RESPONSE_SYNC_FAILED: - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( - "delete_agents", - "Synchronization of Agents in Agent-Controller failed")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + case AGENT_RESPONSE_AGENT_SCANNER_MISMATCH: + SEND_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ( + "delete_agents", "Agents belong to different scanners")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; - case AGENT_RESPONSE_INTERNAL_ERROR: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + case AGENT_RESPONSE_CONNECTOR_CREATION_FAILED: + SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( + "delete_agents", "Could not connect to Agent-Controller")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; - case AGENT_RESPONSE_IN_USE_ERROR: - SENDF_TO_CLIENT_OR_FAIL (XML_ERROR_SYNTAX ("delete_agents", "Resource is in use")); - log_event_fail ("agent", "Agents", NULL, "deleted"); - break; + case AGENT_RESPONSE_CONTROLLER_DELETE_FAILED: + SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( + "delete_agents", "Deletion of Agents in Agent-Controller failed")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; - default: - SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); - log_event_fail ("agents", "Agents", NULL, "deleted"); - break; + case AGENT_RESPONSE_SYNC_FAILED: + SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ( + "delete_agents", + "Synchronization of Agents in Agent-Controller failed")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; + + case AGENT_RESPONSE_INTERNAL_ERROR: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; + + case AGENT_RESPONSE_IN_USE_ERROR: + SENDF_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("delete_agents", "Resource is in use")); + log_event_fail ("agent", "Agents", NULL, "deleted"); + break; + + default: + SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("delete_agents")); + log_event_fail ("agents", "Agents", NULL, "deleted"); + break; } agent_uuid_list_free (agent_uuids); #else - SEND_TO_CLIENT_OR_FAIL (XML_ERROR_UNAVAILABLE ("delete_agents", - "Command unavailable")); + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_UNAVAILABLE ("delete_agents", "Command unavailable")); #endif delete_agent_reset (); } \ No newline at end of file diff --git a/src/gmp_agents.h b/src/gmp_agents.h index 46da71a246..6682c0da6c 100644 --- a/src/gmp_agents.h +++ b/src/gmp_agents.h @@ -17,8 +17,7 @@ /* GET_AGENTS. */ void -get_agents_start (const gchar **, - const gchar **); +get_agents_start (const gchar **, const gchar **); void get_agents_run (gmp_parser_t *gmp_parser, GError **error); @@ -26,22 +25,19 @@ get_agents_run (gmp_parser_t *gmp_parser, GError **error); /* MODIFY_AGENT. */ void -modify_agents_element_start (gmp_parser_t *gmp_parser, - const gchar *name, +modify_agents_element_start (gmp_parser_t *gmp_parser, const gchar *name, const gchar **attribute_names, const gchar **attribute_values); void -modify_agents_start (gmp_parser_t *gmp_parser, - const gchar **attribute_names, +modify_agents_start (gmp_parser_t *gmp_parser, const gchar **attribute_names, const gchar **attribute_values); void modify_agents_element_text (const gchar *text, gsize text_len); int -modify_agents_element_end (gmp_parser_t *gmp_parser, - GError **error, +modify_agents_element_end (gmp_parser_t *gmp_parser, GError **error, const gchar *name); void @@ -50,22 +46,19 @@ modify_agents_run (gmp_parser_t *gmp_parser, GError **error); /* DELETE_AGENTS. */ void -delete_agents_element_start (gmp_parser_t *gmp_parser, - const gchar *name, +delete_agents_element_start (gmp_parser_t *gmp_parser, const gchar *name, const gchar **attribute_names, const gchar **attribute_values); void -delete_agents_start (gmp_parser_t *gmp_parser, - const gchar **attribute_names, +delete_agents_start (gmp_parser_t *gmp_parser, const gchar **attribute_names, const gchar **attribute_values); void delete_agents_element_text (const gchar *text, gsize text_len); int -delete_agents_element_end (gmp_parser_t *gmp_parser, - GError **error, +delete_agents_element_end (gmp_parser_t *gmp_parser, GError **error, const gchar *name); void diff --git a/src/manage.h b/src/manage.h index 039ec860be..5005d1fc5c 100644 --- a/src/manage.h +++ b/src/manage.h @@ -56,6 +56,7 @@ #if ENABLE_AGENTS #include +#include "manage_agent_control_scan_config.h" #include "manage_agent_groups.h" #include "manage_agents.h" #endif diff --git a/src/manage_agent_common.c b/src/manage_agent_common.c index 588fc7e2d1..def2a95738 100644 --- a/src/manage_agent_common.c +++ b/src/manage_agent_common.c @@ -10,6 +10,9 @@ #if ENABLE_AGENTS #include "manage_agent_common.h" +#include "manage_sql.h" + +#include #undef G_LOG_DOMAIN /** @@ -27,15 +30,15 @@ agent_uuid_list_t agent_uuid_list_new (int count) { - if (count <= 0) - return NULL; + if (count <= 0) + return NULL; - agent_uuid_list_t list = g_malloc0 (sizeof (struct agent_uuid_list)); + agent_uuid_list_t list = g_malloc0 (sizeof (struct agent_uuid_list)); - list->count = count; - list->agent_uuids = g_malloc0 (sizeof (gchar *) * (count + 1)); + list->count = count; + list->agent_uuids = g_malloc0 (sizeof (gchar *) * (count + 1)); - return list; + return list; } /** @@ -46,14 +49,94 @@ agent_uuid_list_new (int count) void agent_uuid_list_free (agent_uuid_list_t uuid_list) { - if (!uuid_list) - return; + if (!uuid_list) + return; + + for (int i = 0; i < uuid_list->count; ++i) + g_free (uuid_list->agent_uuids[i]); + + g_free (uuid_list->agent_uuids); + g_free (uuid_list); +} + +/** + * @brief Initialize a new GVMD agent connector from a scanner. + * + * Builds and configures a connection to the agent controller using + * scanner information. + * + * @param[in] scanner Scanner ID used to resolve connection info. + * @return Allocated gvmd_agent_connector_t or NULL on failure. + */ +gvmd_agent_connector_t +gvmd_agent_connector_new_from_scanner (scanner_t scanner) +{ + assert (scanner); + + gboolean has_relay = scanner_has_relay (scanner); + char *host = scanner_host (scanner, has_relay); + int port = scanner_port (scanner, has_relay); + char *ca_cert = scanner_ca_pub (scanner); + char *cert = scanner_key_pub (scanner); + char *key = scanner_key_priv (scanner); + + if (!host || port <= 0) + { + g_warning ("%s: Invalid scanner host or port", __func__); + g_free (host); + g_free (ca_cert); + g_free (cert); + g_free (key); + return NULL; + } - for (int i = 0; i < uuid_list->count; ++i) - g_free (uuid_list->agent_uuids[i]); + const char *protocol = "https"; + if (!ca_cert || !cert) + { + g_debug ("%s: Falling back to HTTP due to missing CA or cert", __func__); + protocol = "http"; + } - g_free (uuid_list->agent_uuids); - g_free (uuid_list); + gvmd_agent_connector_t conn = + g_malloc0 (sizeof (struct gvmd_agent_connector)); + conn->base = agent_controller_connector_new (); + + agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_HOST, host); + agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_PORT, &port); + agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_PROTOCOL, + protocol); + + if (ca_cert) + agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_CA_CERT, + ca_cert); + if (cert) + agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_CERT, + cert); + if (key) + agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_KEY, key); + + conn->scanner_id = scanner; + + g_free (host); + g_free (ca_cert); + g_free (cert); + g_free (key); + + return conn; +} + +/** + * @brief Free a GVMD agent connector. + * + * @param[in] conn GVMD agent connector to free. + */ +void +gvmd_agent_connector_free (gvmd_agent_connector_t conn) +{ + if (!conn) + return; + agent_controller_connector_free (conn->base); + g_free (conn); } #endif // ENABLE_AGENTS \ No newline at end of file diff --git a/src/manage_agent_common.h b/src/manage_agent_common.h index 83983d6ad5..3f001fa3ce 100644 --- a/src/manage_agent_common.h +++ b/src/manage_agent_common.h @@ -15,19 +15,42 @@ #ifndef _GVMD_MANAGE_AGENT_COMMON_H #define _GVMD_MANAGE_AGENT_COMMON_H +#include "iterator.h" +#include "manage_get.h" +#include "manage_resources.h" +#include "manage_utils.h" + +#include #include +/** + * @struct gvmd_agent_connector + * @brief Holds scanner context and base agent controller connection. + */ +struct gvmd_agent_connector +{ + agent_controller_connector_t base; ///< Original gvm-libs connector + scanner_t scanner_id; ///< GVMD-specific scanner id +}; +typedef struct gvmd_agent_connector *gvmd_agent_connector_t; + /** * @struct agent_uuid_list * @brief A structure to store a list of agent UUIDs. */ struct agent_uuid_list { - int count; ///< Number of UUIDs in the list - gchar **agent_uuids; ///< Array of UUID strings + int count; ///< Number of UUIDs in the list + gchar **agent_uuids; ///< Array of UUID strings }; typedef struct agent_uuid_list *agent_uuid_list_t; +gvmd_agent_connector_t +gvmd_agent_connector_new_from_scanner (scanner_t scanner); + +void +gvmd_agent_connector_free (gvmd_agent_connector_t conn); + agent_uuid_list_t agent_uuid_list_new (int count); diff --git a/src/manage_agent_control_scan_config.c b/src/manage_agent_control_scan_config.c new file mode 100644 index 0000000000..794a6bb4ea --- /dev/null +++ b/src/manage_agent_control_scan_config.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2025 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief Manage layer: Agent Controller scan-agent configuration. + * + * Functions for retrieving and updating the scan-agent configuration + * stored by an Agent Controller scanner. + */ + +#if ENABLE_AGENTS +#include "manage_agent_control_scan_config.h" + +#undef G_LOG_DOMAIN +/** + * @brief GLib log domain. + */ +#define G_LOG_DOMAIN "md manage" + +/** + * @brief Retrieve the scan agent configuration for a given scanner. + * + * @param[in] scanner Scanner row id whose agent config is requested. + * + * @return A newly allocated #agent_controller_scan_agent_config_t on success; + * NULL if @p scanner is invalid, no configuration is available, or an + * allocation/parse error occurs. + */ +agent_controller_scan_agent_config_t +get_agent_control_scan_config (scanner_t scanner) +{ + gvmd_agent_connector_t connector = NULL; + agent_controller_scan_agent_config_t agent_config = NULL; + + if (scanner == 0) + return NULL; + + connector = gvmd_agent_connector_new_from_scanner (scanner); + + if (!connector) + return NULL; + agent_config = agent_controller_get_scan_agent_config (connector->base); + if (!agent_config) + { + gvmd_agent_connector_free (connector); + return NULL; + } + + gvmd_agent_connector_free (connector); + + return agent_config; +} + +/** + * @brief Modify (persist/propagate) the scan agent configuration for a scanner. + * + * @param[in] scanner Scanner row identifier (must be non-zero). + * @param[in] cfg New configuration to apply (must be non-NULL). + * + * @return + * 0 on success (configuration accepted by the Agent Controller). + * -1 invalid arguments (either @p scanner == 0 or @p cfg == NULL). + * -2 failed to create a connector for @p scanner + * (e.g., scanner not found/misconfigured). + * -3 Agent Controller update rejected with validation (error propagated from + * agent_controller_update_scan_agent_config(), e.g., validation). + * -4 Agent Controller update failed (error propagated from + * agent_controller_update_scan_agent_config(), + * communication failure). + */ +int +modify_agent_control_scan_config (scanner_t scanner, + agent_controller_scan_agent_config_t cfg, + GPtrArray **errors) +{ + int ret = 0; + gvmd_agent_connector_t connector = NULL; + + /* Avoid returning stale error arrays from previous calls */ + if (errors) + *errors = NULL; + + if (!scanner || !cfg) + return -1; + + connector = gvmd_agent_connector_new_from_scanner (scanner); + if (!connector) + { + ret = -2; + goto cleanup; + } + + int rc = agent_controller_update_scan_agent_config ( + connector->base, cfg, errors); + + if (rc == AGENT_RESP_OK) + { + ret = 0; + } + else if (errors && *errors && (*errors)->len > 0) + { + g_warning ("%s: Agent Controller rejected scan-agent-config update", + __func__); + ret = -3; + } + else + { + g_warning ("%s: Agent Controller update failed (no details)", __func__); + ret = -4; + } + +cleanup: + if (connector) + gvmd_agent_connector_free (connector); + + agent_controller_scan_agent_config_free (cfg); + + return ret; +} +#endif // ENABLE_AGENTS \ No newline at end of file diff --git a/src/manage_agent_control_scan_config.h b/src/manage_agent_control_scan_config.h new file mode 100644 index 0000000000..4731155408 --- /dev/null +++ b/src/manage_agent_control_scan_config.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2025 Greenbone AG + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * @file + * @brief Manage layer: Agent Controller scan-agent configuration. + * + * Functions for retrieving and updating the scan-agent configuration + * stored by an Agent Controller scanner. + */ + +#if ENABLE_AGENTS +#ifndef _GVMD_MANAGE_AGENT_SCAN_CONFIG_H +#define _GVMD_MANAGE_AGENT_SCAN_CONFIG_H + +#include "manage_agent_common.h" + +agent_controller_scan_agent_config_t +get_agent_control_scan_config(scanner_t scanner); + +int +modify_agent_control_scan_config(scanner_t scanner, + agent_controller_scan_agent_config_t cfg, + GPtrArray** errors); + +#endif //_GVMD_MANAGE_AGENT_SCAN_CONFIG_H +#endif // ENABLE_AGENTS diff --git a/src/manage_agents.c b/src/manage_agents.c index fc0544d405..1c628f99c3 100644 --- a/src/manage_agents.c +++ b/src/manage_agents.c @@ -30,9 +30,9 @@ * * @param[in] count Number of agent entries to allocate space for. * - * @return A newly allocated agent_data_list_t on success, or NULL on memory allocation failure. + * @return A newly allocated agent_data_list_t on success, or NULL on memory + * allocation failure. */ - static agent_data_list_t agent_data_list_new (int count) { @@ -44,6 +44,95 @@ agent_data_list_new (int count) return list; } +/** + * @brief Deep-copy a GPtrArray of char*. + * + * @param[in] src Source pointer array to duplicate (may be NULL). + * + * @return A newly allocated #GPtrArray on success; NULL if @p src is NULL + * or if an allocation fails. + */ +static GPtrArray * +dup_str_ptr_array (const GPtrArray *src) +{ + if (!src) + return NULL; + + GPtrArray *dst = g_ptr_array_sized_new (src->len); + if (!dst) + return NULL; + + g_ptr_array_set_free_func (dst, g_free); + + for (guint i = 0; i < src->len; ++i) + { + const char *s = g_ptr_array_index ((GPtrArray *) src, i); + char *copy = s ? g_strdup (s) : NULL; + + if (s && !copy) + { + g_ptr_array_free (dst, TRUE); + return NULL; + } + + g_ptr_array_add (dst, copy); + } + + return dst; +} + +/** + * @brief Deep-copy a scan agent configuration structure. + * + * @param[in] src Source configuration to copy. May be NULL. + * + * @return A newly allocated #agent_controller_scan_agent_config_t on success; + * NULL if @p src is NULL or if any allocation fails. On failure, any + * partially allocated memory is released. + */ +static agent_controller_scan_agent_config_t +copy_agent_controller_scan_agent_config ( + agent_controller_scan_agent_config_t src) +{ + if (!src) + return NULL; + + agent_controller_scan_agent_config_t dst = + agent_controller_scan_agent_config_new (); + + /* Plain struct copies */ + dst->agent_control.retry.attempts = src->agent_control.retry.attempts; + dst->agent_control.retry.delay_in_seconds = + src->agent_control.retry.delay_in_seconds; + dst->agent_control.retry.max_jitter_in_seconds = + src->agent_control.retry.max_jitter_in_seconds; + + dst->agent_script_executor.bulk_size = src->agent_script_executor.bulk_size; + dst->agent_script_executor.bulk_throttle_time_in_ms = + src->agent_script_executor.bulk_throttle_time_in_ms; + dst->agent_script_executor.indexer_dir_depth = + src->agent_script_executor.indexer_dir_depth; + dst->agent_script_executor.period_in_seconds = + src->agent_script_executor.period_in_seconds; + + dst->heartbeat.interval_in_seconds = src->heartbeat.interval_in_seconds; + dst->heartbeat.miss_until_inactive = src->heartbeat.miss_until_inactive; + + /* Deep copy of GPtrArray */ + dst->agent_script_executor.scheduler_cron_time = + dup_str_ptr_array (src->agent_script_executor.scheduler_cron_time); + + if (src->agent_script_executor.scheduler_cron_time + && !dst->agent_script_executor.scheduler_cron_time) + { + /* allocation failed – clean up partial dst */ + agent_controller_scan_agent_config_free (dst); + return NULL; + } + + return dst; +} + /** * @brief Populate GVMD agent data list from agent controller list. * @@ -52,14 +141,16 @@ agent_data_list_new (int count) * * @param[in] list List of agents from the agent controller. * @param[in] scanner Scanner ID associated with these agents. - * @param[out] out_list Pre-allocated agent_data_list_t with `count` matching `list->count`. + * @param[out] out_list Pre-allocated agent_data_list_t with `count` matching + * `list->count`. * - * @return AGENT_RESPONSE_SUCCESS on success, or an appropriate AGENT_RESPONSE_* error code. + * @return AGENT_RESPONSE_SUCCESS on success, or an appropriate AGENT_RESPONSE_* + * error code. */ static agent_response_t -convert_agent_control_list_to_agent_data_list (agent_controller_agent_list_t list, - scanner_t scanner, - agent_data_list_t out_list) +convert_agent_control_list_to_agent_data_list ( + agent_controller_agent_list_t list, scanner_t scanner, + agent_data_list_t out_list) { if (!list || list->count == 0 || !out_list) return AGENT_RESPONSE_INVALID_ARGUMENT; @@ -87,15 +178,14 @@ convert_agent_control_list_to_agent_data_list (agent_controller_agent_list_t lis dest->agent_id = g_strdup (src->agent_id); dest->hostname = g_strdup (src->hostname); dest->authorized = src->authorized; - dest->min_interval = src->min_interval; - dest->heartbeat_interval = src->heartbeat_interval; dest->connection_status = g_strdup (src->connection_status); dest->last_update_agent_control = src->last_update; - - if (src->schedule_config && src->schedule_config->schedule) - dest->schedule = g_strdup (src->schedule_config->schedule); - else - dest->schedule = g_strdup (""); + dest->config = copy_agent_controller_scan_agent_config (src->config); + dest->updater_version = g_strdup (src->updater_version); + dest->agent_version = g_strdup (src->agent_version); + dest->operating_system = g_strdup (src->operating_system); + dest->architecture = g_strdup (src->architecture); + dest->update_to_latest = src->update_to_latest; dest->scanner = scanner; @@ -104,15 +194,18 @@ convert_agent_control_list_to_agent_data_list (agent_controller_agent_list_t lis { dest->ip_addresses = agent_ip_data_list_new (src->ip_address_count); dest->ip_addresses->count = src->ip_address_count; - dest->ip_addresses->items = g_malloc0 (sizeof (agent_ip_data_t) * src->ip_address_count); + dest->ip_addresses->items = + g_malloc0 (sizeof (agent_ip_data_t) * src->ip_address_count); for (int j = 0; j < src->ip_address_count; ++j) { if (!src->ip_addresses[j]) continue; - dest->ip_addresses->items[j] = g_malloc0 (sizeof (struct agent_ip_data)); - dest->ip_addresses->items[j]->ip_address = g_strdup (src->ip_addresses[j]); + dest->ip_addresses->items[j] = + g_malloc0 (sizeof (struct agent_ip_data)); + dest->ip_addresses->items[j]->ip_address = + g_strdup (src->ip_addresses[j]); } } else @@ -136,8 +229,8 @@ convert_agent_control_list_to_agent_data_list (agent_controller_agent_list_t lis * Transforms internal GVMD agent records into a format understood * by the agent controller for update or deletion operations. * - * The caller is responsible for freeing the populated `agent_controller_agent_list_t` - * using the appropriate cleanup function. + * The caller is responsible for freeing the populated + * `agent_controller_agent_list_t` using the appropriate cleanup function. * * @param[in] list Source list of GVMD agent_data_t entries. * @param[out] out_list Target agent controller list to populate. @@ -145,8 +238,8 @@ convert_agent_control_list_to_agent_data_list (agent_controller_agent_list_t lis * @return AGENT_RESPONSE_SUCCESS on success, otherwise an error code on failure */ static agent_response_t -convert_agent_data_list_to_agent_control_list (agent_data_list_t list, - agent_controller_agent_list_t out_list) +convert_agent_data_list_to_agent_control_list ( + agent_data_list_t list, agent_controller_agent_list_t out_list) { if (!list || list->count == 0) return AGENT_RESPONSE_INVALID_ARGUMENT; @@ -159,27 +252,27 @@ convert_agent_data_list_to_agent_control_list (agent_data_list_t list, dest->agent_id = g_strdup (src->agent_id); dest->hostname = g_strdup (src->hostname); dest->authorized = src->authorized; - dest->min_interval = src->min_interval; - dest->heartbeat_interval = src->heartbeat_interval; dest->connection_status = g_strdup (src->connection_status); dest->last_update = src->last_update_agent_control; - - // Schedule config - if (src->schedule && strlen (src->schedule) > 0) - { - dest->schedule_config = agent_controller_config_schedule_new (); - dest->schedule_config->schedule = g_strdup (src->schedule); - } + dest->last_updater_heartbeat = src->last_updater_heartbeat; + dest->config = copy_agent_controller_scan_agent_config (src->config); + dest->updater_version = g_strdup (src->updater_version); + dest->agent_version = g_strdup (src->agent_version); + dest->operating_system = g_strdup (src->operating_system); + dest->architecture = g_strdup (src->architecture); + dest->last_update = src->update_to_latest; // IP addresses if (src->ip_addresses && src->ip_addresses->count > 0) { dest->ip_address_count = src->ip_addresses->count; - dest->ip_addresses = g_malloc0 (sizeof (char *) * dest->ip_address_count); + dest->ip_addresses = + g_malloc0 (sizeof (char *) * dest->ip_address_count); for (int j = 0; j < dest->ip_address_count; ++j) { - dest->ip_addresses[j] = g_strdup (src->ip_addresses->items[j]->ip_address); + dest->ip_addresses[j] = + g_strdup (src->ip_addresses->items[j]->ip_address); } } @@ -198,12 +291,13 @@ convert_agent_data_list_to_agent_control_list (agent_data_list_t list, * @param[in] agent_uuids List of agent UUIDs to retrieve. * @param[out] out_list Output list for agent controller formatted agents. * - * @return AGENT_RESPONSE_SUCCESS on success, or an appropriate error code on failure. + * @return AGENT_RESPONSE_SUCCESS on success, or an appropriate error code on + * failure. */ static agent_response_t get_agent_controller_agents_from_uuids (scanner_t scanner, - agent_uuid_list_t agent_uuids, - agent_controller_agent_list_t out_list) + agent_uuid_list_t agent_uuids, + agent_controller_agent_list_t out_list) { if (!scanner) { @@ -224,9 +318,8 @@ get_agent_controller_agents_from_uuids (scanner_t scanner, } agent_data_list_t agent_data_list = agent_data_list_new (agent_uuids->count); - agent_response_t get_agent_result = get_agents_by_scanner_and_uuids (scanner, - agent_uuids, - agent_data_list); + agent_response_t get_agent_result = + get_agents_by_scanner_and_uuids (scanner, agent_uuids, agent_data_list); if (get_agent_result != 0) { agent_data_list_free (agent_data_list); @@ -235,7 +328,8 @@ get_agent_controller_agents_from_uuids (scanner_t scanner, return get_agent_result; } - int convert_result = convert_agent_data_list_to_agent_control_list (agent_data_list, out_list); + int convert_result = + convert_agent_data_list_to_agent_control_list (agent_data_list, out_list); if (convert_result != AGENT_RESPONSE_SUCCESS) { @@ -249,7 +343,8 @@ get_agent_controller_agents_from_uuids (scanner_t scanner, } /** - * @brief Maps the return value of get_scanner_from_agent_uuid() to agent_response_t. + * @brief Maps the return value of get_scanner_from_agent_uuid() to + * agent_response_t. * * @param[in] result Return code from get_scanner_from_agent_uuid(). * @@ -275,81 +370,6 @@ map_get_scanner_result_to_agent_response (int result) } } -/** - * @brief Initialize a new GVMD agent connector from a scanner. - * - * Builds and configures a connection to the agent controller using - * scanner information. - * - * @param[in] scanner Scanner ID used to resolve connection info. - * @return Allocated gvmd_agent_connector_t or NULL on failure. - */ -gvmd_agent_connector_t -gvmd_agent_connector_new_from_scanner (scanner_t scanner) -{ - assert (scanner); - - gboolean has_relay = scanner_has_relay (scanner); - char *host = scanner_host (scanner, has_relay); - int port = scanner_port (scanner, has_relay); - char *ca_cert = scanner_ca_pub (scanner); - char *cert = scanner_key_pub (scanner); - char *key = scanner_key_priv (scanner); - - if (!host || port <= 0) - { - g_warning ("%s: Invalid scanner host or port", __func__); - g_free (host); - g_free (ca_cert); - g_free (cert); - g_free (key); - return NULL; - } - - const char *protocol = "https"; - if (!ca_cert || !cert) - { - g_debug ("%s: Falling back to HTTP due to missing CA or cert", __func__); - protocol = "http"; - } - - gvmd_agent_connector_t conn = g_malloc0 (sizeof (struct gvmd_agent_connector)); - conn->base = agent_controller_connector_new (); - - agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_HOST, host); - agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_PORT, &port); - agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_PROTOCOL, protocol); - - if (ca_cert) - agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_CA_CERT, ca_cert); - if (cert) - agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_CERT, cert); - if (key) - agent_controller_connector_builder (conn->base, AGENT_CONTROLLER_KEY, key); - - conn->scanner_id = scanner; - - g_free (host); - g_free (ca_cert); - g_free (cert); - g_free (key); - - return conn; -} - -/** - * @brief Free a GVMD agent connector. - * - * @param[in] conn GVMD agent connector to free. - */ -void -gvmd_agent_connector_free (gvmd_agent_connector_t conn) -{ - if (!conn) return; - agent_controller_connector_free (conn->base); - g_free (conn); -} - /** * @brief Free an agent_ip_data_list_t and its contents. * @@ -368,24 +388,25 @@ agent_ip_data_list_free (agent_ip_data_list_t ip_list) if (ip_list->items[i]) { if (ip_list->items[i]->ip_address) - g_free(ip_list->items[i]->ip_address); + g_free (ip_list->items[i]->ip_address); - g_free(ip_list->items[i]); + g_free (ip_list->items[i]); ip_list->items[i] = NULL; } } - g_free(ip_list->items); + g_free (ip_list->items); ip_list->items = NULL; } - g_free(ip_list); + g_free (ip_list); } /** * @brief Allocate and initialize an agent_ip_data_list_t structure. * * @param[in] count Number of IP address items to allocate. - * @return A newly allocated agent_ip_data_list_t, or NULL on allocation failure. + * @return A newly allocated agent_ip_data_list_t, or NULL on allocation + * failure. */ agent_ip_data_list_t agent_ip_data_list_new (int count) @@ -441,8 +462,20 @@ agent_data_free (agent_data_t data) if (data->connection_status) g_free (data->connection_status); - if (data->schedule) - g_free (data->schedule); + if (data->config) + agent_controller_scan_agent_config_free (data->config); + + if (data->updater_version) + g_free (data->updater_version); + + if (data->agent_version) + g_free (data->agent_version); + + if (data->operating_system) + g_free (data->operating_system); + + if (data->architecture) + g_free (data->architecture); if (data->comment) g_free (data->comment); @@ -481,7 +514,8 @@ agent_data_list_free (agent_data_list_t agents) * Gets all agent information from the agent controller * and saves it into GVMD's internal database. * - * @param[in] connector An initialized agent controller connector with scanner ID. + * @param[in] connector An initialized agent controller connector with scanner + * ID. * * @return AGENT_RESPONSE_SUCCESS on success, * or a specific AGENT_RESPONSE_* error code on failure. @@ -504,13 +538,13 @@ sync_agents_from_agent_controller (gvmd_agent_connector_t connector) return AGENT_RESPONSE_SUCCESS; } - agent_data_list_t agent_data_list = agent_data_list_new (agent_controller_agents->count); - agent_response_t convert_result = convert_agent_control_list_to_agent_data_list ( - agent_controller_agents, - connector->scanner_id, - agent_data_list); + agent_data_list_t agent_data_list = + agent_data_list_new (agent_controller_agents->count); + agent_response_t convert_result = + convert_agent_control_list_to_agent_data_list ( + agent_controller_agents, connector->scanner_id, agent_data_list); - if (convert_result!= AGENT_RESPONSE_SUCCESS) + if (convert_result != AGENT_RESPONSE_SUCCESS) { agent_data_list_free (agent_data_list); agent_controller_agent_list_free (agent_controller_agents); @@ -542,15 +576,13 @@ sync_agents_from_agent_controller (gvmd_agent_connector_t connector) * @param[in] uuid_list List of agent UUIDs to look up. * @param[out] out_list Output list to populate with matching agents. * -* @return AGENT_RESPONSE_SUCCESS on success, + * @return AGENT_RESPONSE_SUCCESS on success, * or a specific AGENT_RESPONSE_* error code on failure. */ agent_response_t -get_agents_by_scanner_and_uuids (scanner_t scanner, - agent_uuid_list_t uuid_list, +get_agents_by_scanner_and_uuids (scanner_t scanner, agent_uuid_list_t uuid_list, agent_data_list_t out_list) { - if (!out_list || !uuid_list || out_list->count == 0 || uuid_list->count == 0) return AGENT_RESPONSE_INVALID_ARGUMENT; @@ -565,25 +597,37 @@ get_agents_by_scanner_and_uuids (scanner_t scanner, if (agent_iterator_scanner (&iterator) != scanner) { cleanup_iterator (&iterator); - return AGENT_RESPONSE_AGENT_SCANNER_MISMATCH; // Mismatch found, early exit + return AGENT_RESPONSE_AGENT_SCANNER_MISMATCH; } agent_data_t agent = g_malloc0 (sizeof (struct agent_data)); if (!agent) continue; + /* config conversion */ + const gchar *cfg_str = g_strdup (agent_iterator_config (&iterator)); + agent_controller_scan_agent_config_t config = + agent_controller_parse_scan_agent_config_string (cfg_str); + agent->row_id = iterator_int64 (&iterator, 0); agent->agent_id = g_strdup (agent_iterator_agent_id (&iterator)); agent->hostname = g_strdup (agent_iterator_hostname (&iterator)); agent->authorized = agent_iterator_authorized (&iterator); - agent->min_interval = agent_iterator_min_interval (&iterator); - agent->heartbeat_interval = agent_iterator_heartbeat_interval (&iterator); - agent->connection_status = g_strdup (agent_iterator_connection_status (&iterator)); + agent->connection_status = + g_strdup (agent_iterator_connection_status (&iterator)); agent->last_update_agent_control = agent_iterator_last_update (&iterator); - agent->schedule = g_strdup (agent_iterator_schedule (&iterator)); + agent->config = config; agent->comment = g_strdup (get_iterator_comment (&iterator)); agent->creation_time = get_iterator_creation_time (&iterator); agent->modification_time = get_iterator_modification_time (&iterator); + agent->updater_version = + g_strdup (agent_iterator_updater_version (&iterator)); + agent->agent_version = + g_strdup (agent_iterator_agent_version (&iterator)); + agent->operating_system = + g_strdup (agent_iterator_operating_system (&iterator)); + agent->architecture = g_strdup (agent_iterator_architecture (&iterator)); + agent->update_to_latest = agent_iterator_update_to_latest (&iterator); agent->scanner = scanner; agent->owner = get_iterator_owner (&iterator); agent->uuid = g_strdup (get_iterator_uuid (&iterator)); @@ -608,8 +652,8 @@ get_agents_by_scanner_and_uuids (scanner_t scanner, * Sends update instructions for the selected agents and re-synchronizes * their state from the agent controller. * -* @param[in] agent_uuids List of agent UUIDs to be modified. - * @param[in] agent_update Update parameters to apply (e.g., authorize/revoke). + * @param[in] agent_uuids List of agent UUIDs to be modified. + * @param[in] agent_update Update parameters to apply (e.g. authorize/revoke). * @param[in] comment Optional comment string to apply to agents in GVMD. * * @return AGENT_RESPONSE_SUCCESS on success, @@ -618,7 +662,8 @@ get_agents_by_scanner_and_uuids (scanner_t scanner, agent_response_t modify_and_resync_agents (agent_uuid_list_t agent_uuids, agent_controller_agent_update_t agent_update, - const gchar *comment) + const gchar *comment, + GPtrArray **errors) { scanner_t scanner = 0; agent_controller_agent_list_t agent_control_list = NULL; @@ -630,15 +675,14 @@ modify_and_resync_agents (agent_uuid_list_t agent_uuids, } int ret = get_scanner_from_agent_uuid (agent_uuids->agent_uuids[0], &scanner); - agent_response_t map_response = map_get_scanner_result_to_agent_response (ret); + agent_response_t map_response = + map_get_scanner_result_to_agent_response (ret); if (map_response != AGENT_RESPONSE_SUCCESS) return map_response; agent_control_list = agent_controller_agent_list_new (agent_uuids->count); agent_response_t get_response = get_agent_controller_agents_from_uuids ( - scanner, - agent_uuids, - agent_control_list); + scanner, agent_uuids, agent_control_list); if (get_response != AGENT_RESPONSE_SUCCESS) { agent_controller_agent_list_free (agent_control_list); @@ -655,9 +699,16 @@ modify_and_resync_agents (agent_uuid_list_t agent_uuids, } int update_result = agent_controller_update_agents ( - connector->base, - agent_control_list, - agent_update); + connector->base, agent_control_list, agent_update, errors); + + if (update_result < 0 && errors && *errors && (*errors)->len > 0) + { + g_warning ("%s: agent_controller_update_agents rejected", __func__); + agent_controller_agent_list_free (agent_control_list); + gvmd_agent_connector_free (connector); + manage_option_cleanup (); + return AGENT_RESPONSE_CONTROLLER_UPDATE_REJECTED; + } if (update_result < 0) { @@ -668,8 +719,8 @@ modify_and_resync_agents (agent_uuid_list_t agent_uuids, return AGENT_RESPONSE_CONTROLLER_UPDATE_FAILED; } - if (comment) - update_agents_comment (agent_uuids, comment); + if (comment) + update_agents_comment (agent_uuids, comment); agent_response_t result = sync_agents_from_agent_controller (connector); if (result != AGENT_RESPONSE_SUCCESS) @@ -713,15 +764,14 @@ delete_and_resync_agents (agent_uuid_list_t agent_uuids) } int ret = get_scanner_from_agent_uuid (agent_uuids->agent_uuids[0], &scanner); - agent_response_t map_response = map_get_scanner_result_to_agent_response (ret); + agent_response_t map_response = + map_get_scanner_result_to_agent_response (ret); if (map_response != AGENT_RESPONSE_SUCCESS) return map_response; agent_control_list = agent_controller_agent_list_new (agent_uuids->count); agent_response_t get_result = get_agent_controller_agents_from_uuids ( - scanner, - agent_uuids, - agent_control_list); + scanner, agent_uuids, agent_control_list); if (get_result != AGENT_RESPONSE_SUCCESS) { agent_controller_agent_list_free (agent_control_list); @@ -744,9 +794,8 @@ delete_and_resync_agents (agent_uuid_list_t agent_uuids) return AGENT_RESPONSE_CONNECTOR_CREATION_FAILED; } - int update_result = agent_controller_delete_agents ( - connector->base, - agent_control_list); + int update_result = + agent_controller_delete_agents (connector->base, agent_control_list); if (update_result < 0) { diff --git a/src/manage_agents.h b/src/manage_agents.h index ad278d484e..a1ca2206b4 100644 --- a/src/manage_agents.h +++ b/src/manage_agents.h @@ -20,24 +20,11 @@ #include "iterator.h" #include "manage_agent_common.h" -#include "manage_get.h" -#include "manage_resources.h" #include typedef resource_t agent_t; -/** - * @struct gvmd_agent_connector - * @brief Holds scanner context and base agent controller connection. - */ -struct gvmd_agent_connector -{ - agent_controller_connector_t base; ///< Original gvm-libs connector - scanner_t scanner_id; ///< GVMD-specific scanner id -}; -typedef struct gvmd_agent_connector *gvmd_agent_connector_t; - /** * @struct agent_ip_data * @brief Represents a single IP address associated with an agent. @@ -66,23 +53,27 @@ typedef struct agent_ip_data_list *agent_ip_data_list_t; struct agent_data { agent_t row_id; - gchar * uuid; - gchar * name; + gchar *uuid; + gchar *name; gchar *agent_id; gchar *hostname; int authorized; - int min_interval; - int heartbeat_interval; gchar *connection_status; agent_ip_data_list_t ip_addresses; int ip_address_count; time_t creation_time; time_t modification_time; time_t last_update_agent_control; - gchar *schedule; + time_t last_updater_heartbeat; + agent_controller_scan_agent_config_t config; gchar *comment; user_t owner; scanner_t scanner; + gchar *updater_version; + gchar *agent_version; + gchar *operating_system; + gchar *architecture; + int update_to_latest; }; typedef struct agent_data *agent_data_t; @@ -97,28 +88,24 @@ struct agent_data_list }; typedef struct agent_data_list *agent_data_list_t; -typedef enum { - AGENT_RESPONSE_SUCCESS = 0, ///< Success - AGENT_RESPONSE_NO_AGENTS_PROVIDED = -1, ///< No agent UUIDs provided - AGENT_RESPONSE_SCANNER_LOOKUP_FAILED = -2, ///< Scanner lookup failed - AGENT_RESPONSE_AGENT_SCANNER_MISMATCH = -3, ///< Agent list count mismatch (not same scanner) - AGENT_RESPONSE_CONNECTOR_CREATION_FAILED = -4, ///< Failed to create connector - AGENT_RESPONSE_CONTROLLER_UPDATE_FAILED = -5, ///< Failed to update agents - AGENT_RESPONSE_CONTROLLER_DELETE_FAILED = -6, ///< Failed to delete agents - AGENT_RESPONSE_SYNC_FAILED = -7, ///< Failed during sync - AGENT_RESPONSE_INVALID_ARGUMENT = -8, ///< Failed invalid argument - AGENT_RESPONSE_INVALID_AGENT_OWNER = -9, ///< Failed getting owner UUID - AGENT_RESPONSE_AGENT_NOT_FOUND = -10, ///< Failed getting owner UUID - AGENT_RESPONSE_INTERNAL_ERROR = -11, ///< Internal error - AGENT_RESPONSE_IN_USE_ERROR = -12 ///< Agent is used by an Agent Group +typedef enum +{ + AGENT_RESPONSE_SUCCESS = 0, ///< Success + AGENT_RESPONSE_NO_AGENTS_PROVIDED = -1, ///< No agent UUIDs provided + AGENT_RESPONSE_SCANNER_LOOKUP_FAILED = -2, ///< Scanner lookup failed + AGENT_RESPONSE_AGENT_SCANNER_MISMATCH = -3, ///< Agent list count mismatch (not same scanner) + AGENT_RESPONSE_CONNECTOR_CREATION_FAILED = -4, ///< Failed to create connector + AGENT_RESPONSE_CONTROLLER_UPDATE_FAILED = -5, ///< Failed to update agents + AGENT_RESPONSE_CONTROLLER_DELETE_FAILED = -6, ///< Failed to delete agents + AGENT_RESPONSE_SYNC_FAILED = -7, ///< Failed during sync + AGENT_RESPONSE_INVALID_ARGUMENT = -8, ///< Failed invalid argument + AGENT_RESPONSE_INVALID_AGENT_OWNER = -9, ///< Failed getting owner UUID + AGENT_RESPONSE_AGENT_NOT_FOUND = -10, ///< Failed getting owner UUID + AGENT_RESPONSE_INTERNAL_ERROR = -11, ///< Internal error + AGENT_RESPONSE_IN_USE_ERROR = -12, ///< Agent is used by an Agent Group + AGENT_RESPONSE_CONTROLLER_UPDATE_REJECTED = -13 ///< Agent update validation error } agent_response_t; -gvmd_agent_connector_t -gvmd_agent_connector_new_from_scanner (scanner_t scanner); - -void -gvmd_agent_connector_free (gvmd_agent_connector_t conn); - void agent_ip_data_list_free (agent_ip_data_list_t ip_list); @@ -138,14 +125,14 @@ agent_response_t sync_agents_from_agent_controller (gvmd_agent_connector_t connector); agent_response_t -get_agents_by_scanner_and_uuids (scanner_t scanner, - agent_uuid_list_t uuid_list, +get_agents_by_scanner_and_uuids (scanner_t scanner, agent_uuid_list_t uuid_list, agent_data_list_t out_list); agent_response_t modify_and_resync_agents (agent_uuid_list_t agent_uuids, agent_controller_agent_update_t agent_update, - const gchar *comment); + const gchar *comment, + GPtrArray **errors); agent_response_t delete_and_resync_agents (agent_uuid_list_t agent_uuids); @@ -170,23 +157,35 @@ const gchar * agent_iterator_connection_status (iterator_t *iterator); const gchar * -agent_iterator_schedule (iterator_t *iterator); +agent_iterator_config (iterator_t *iterator); int agent_iterator_authorized (iterator_t *iterator); -int -agent_iterator_min_interval (iterator_t *iterator); - -int -agent_iterator_heartbeat_interval (iterator_t *iterator); - time_t agent_iterator_last_update (iterator_t *iterator); +time_t +agent_iterator_last_updater_heartbeat (iterator_t *iterator); + scanner_t agent_iterator_scanner (iterator_t *iterator); +const gchar * +agent_iterator_updater_version (iterator_t *iterator); + +const gchar * +agent_iterator_agent_version (iterator_t *iterator); + +const gchar * +agent_iterator_operating_system (iterator_t *iterator); + +const gchar * +agent_iterator_architecture (iterator_t *iterator); + +int +agent_iterator_update_to_latest (iterator_t *iterator); + int agent_count (const get_data_t *get); @@ -197,7 +196,8 @@ int agent_in_use (agent_t agent); void -delete_agents_by_scanner_and_uuids (scanner_t scanner, agent_uuid_list_t agent_uuids); +delete_agents_by_scanner_and_uuids (scanner_t scanner, + agent_uuid_list_t agent_uuids); gboolean agents_in_use (agent_uuid_list_t agent_uuids); diff --git a/src/manage_commands.c b/src/manage_commands.c index 5a9e3b060c..ef9ab16ea2 100644 --- a/src/manage_commands.c +++ b/src/manage_commands.c @@ -134,6 +134,7 @@ command_t gmp_commands[] {"GET_VULNS", "Get all vulnerabilities."}, {"HELP", "Get this help text."}, #if ENABLE_AGENTS + {"MODIFY_AGENT_CONTROL_SCAN_CONFIG", "Modify an agent control scan agent configuration."}, {"MODIFY_AGENT_GROUP", "Modify an agent group."}, {"MODIFY_AGENTS", "Modify one or more existing agents."}, #endif /* ENABLE_AGENTS */ diff --git a/src/manage_pg.c b/src/manage_pg.c index 0a4b166e70..c83d6b0f52 100644 --- a/src/manage_pg.c +++ b/src/manage_pg.c @@ -2233,15 +2233,19 @@ create_tables () " scanner INTEGER NOT NULL REFERENCES scanners (id) ON DELETE RESTRICT," " hostname TEXT," " authorized INTEGER NOT NULL," - " min_interval INTEGER," - " heartbeat_interval INTEGER," " connection_status TEXT," " last_update INTEGER," - " schedule TEXT," + " last_updater_heartbeat INTEGER," + " config TEXT," " owner INTEGER REFERENCES users (id) ON DELETE RESTRICT," " comment TEXT," " creation_time INTEGER," - " modification_time INTEGER);"); + " modification_time INTEGER," + " updater_version TEXT," + " agent_version TEXT," + " operating_system TEXT," + " architecture TEXT," + " update_to_latest INTEGER);"); sql ("CREATE TABLE IF NOT EXISTS agent_ip_addresses" " (id SERIAL PRIMARY KEY," diff --git a/src/manage_sql_agents.c b/src/manage_sql_agents.c index c0bfe9af71..dc6031d072 100644 --- a/src/manage_sql_agents.c +++ b/src/manage_sql_agents.c @@ -17,6 +17,7 @@ #if ENABLE_AGENTS #include "manage_sql_agents.h" + #include "manage_sql_copy.h" #include @@ -36,8 +37,7 @@ static void delete_existing_agent_ips (const gchar *agent_id) { gchar *insert_agent_id = sql_insert (agent_id); - sql ("DELETE FROM agent_ip_addresses WHERE agent_id = %s;", - insert_agent_id); + sql ("DELETE FROM agent_ip_addresses WHERE agent_id = %s;", insert_agent_id); g_free (insert_agent_id); } @@ -58,8 +58,8 @@ agent_column_exists (const gchar *column_name, const gchar *value) } gchar *insert_value = sql_insert (value); - gchar *query = g_strdup_printf ( - "SELECT COUNT(*) FROM agents WHERE %s = %s;", column_name, insert_value); + gchar *query = g_strdup_printf ("SELECT COUNT(*) FROM agents WHERE %s = %s;", + column_name, insert_value); int result = sql_int (query); @@ -80,31 +80,51 @@ agent_column_exists (const gchar *column_name, const gchar *value) static void update_existing_agent (agent_data_t agent) { - gchar *insert_hostname = sql_insert (agent->hostname); - gchar *insert_connection_status = sql_insert (agent->connection_status); - gchar *insert_schedule = sql_insert (agent->schedule); - gchar *insert_agent_id = sql_insert (agent->agent_id); - - sql ("UPDATE agents SET hostname = %s, authorized = %d, min_interval = %d," - " heartbeat_interval = %d, connection_status = %s, last_update = %ld," - " schedule = %s, owner = %u, modification_time = %ld, scanner = %llu " - " WHERE agent_id = %s;", - insert_hostname, - agent->authorized, - agent->min_interval, - agent->heartbeat_interval, - insert_connection_status, - agent->last_update_agent_control, - insert_schedule, - agent->owner, - agent->modification_time, - agent->scanner, - insert_agent_id); + gchar *config_string = + agent_controller_convert_scan_agent_config_string (agent->config); + if (!config_string) + config_string = g_strdup (""); + + gchar *insert_hostname = sql_insert (agent->hostname); + gchar *insert_connection_status = sql_insert (agent->connection_status); + gchar *insert_config = sql_insert (config_string); + gchar *insert_agent_id = sql_insert (agent->agent_id); + gchar *insert_updater_version = sql_insert (agent->updater_version); + gchar *insert_agent_version = sql_insert (agent->agent_version); + gchar *insert_operating_system = sql_insert (agent->operating_system); + gchar *insert_architecture = sql_insert (agent->architecture); + + sql ("UPDATE agents SET " + " hostname = %s," + " authorized = %d," + " connection_status = %s," + " last_update = %ld," + " last_updater_heartbeat = %ld," + " config = %s," + " owner = %u," + " modification_time = %ld," + " scanner = %llu," + " updater_version = %s," + " agent_version = %s," + " operating_system = %s," + " architecture = %s," + " update_to_latest = %d" + " WHERE agent_id = %s;", + insert_hostname, agent->authorized, insert_connection_status, + agent->last_update_agent_control, agent->last_updater_heartbeat, + insert_config, agent->owner, agent->modification_time, agent->scanner, + insert_updater_version, insert_agent_version, insert_operating_system, + insert_architecture, agent->update_to_latest ? 1 : 0, insert_agent_id); g_free (insert_hostname); g_free (insert_connection_status); - g_free (insert_schedule); + g_free (insert_config); g_free (insert_agent_id); + g_free (insert_updater_version); + g_free (insert_agent_version); + g_free (insert_operating_system); + g_free (insert_architecture); + g_free (config_string); } /** @@ -122,35 +142,44 @@ append_agent_row_to_buffer (db_copy_buffer_t *buffer, agent_data_t agent) if (agent->uuid == NULL) return; } - gchar *escaped_hostname = sql_copy_escape (agent->hostname); - gchar *escaped_connection_status = sql_copy_escape (agent->connection_status); - gchar *escaped_schedule = sql_copy_escape (agent->schedule); - gchar *escaped_comment = sql_copy_escape (""); + + gchar *config_string = + agent_controller_convert_scan_agent_config_string (agent->config); + if (!config_string) + config_string = g_strdup (""); + + gchar *escaped_hostname = sql_copy_escape (agent->hostname); + gchar *escaped_connection_status = sql_copy_escape (agent->connection_status); + gchar *escaped_config = sql_copy_escape (config_string); + gchar *escaped_comment = sql_copy_escape (""); + + gchar *escaped_updater_version = sql_copy_escape (agent->updater_version); + gchar *escaped_agent_version = sql_copy_escape (agent->agent_version); + gchar *escaped_operating_system = sql_copy_escape (agent->operating_system); + gchar *escaped_architecture = sql_copy_escape (agent->architecture); db_copy_buffer_append_printf ( buffer, - "%s\t%s\t%s\t%s\t%d\t%d\t%d\t%s\t%ld\t%s\t%u\t%s\t%ld\t%ld\t%llu\n", - agent->uuid, - agent->name, - agent->agent_id, - escaped_hostname, - agent->authorized, - agent->min_interval, - agent->heartbeat_interval, - escaped_connection_status, - agent->last_update_agent_control, - escaped_schedule, - agent->owner, - escaped_comment, - time (NULL), // creation time - agent->modification_time, - agent->scanner - ); + "%s\t%s\t%s\t%llu\t%s\t%d\t%s\t%ld\t%ld\t%s\t%u\t%s\t%ld\t%ld\t%s\t%s\t%" + "s\t%s\t%d\n", + agent->uuid, agent->name, agent->agent_id, agent->scanner, escaped_hostname, + agent->authorized, escaped_connection_status, + agent->last_update_agent_control, agent->last_updater_heartbeat, + escaped_config, agent->owner, escaped_comment, + time (NULL), // creation_time + agent->modification_time, escaped_updater_version, escaped_agent_version, + escaped_operating_system, escaped_architecture, + agent->update_to_latest ? 1 : 0); g_free (escaped_hostname); g_free (escaped_connection_status); - g_free (escaped_schedule); + g_free (escaped_config); g_free (escaped_comment); + g_free (escaped_updater_version); + g_free (escaped_agent_version); + g_free (escaped_operating_system); + g_free (escaped_architecture); + g_free (config_string); } /** @@ -161,24 +190,21 @@ append_agent_row_to_buffer (db_copy_buffer_t *buffer, agent_data_t agent) * @param[in] ip_list List of IP addresses to append. */ static void -append_ip_rows_to_buffer (db_copy_buffer_t *buffer, - const gchar *agent_id, +append_ip_rows_to_buffer (db_copy_buffer_t *buffer, const gchar *agent_id, agent_ip_data_list_t ip_list) { - if (!ip_list) return; + if (!ip_list) + return; - gchar *escaped_agent_id = sql_copy_escape (agent_id); + gchar *escaped_agent_id = sql_copy_escape (agent_id); for (int j = 0; j < ip_list->count; ++j) { agent_ip_data_t ip = ip_list->items[j]; - gchar *escaped_ip_address = sql_copy_escape (ip->ip_address); + gchar *escaped_ip_address = sql_copy_escape (ip->ip_address); - db_copy_buffer_append_printf ( - buffer, - "%s\t%s\n", - escaped_agent_id, - escaped_ip_address); + db_copy_buffer_append_printf (buffer, "%s\t%s\n", escaped_agent_id, + escaped_ip_address); g_free (escaped_ip_address); } @@ -202,7 +228,6 @@ append_ip_rows_to_buffer (db_copy_buffer_t *buffer, int get_scanner_from_agent_uuid (const gchar *agent_uuid, scanner_t *scanner) { - if (!agent_uuid) { g_warning ("%s: Agent UUID is required but missing", __func__); @@ -213,7 +238,8 @@ get_scanner_from_agent_uuid (const gchar *agent_uuid, scanner_t *scanner) int exists = agent_column_exists ("uuid", agent_uuid); if (exists == -1) { - g_warning ("%s: Failed to check if agent UUID '%s' exists (DB error)", __func__, agent_uuid); + g_warning ("%s: Failed to check if agent UUID '%s' exists (DB error)", + __func__, agent_uuid); manage_option_cleanup (); return -2; } @@ -225,12 +251,14 @@ get_scanner_from_agent_uuid (const gchar *agent_uuid, scanner_t *scanner) } gchar *insert_agent_uuid = sql_insert (agent_uuid); - *scanner = sql_int ("SELECT scanner FROM agents WHERE uuid = %s;", insert_agent_uuid); + *scanner = + sql_int ("SELECT scanner FROM agents WHERE uuid = %s;", insert_agent_uuid); g_free (insert_agent_uuid); if (*scanner <= 0) { - g_warning ("%s: Failed to find scanner for agent UUID %s", __func__, agent_uuid); + g_warning ("%s: Failed to find scanner for agent UUID %s", __func__, + agent_uuid); manage_option_cleanup (); return -4; } @@ -253,24 +281,36 @@ sync_agents_from_data_list (agent_data_list_t agent_list) if (!agent_list || agent_list->count == 0) return 0; - db_copy_buffer_t agent_buffer = { 0 }; - db_copy_buffer_t ip_buffer = { 0 }; + db_copy_buffer_t agent_buffer = {0}; + db_copy_buffer_t ip_buffer = {0}; int status = 0; - db_copy_buffer_init ( - &agent_buffer, - 64 * 1024, - "COPY agents (uuid, name, agent_id, hostname, authorized, min_interval, heartbeat_interval," - " connection_status, last_update, schedule, owner, comment, creation_time," - " modification_time, scanner) FROM STDIN;" - ); - + db_copy_buffer_init (&agent_buffer, 64 * 1024, + "COPY agents (" + " uuid," + " name," + " agent_id," + " scanner," + " hostname," + " authorized," + " connection_status," + " last_update," + " last_updater_heartbeat," + " config," + " owner," + " comment," + " creation_time," + " modification_time," + " updater_version," + " agent_version," + " operating_system," + " architecture," + " update_to_latest" + ") FROM STDIN;"); db_copy_buffer_init ( - &ip_buffer, - 32 * 1024, - "COPY agent_ip_addresses (agent_id, ip_address) FROM STDIN;" - ); + &ip_buffer, 32 * 1024, + "COPY agent_ip_addresses (agent_id, ip_address) FROM STDIN;"); sql_begin_immediate (); @@ -282,15 +322,16 @@ sync_agents_from_data_list (agent_data_list_t agent_list) if (exists) { - update_existing_agent(agent); - delete_existing_agent_ips(agent->agent_id); + update_existing_agent (agent); + delete_existing_agent_ips (agent->agent_id); } else { append_agent_row_to_buffer (&agent_buffer, agent); } - append_ip_rows_to_buffer (&ip_buffer, agent->agent_id, agent->ip_addresses); + append_ip_rows_to_buffer (&ip_buffer, agent->agent_id, + agent->ip_addresses); } if (db_copy_buffer_commit (&agent_buffer, TRUE)) @@ -323,7 +364,8 @@ sync_agents_from_data_list (agent_data_list_t agent_list) * @brief Initialize SQL-based agent iterator with filtering support. * * @param[out] iterator Pointer to the iterator to initialize. - * @param[in] get Get parameters containing filtering criteria (e.g., agent ID). + * @param[in] get Get parameters containing filtering criteria (e.g., + * agent ID). * * @return 0 on success, -1 on failure. */ @@ -345,16 +387,12 @@ init_agent_iterator (iterator_t *iterator, get_data_t *get) where_clause = g_strdup_printf ("agent_id = '%s'", quoted); } - int ret = init_get_iterator (iterator, - "agent", - get, - columns, - NULL, // no trash columns + int ret = init_get_iterator (iterator, "agent", get, columns, + NULL, // no trash columns filter_columns, - 0, // no trashcan - NULL, // no joins - where_clause, - 0); + 0, // no trashcan + NULL, // no joins + where_clause, 0); g_free (where_clause); g_free (quoted); @@ -363,7 +401,8 @@ init_agent_iterator (iterator_t *iterator, get_data_t *get) } /** - * @brief Initialize an agent iterator for a specific scanner and list of agent UUIDs. + * @brief Initialize an agent iterator for a specific scanner and list of agent + * UUIDs. * * @param[out] iterator Pointer to the iterator to initialize. * @param[in] uuid_list List of agent UUIDs to include in the iteration. @@ -373,7 +412,7 @@ init_agent_uuid_list_iterator (iterator_t *iterator, agent_uuid_list_t uuid_list) { get_data_t get; - memset(&get, 0, sizeof(get)); + memset (&get, 0, sizeof (get)); get.type = "agent"; get.ignore_pagination = 1; get.ignore_max_rows_per_page = 1; @@ -387,8 +426,7 @@ init_agent_uuid_list_iterator (iterator_t *iterator, for (int i = 0; i < uuid_list->count; ++i) { gchar *quoted_uuid = sql_quote (uuid_list->agent_uuids[i]); - g_string_append_printf (where_clause, "'%s'%s", - quoted_uuid, + g_string_append_printf (where_clause, "'%s'%s", quoted_uuid, (i < uuid_list->count - 1) ? ", " : ""); g_free (quoted_uuid); } @@ -396,16 +434,12 @@ init_agent_uuid_list_iterator (iterator_t *iterator, } static column_t columns[] = AGENT_ITERATOR_COLUMNS; static const char *filter_columns[] = AGENT_ITERATOR_FILTER_COLUMNS; - init_get_iterator (iterator, - "agent", - &get, - columns, - NULL, // no trash columns + init_get_iterator (iterator, "agent", &get, columns, + NULL, // no trash columns filter_columns, - 0, // no trashcan - NULL, // no joins - where_clause->str, - 0); + 0, // no trashcan + NULL, // no joins + where_clause->str, 0); g_string_free (where_clause, TRUE); } @@ -425,7 +459,7 @@ load_agent_ip_addresses (const char *agent_id) gchar *inserted_agent_id = sql_insert (agent_id); gchar *count_query = g_strdup_printf ( "SELECT COUNT(*) FROM agent_ip_addresses WHERE agent_id = %s;", - inserted_agent_id); + inserted_agent_id); int count = sql_int (count_query); g_free (count_query); @@ -438,9 +472,10 @@ load_agent_ip_addresses (const char *agent_id) iterator_t ip_iterator; - init_iterator (&ip_iterator, - "SELECT ip_address FROM agent_ip_addresses WHERE agent_id = %s;", - inserted_agent_id); + init_iterator ( + &ip_iterator, + "SELECT ip_address FROM agent_ip_addresses WHERE agent_id = %s;", + inserted_agent_id); int index = 0; while (next (&ip_iterator) && index < count) @@ -486,57 +521,93 @@ agent_iterator_authorized (iterator_t *iterator) } /** - * @brief Retrieve min_interval of current agent. + * @brief Retrieve connection status string of current agent. */ -int -agent_iterator_min_interval (iterator_t *iterator) +const char * +agent_iterator_connection_status (iterator_t *iterator) { - return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3); + return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 3); } /** - * @brief Retrieve heartbeat_interval of current agent. + * @brief Retrieve last update timestamp of current agent. */ -int -agent_iterator_heartbeat_interval (iterator_t *iterator) +time_t +agent_iterator_last_update (iterator_t *iterator) { return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4); } /** - * @brief Retrieve connection status string of current agent. + * @brief Retrieve last update timestamp of current agent. + */ +time_t +agent_iterator_last_updater_heartbeat (iterator_t *iterator) +{ + return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 5); +} + +/** + * @brief Retrieve config string of current agent. */ const char * -agent_iterator_connection_status (iterator_t *iterator) +agent_iterator_config (iterator_t *iterator) { - return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 5); + return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 6); } /** - * @brief Retrieve last update timestamp of current agent. + * @brief Retrieve scanner ID of current agent. */ -time_t -agent_iterator_last_update (iterator_t *iterator) +scanner_t +agent_iterator_scanner (iterator_t *iterator) { - return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6); + return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 7); } /** - * @brief Retrieve schedule string of current agent. + * @brief Retrieve updater version of current agent. */ const char * -agent_iterator_schedule (iterator_t *iterator) +agent_iterator_updater_version (iterator_t *iterator) { - return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 7); + return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 8); } /** - * @brief Retrieve scanner ID of current agent. + * @brief Retrieve agent version of current agent. */ -scanner_t -agent_iterator_scanner (iterator_t *iterator) +const char * +agent_iterator_agent_version (iterator_t *iterator) +{ + return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 9); +} + +/** + * @brief Retrieve operating system of current agent. + */ +const char * +agent_iterator_operating_system (iterator_t *iterator) +{ + return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 10); +} + +/** + * @brief Retrieve architecture system of current agent. + */ +const char * +agent_iterator_architecture (iterator_t *iterator) +{ + return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 11); +} + +/** + * @brief Retrieve latest update status of current agent. + */ +int +agent_iterator_update_to_latest (iterator_t *iterator) { - return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 8); + return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 12); } /** @@ -551,8 +622,7 @@ agent_count (const get_data_t *get) static const char *extra_columns[] = AGENT_ITERATOR_FILTER_COLUMNS; static column_t columns[] = AGENT_ITERATOR_COLUMNS; - return count ("agent", get, columns, NULL, extra_columns, - 0, 0, 0, TRUE); + return count ("agent", get, columns, NULL, extra_columns, 0, 0, 0, TRUE); } /** @@ -564,7 +634,7 @@ agent_count (const get_data_t *get) int agent_writable (agent_t agent) { - (void)agent; + (void) agent; return 1; } @@ -581,7 +651,8 @@ agent_in_use (agent_t agent) "WITH usage_counts AS (" " SELECT COUNT(*) AS count FROM agent_group_agents WHERE agent_id = %llu" " UNION ALL " - " SELECT COUNT(*) AS count FROM agent_group_agents_trash WHERE agent = %llu" + " SELECT COUNT(*) AS count FROM agent_group_agents_trash WHERE agent = " + "%llu" ") " "SELECT SUM(count) FROM usage_counts;", agent, agent); @@ -591,15 +662,16 @@ agent_in_use (agent_t agent) * @brief Delete agents and associated IPs using a filtered UUID list. * * Deletes agents from the database and their associated IPs. - * If @p agent_uuids is provided and non-empty, only those agents will be deleted. - * If @p agent_uuids is NULL or empty, and @p scanner is non-zero, + * If @p agent_uuids is provided and non-empty, only those agents will be + * deleted. If @p agent_uuids is NULL or empty, and @p scanner is non-zero, * deletes all agents associated with that scanner. * * @param[in] scanner Optional scanner filter (0 to ignore). * @param[in] agent_uuids List of agent UUIDs to delete. */ void -delete_agents_by_scanner_and_uuids (scanner_t scanner, agent_uuid_list_t agent_uuids) +delete_agents_by_scanner_and_uuids (scanner_t scanner, + agent_uuid_list_t agent_uuids) { GString *where_clause = g_string_new ("WHERE 1=1"); @@ -611,7 +683,8 @@ delete_agents_by_scanner_and_uuids (scanner_t scanner, agent_uuid_list_t agent_u { if (i > 0) g_string_append (where_clause, ", "); - g_string_append_printf (where_clause, "'%s'", agent_uuids->agent_uuids[i]); + g_string_append_printf (where_clause, "'%s'", + agent_uuids->agent_uuids[i]); } g_string_append (where_clause, ")"); @@ -625,15 +698,12 @@ delete_agents_by_scanner_and_uuids (scanner_t scanner, agent_uuid_list_t agent_u sql_begin_immediate (); // Delete associated IPs - sql ( - "DELETE FROM agent_ip_addresses " - "WHERE agent_id IN (SELECT agent_id FROM agents %s);", - where_clause->str); + sql ("DELETE FROM agent_ip_addresses " + "WHERE agent_id IN (SELECT agent_id FROM agents %s);", + where_clause->str); // Delete agents - sql ( - "DELETE FROM agents %s;", - where_clause->str); + sql ("DELETE FROM agents %s;", where_clause->str); sql_commit (); @@ -663,10 +733,8 @@ update_agents_comment (agent_uuid_list_t agent_uuids, const gchar *new_comment) sql_begin_immediate (); - sql ( - "UPDATE agents SET comment = '%s' WHERE uuid IN (%s);", - new_comment, - uuid_list->str); + sql ("UPDATE agents SET comment = '%s' WHERE uuid IN (%s);", new_comment, + uuid_list->str); sql_commit (); @@ -685,33 +753,31 @@ update_agents_comment (agent_uuid_list_t agent_uuids, const gchar *new_comment) * 2 if scanner mismatch. */ int -agent_id_by_uuid_and_scanner (const gchar *agent_uuid, - scanner_t scanner_id, +agent_id_by_uuid_and_scanner (const gchar *agent_uuid, scanner_t scanner_id, agent_t *agent_id_out) { g_return_val_if_fail (agent_uuid != NULL, 1); g_return_val_if_fail (agent_id_out != NULL, 1); // Get the agent ID with matching scanner - agent_t agent_id = sql_int64_0 ( - "SELECT id FROM agents WHERE uuid = '%s' AND scanner = %llu;", - agent_uuid, scanner_id); + agent_t agent_id = + sql_int64_0 ("SELECT id FROM agents WHERE uuid = '%s' AND scanner = %llu;", + agent_uuid, scanner_id); if (agent_id != 0) - { - *agent_id_out = agent_id; - return 0; // success - } + { + *agent_id_out = agent_id; + return 0; // success + } // Check if agent exists but scanner doesn't match - agent_id = sql_int64_0 ( - "SELECT id FROM agents WHERE uuid = '%s';", - agent_uuid); + agent_id = + sql_int64_0 ("SELECT id FROM agents WHERE uuid = '%s';", agent_uuid); if (agent_id != 0) - return 2; // scanner mismatch + return 2; // scanner mismatch - return 1; // agent not found + return 1; // agent not found } /** @@ -730,24 +796,23 @@ agents_in_use (agent_uuid_list_t agent_uuids) GString *uuid_filter = g_string_new (""); for (int i = 0; i < agent_uuids->count; ++i) - { - if (i > 0) - g_string_append (uuid_filter, ", "); - g_string_append_printf (uuid_filter, "'%s'", agent_uuids->agent_uuids[i]); - } - - int count = sql_int ( - "WITH matching_agents AS (" - " SELECT id FROM agents WHERE uuid IN (%s)" - ") " - "SELECT COUNT(*) FROM (" - " SELECT agent_id AS id FROM agent_group_agents" - " WHERE agent_id IN (SELECT id FROM matching_agents)" - " UNION ALL " - " SELECT agent AS id FROM agent_group_agents_trash" - " WHERE agent IN (SELECT id FROM matching_agents)" - ") AS used_agents;", - uuid_filter->str); + { + if (i > 0) + g_string_append (uuid_filter, ", "); + g_string_append_printf (uuid_filter, "'%s'", agent_uuids->agent_uuids[i]); + } + + int count = sql_int ("WITH matching_agents AS (" + " SELECT id FROM agents WHERE uuid IN (%s)" + ") " + "SELECT COUNT(*) FROM (" + " SELECT agent_id AS id FROM agent_group_agents" + " WHERE agent_id IN (SELECT id FROM matching_agents)" + " UNION ALL " + " SELECT agent AS id FROM agent_group_agents_trash" + " WHERE agent IN (SELECT id FROM matching_agents)" + ") AS used_agents;", + uuid_filter->str); g_string_free (uuid_filter, TRUE); diff --git a/src/manage_sql_agents.h b/src/manage_sql_agents.h index 01a2eab386..2fdb318861 100644 --- a/src/manage_sql_agents.h +++ b/src/manage_sql_agents.h @@ -17,50 +17,43 @@ #ifndef _GVMD_MANAGE_SQL_AGENTS_H #define _GVMD_MANAGE_SQL_AGENTS_H -#include "manage_sql.h" #include "manage_agents.h" +#include "manage_sql.h" /** * @brief Agent iterator columns. */ -#define AGENT_ITERATOR_COLUMNS \ -{ \ - GET_ITERATOR_COLUMNS (agents), \ - { "agent_id", NULL, KEYWORD_TYPE_STRING }, \ - { "hostname", NULL, KEYWORD_TYPE_STRING }, \ - { "authorized", NULL, KEYWORD_TYPE_INTEGER }, \ - { "min_interval", NULL, KEYWORD_TYPE_INTEGER }, \ - { "heartbeat_interval", NULL, KEYWORD_TYPE_INTEGER }, \ - { "connection_status", NULL, KEYWORD_TYPE_STRING }, \ - { "last_update", NULL, KEYWORD_TYPE_INTEGER }, \ - { "schedule", NULL, KEYWORD_TYPE_STRING }, \ - { "scanner", NULL, KEYWORD_TYPE_INTEGER }, \ - { NULL, NULL, KEYWORD_TYPE_UNKNOWN } \ -} +#define AGENT_ITERATOR_COLUMNS \ + { \ + GET_ITERATOR_COLUMNS (agents), {"agent_id", NULL, KEYWORD_TYPE_STRING}, \ + {"hostname", NULL, KEYWORD_TYPE_STRING}, \ + {"authorized", NULL, KEYWORD_TYPE_INTEGER}, \ + {"connection_status", NULL, KEYWORD_TYPE_STRING}, \ + {"last_update", NULL, KEYWORD_TYPE_INTEGER}, \ + {"last_updater_heartbeat", NULL, KEYWORD_TYPE_INTEGER}, \ + {"config", NULL, KEYWORD_TYPE_STRING}, \ + {"scanner", NULL, KEYWORD_TYPE_INTEGER}, \ + {"updater_version", NULL, KEYWORD_TYPE_STRING}, \ + {"agent_version", NULL, KEYWORD_TYPE_STRING}, \ + {"operating_system", NULL, KEYWORD_TYPE_STRING}, \ + {"architecture", NULL, KEYWORD_TYPE_STRING}, \ + {"update_to_latest", NULL, KEYWORD_TYPE_INTEGER}, \ + { \ + NULL, NULL, KEYWORD_TYPE_UNKNOWN \ + } \ + } /** * @brief Filter columns for agent iterator. */ -#define AGENT_ITERATOR_FILTER_COLUMNS \ -{ \ - "uuid", \ - "agent_id", \ - "name", \ - "hostname", \ - "scanner", \ - "authorized", \ - "min_interval", \ - "heartbeat_interval", \ - "connection_status", \ - "last_update", \ - "schedule", \ - "comment", \ - "creation_time", \ - "modification_time", \ - "owner", \ - "id", \ - NULL \ -} +#define AGENT_ITERATOR_FILTER_COLUMNS \ + { \ + "uuid", "agent_id", "name", "hostname", "scanner", "authorized", \ + "min_interval", "last_update", "last_updater_heartbeat", "comment", \ + "creation_time", "modification_time", "owner", "id", "updater_version", \ + "agent_version", "operating_system", "architecture", "update_to_latest", \ + NULL \ + } int sync_agents_from_data_list (agent_data_list_t agent_list); @@ -69,10 +62,11 @@ void update_agents_comment (agent_uuid_list_t agent_uuids, const gchar *new_comment); int -get_scanner_from_agent_uuid(const gchar *agent_uuid, scanner_t *scanner); +get_scanner_from_agent_uuid (const gchar *agent_uuid, scanner_t *scanner); int -agent_id_by_uuid_and_scanner (const gchar *agent_uuid, scanner_t scanner_id, agent_t *agent_id); +agent_id_by_uuid_and_scanner (const gchar *agent_uuid, scanner_t scanner_id, + agent_t *agent_id); #endif //_GVMD_MANAGE_SQL_AGENTS_H -#endif // ENABLE_AGENTS \ No newline at end of file +#endif // ENABLE_AGENTS diff --git a/src/manage_utils.c b/src/manage_utils.c index f01eb6a09d..8189737630 100644 --- a/src/manage_utils.c +++ b/src/manage_utils.c @@ -27,12 +27,11 @@ #include /* for assert */ #include -#include /* for getenv */ -#include /* for sscanf */ -#include /* for strcmp */ - #include #include +#include /* for sscanf */ +#include /* for getenv */ +#include /* for strcmp */ #undef G_LOG_DOMAIN /** @@ -95,7 +94,7 @@ current_offset (const char *zone) return 0; } tzset (); - offset = - (now - mktime (&now_broken)); + offset = -(now - mktime (&now_broken)); /* Revert to stored TZ. */ if (tz) @@ -167,9 +166,7 @@ manage_count_hosts_max (const char *given_hosts, const char *exclude_hosts, gchar *clean_exclude_hosts; clean_exclude_hosts = clean_hosts_string (exclude_hosts); - if (gvm_hosts_exclude_with_max (hosts, - clean_exclude_hosts, - max_hosts) + if (gvm_hosts_exclude_with_max (hosts, clean_exclude_hosts, max_hosts) < 0) { g_free (clean_hosts); @@ -268,7 +265,7 @@ level_max_severity (const char *level) * @return 1 if host has equal in hosts_str, 0 otherwise. */ int -hosts_str_contains (const char* hosts_str, const char* find_host_str, +hosts_str_contains (const char *hosts_str, const char *find_host_str, int max_hosts) { gvm_hosts_t *hosts, *find_hosts; @@ -297,12 +294,12 @@ hosts_str_contains (const char* hosts_str, const char* find_host_str, * @return 1 yes, 0 no. */ int -valid_db_resource_type (const char* type) +valid_db_resource_type (const char *type) { if (type == NULL) return 0; #if ENABLE_AGENTS - if(strcasecmp (type, "agent_group") == 0) + if (strcasecmp (type, "agent_group") == 0) return 1; #endif @@ -312,8 +309,7 @@ valid_db_resource_type (const char* type) return 1; #endif /* ENABLE_AGENTS */ - return (strcasecmp (type, "alert") == 0) - || (strcasecmp (type, "config") == 0) + return (strcasecmp (type, "alert") == 0) || (strcasecmp (type, "config") == 0) || (strcasecmp (type, "cpe") == 0) || (strcasecmp (type, "credential") == 0) || (strcasecmp (type, "cve") == 0) @@ -321,10 +317,8 @@ valid_db_resource_type (const char* type) || (strcasecmp (type, "dfn_cert_adv") == 0) || (strcasecmp (type, "filter") == 0) || (strcasecmp (type, "group") == 0) - || (strcasecmp (type, "host") == 0) - || (strcasecmp (type, "os") == 0) - || (strcasecmp (type, "note") == 0) - || (strcasecmp (type, "nvt") == 0) + || (strcasecmp (type, "host") == 0) || (strcasecmp (type, "os") == 0) + || (strcasecmp (type, "note") == 0) || (strcasecmp (type, "nvt") == 0) #if ENABLE_CONTAINER_SCANNING || (strcasecmp (type, "oci_image_target") == 0) #endif /* ENABLE_CONTAINER_SCANNING */ @@ -354,14 +348,15 @@ void blank_control_chars (char *string) { for (; *string; string++) - if (iscntrl (*string) && *string != '\n') *string = ' '; + if (iscntrl (*string) && *string != '\n') + *string = ' '; } /** * @brief GVM product ID. */ -#define GVM_PRODID "-//Greenbone.net//NONSGML Greenbone Security Manager " \ - GVMD_VERSION "//EN" +#define GVM_PRODID \ + "-//Greenbone.net//NONSGML Greenbone Security Manager " GVMD_VERSION "//EN" /** * @brief Try to get a built-in libical timezone from a tzid or city name. @@ -370,7 +365,7 @@ blank_control_chars (char *string) * * @return The built-in timezone if found, else NULL. */ -icaltimezone* +icaltimezone * icalendar_timezone_from_string (const char *tzid) { if (tzid) @@ -398,9 +393,8 @@ icalendar_timezone_from_string (const char *tzid) * @return The generated iCalendar component. */ icalcomponent * -icalendar_from_old_schedule_data (time_t first_time, - time_t period, time_t period_months, - time_t duration, +icalendar_from_old_schedule_data (time_t first_time, time_t period, + time_t period_months, time_t duration, int byday_mask) { gchar *uid; @@ -413,8 +407,7 @@ icalendar_from_old_schedule_data (time_t first_time, // Setup base calendar component ical_new = icalcomponent_new_vcalendar (); icalcomponent_add_property (ical_new, icalproperty_new_version ("2.0")); - icalcomponent_add_property (ical_new, - icalproperty_new_prodid (GVM_PRODID)); + icalcomponent_add_property (ical_new, icalproperty_new_prodid (GVM_PRODID)); // Create event component vevent = icalcomponent_new_vevent (); @@ -502,13 +495,12 @@ icalendar_from_old_schedule_data (time_t first_time, if (byday_mask & (1 << mask_bit)) { recurrence.by_day[array_pos] = ical_day; - array_pos ++; + array_pos++; } } } - icalcomponent_add_property (vevent, - icalproperty_new_rrule (recurrence)); + icalcomponent_add_property (vevent, icalproperty_new_rrule (recurrence)); } // Add duration @@ -547,8 +539,8 @@ icalendar_simplify_vevent (icalcomponent *vevent, icaltimezone *zone, // Check for errors icalrestriction_check (vevent); - error_prop = icalcomponent_get_first_property (vevent, - ICAL_XLICERROR_PROPERTY); + error_prop = + icalcomponent_get_first_property (vevent, ICAL_XLICERROR_PROPERTY); if (error_prop) { if (error) @@ -594,12 +586,10 @@ icalendar_simplify_vevent (icalcomponent *vevent, icaltimezone *zone, * Technically there can be multiple ones but behavior is undefined in * the iCalendar specification. */ - rrule_prop = icalcomponent_get_first_property (vevent, - ICAL_RRULE_PROPERTY); + rrule_prop = icalcomponent_get_first_property (vevent, ICAL_RRULE_PROPERTY); // Warn about EXRULE being deprecated - exrule_prop = icalcomponent_get_first_property (vevent, - ICAL_EXRULE_PROPERTY); + exrule_prop = icalcomponent_get_first_property (vevent, ICAL_EXRULE_PROPERTY); if (exrule_prop) { g_string_append_printf (warnings_buffer, @@ -620,8 +610,7 @@ icalendar_simplify_vevent (icalcomponent *vevent, icaltimezone *zone, } // Simplify and copy RDATE properties - rdate_prop = icalcomponent_get_first_property (vevent, - ICAL_RDATE_PROPERTY); + rdate_prop = icalcomponent_get_first_property (vevent, ICAL_RDATE_PROPERTY); while (rdate_prop) { struct icaldatetimeperiodtype old_datetimeperiod, new_datetimeperiod; @@ -633,38 +622,36 @@ icalendar_simplify_vevent (icalcomponent *vevent, icaltimezone *zone, new_datetimeperiod.period = icalperiodtype_null_period (); if (icalperiodtype_is_null_period (old_datetimeperiod.period)) { - new_datetimeperiod.time - = icaltime_convert_to_zone (old_datetimeperiod.time, zone); + new_datetimeperiod.time = + icaltime_convert_to_zone (old_datetimeperiod.time, zone); } else { - new_datetimeperiod.time - = icaltime_convert_to_zone (old_datetimeperiod.period.start, zone); + new_datetimeperiod.time = + icaltime_convert_to_zone (old_datetimeperiod.period.start, zone); } new_rdate = icalproperty_new_rdate (new_datetimeperiod); icalcomponent_add_property (vevent_simplified, new_rdate); - rdate_prop - = icalcomponent_get_next_property (vevent, ICAL_RDATE_PROPERTY); + rdate_prop = + icalcomponent_get_next_property (vevent, ICAL_RDATE_PROPERTY); } // Copy EXDATE properties - exdate_prop = icalcomponent_get_first_property (vevent, - ICAL_EXDATE_PROPERTY); + exdate_prop = icalcomponent_get_first_property (vevent, ICAL_EXDATE_PROPERTY); while (exdate_prop) { icaltimetype original_exdate_time, exdate_time; icalproperty *prop_clone; original_exdate_time = icalproperty_get_exdate (exdate_prop); - exdate_time - = icaltime_convert_to_zone (original_exdate_time, zone); + exdate_time = icaltime_convert_to_zone (original_exdate_time, zone); prop_clone = icalproperty_new_exdate (exdate_time); icalcomponent_add_property (vevent_simplified, prop_clone); - exdate_prop - = icalcomponent_get_next_property (vevent, ICAL_EXDATE_PROPERTY); + exdate_prop = + icalcomponent_get_next_property (vevent, ICAL_EXDATE_PROPERTY); } // Generate UID for event @@ -683,16 +670,16 @@ icalendar_simplify_vevent (icalcomponent *vevent, icaltimezone *zone, /** * @brief Error return for icalendar_from_string. */ -#define ICAL_RETURN_ERROR(message) \ - do \ - { \ - if (error) \ - *error = message; \ - icalcomponent_free (ical_parsed); \ - icalcomponent_free (ical_new); \ - g_string_free (warnings_buffer, TRUE); \ - return NULL; \ - } \ +#define ICAL_RETURN_ERROR(message) \ + do \ + { \ + if (error) \ + *error = message; \ + icalcomponent_free (ical_parsed); \ + icalcomponent_free (ical_new); \ + g_string_free (warnings_buffer, TRUE); \ + return NULL; \ + } \ while (0) /** @@ -726,8 +713,8 @@ icalendar_from_string (const char *ical_string, icaltimezone *zone, // Check for errors icalrestriction_check (ical_parsed); - error_prop = icalcomponent_get_first_property (ical_parsed, - ICAL_XLICERROR_PROPERTY); + error_prop = + icalcomponent_get_first_property (ical_parsed, ICAL_XLICERROR_PROPERTY); if (error_prop) { if (error) @@ -742,109 +729,102 @@ icalendar_from_string (const char *ical_string, icaltimezone *zone, ical_new = icalcomponent_new_vcalendar (); icalcomponent_add_property (ical_new, icalproperty_new_version ("2.0")); - icalcomponent_add_property (ical_new, - icalproperty_new_prodid (GVM_PRODID)); + icalcomponent_add_property (ical_new, icalproperty_new_prodid (GVM_PRODID)); - timezone_component - = icalcomponent_new_clone (icaltimezone_get_component (zone)); + timezone_component = + icalcomponent_new_clone (icaltimezone_get_component (zone)); icalcomponent_add_component (ical_new, timezone_component); switch (icalcomponent_isa (ical_parsed)) { - case ICAL_NO_COMPONENT: - // The text must contain valid iCalendar component - ICAL_RETURN_ERROR - (g_strdup_printf ("String contains no iCalendar component")); - break; - case ICAL_XROOT_COMPONENT: - case ICAL_VCALENDAR_COMPONENT: - // Check multiple components - ical_iter = icalcomponent_begin_component (ical_parsed, - ICAL_ANY_COMPONENT); - icalcomponent *subcomp; - while ((subcomp = icalcompiter_deref (&ical_iter))) - { - icalcomponent *new_vevent; - switch (icalcomponent_isa (subcomp)) - { - case ICAL_VEVENT_COMPONENT: - // Copy and simplify only the first VEVENT, ignoring all - // following ones. - if (vevent_count == 0) - { - new_vevent = icalendar_simplify_vevent - (subcomp, - zone, - error, - warnings_buffer); - if (new_vevent == NULL) - ICAL_RETURN_ERROR (*error); - icalcomponent_add_component (ical_new, new_vevent); - } - vevent_count ++; - break; - case ICAL_VTIMEZONE_COMPONENT: - // Timezones are collected separately - break; - case ICAL_VJOURNAL_COMPONENT: - case ICAL_VTODO_COMPONENT: - // VJOURNAL and VTODO components are ignored - other_component_count ++; - break; - default: - // Unexpected components - ICAL_RETURN_ERROR - (g_strdup_printf ("Unexpected component type: %s", - icalcomponent_kind_to_string - (icalcomponent_isa (subcomp)))); - } - icalcompiter_next (&ical_iter); - } - - if (vevent_count == 0) - { - ICAL_RETURN_ERROR - (g_strdup_printf ("iCalendar string must contain a VEVENT")); - } - else if (vevent_count > 1) - { - g_string_append_printf (warnings_buffer, - "" - "iCalendar contains %d VEVENT components" - " but only the first one will be used" - "", - vevent_count); - } - - if (other_component_count) - { - g_string_append_printf (warnings_buffer, - "" - "iCalendar contains %d VTODO and/or" - " VJOURNAL component(s) which will be" - " ignored" - "", - other_component_count); - } - break; - case ICAL_VEVENT_COMPONENT: + case ICAL_NO_COMPONENT: + // The text must contain valid iCalendar component + ICAL_RETURN_ERROR ( + g_strdup_printf ("String contains no iCalendar component")); + break; + case ICAL_XROOT_COMPONENT: + case ICAL_VCALENDAR_COMPONENT: + // Check multiple components + ical_iter = + icalcomponent_begin_component (ical_parsed, ICAL_ANY_COMPONENT); + icalcomponent *subcomp; + while ((subcomp = icalcompiter_deref (&ical_iter))) { icalcomponent *new_vevent; + switch (icalcomponent_isa (subcomp)) + { + case ICAL_VEVENT_COMPONENT: + // Copy and simplify only the first VEVENT, ignoring all + // following ones. + if (vevent_count == 0) + { + new_vevent = icalendar_simplify_vevent (subcomp, zone, error, + warnings_buffer); + if (new_vevent == NULL) + ICAL_RETURN_ERROR (*error); + icalcomponent_add_component (ical_new, new_vevent); + } + vevent_count++; + break; + case ICAL_VTIMEZONE_COMPONENT: + // Timezones are collected separately + break; + case ICAL_VJOURNAL_COMPONENT: + case ICAL_VTODO_COMPONENT: + // VJOURNAL and VTODO components are ignored + other_component_count++; + break; + default: + // Unexpected components + ICAL_RETURN_ERROR (g_strdup_printf ( + "Unexpected component type: %s", + icalcomponent_kind_to_string (icalcomponent_isa (subcomp)))); + } + icalcompiter_next (&ical_iter); + } - new_vevent = icalendar_simplify_vevent (ical_parsed, - zone, - error, - warnings_buffer); - if (new_vevent == NULL) - ICAL_RETURN_ERROR (*error); - icalcomponent_add_component (ical_new, new_vevent); + if (vevent_count == 0) + { + ICAL_RETURN_ERROR ( + g_strdup_printf ("iCalendar string must contain a VEVENT")); + } + else if (vevent_count > 1) + { + g_string_append_printf (warnings_buffer, + "" + "iCalendar contains %d VEVENT components" + " but only the first one will be used" + "", + vevent_count); } - break; - default: - ICAL_RETURN_ERROR - (g_strdup_printf ("iCalendar string must be a VCALENDAR or VEVENT" - " component or consist of multiple elements.")); - break; + + if (other_component_count) + { + g_string_append_printf (warnings_buffer, + "" + "iCalendar contains %d VTODO and/or" + " VJOURNAL component(s) which will be" + " ignored" + "", + other_component_count); + } + break; + case ICAL_VEVENT_COMPONENT: + { + icalcomponent *new_vevent; + + new_vevent = + icalendar_simplify_vevent (ical_parsed, zone, error, warnings_buffer); + if (new_vevent == NULL) + ICAL_RETURN_ERROR (*error); + icalcomponent_add_component (ical_new, new_vevent); + } + break; + default: + ICAL_RETURN_ERROR ( + g_strdup_printf ("iCalendar string must be a VCALENDAR or VEVENT" + " component or consist of multiple elements.")); + break; } icalcomponent_free (ical_parsed); @@ -878,7 +858,6 @@ icalendar_approximate_rrule_from_vcalendar (icalcomponent *vcalendar, icalcomponent *vevent; icalproperty *rrule_prop; - assert (period); assert (period_months); assert (byday_mask); @@ -894,14 +873,12 @@ icalendar_approximate_rrule_from_vcalendar (icalcomponent *vcalendar, // Process only the first VEVENT // Others should be removed by icalendar_from_string - vevent = icalcomponent_get_first_component (vcalendar, - ICAL_VEVENT_COMPONENT); + vevent = icalcomponent_get_first_component (vcalendar, ICAL_VEVENT_COMPONENT); if (vevent == NULL) return -1; // Process only first RRULE. - rrule_prop = icalcomponent_get_first_property (vevent, - ICAL_RRULE_PROPERTY); + rrule_prop = icalcomponent_get_first_property (vevent, ICAL_RRULE_PROPERTY); if (rrule_prop) { struct icalrecurrencetype recurrence; @@ -911,30 +888,30 @@ icalendar_approximate_rrule_from_vcalendar (icalcomponent *vcalendar, // Get period or period_months switch (recurrence.freq) { - case ICAL_YEARLY_RECURRENCE: - *period_months = recurrence.interval * 12; - break; - case ICAL_MONTHLY_RECURRENCE: - *period_months = recurrence.interval; - break; - case ICAL_WEEKLY_RECURRENCE: - *period = recurrence.interval * 604800; - break; - case ICAL_DAILY_RECURRENCE: - *period = recurrence.interval * 86400; - break; - case ICAL_HOURLY_RECURRENCE: - *period = recurrence.interval * 3600; - break; - case ICAL_MINUTELY_RECURRENCE: - *period = recurrence.interval * 60; - break; - case ICAL_SECONDLY_RECURRENCE: - *period = recurrence.interval; - case ICAL_NO_RECURRENCE: - break; - default: - return -1; + case ICAL_YEARLY_RECURRENCE: + *period_months = recurrence.interval * 12; + break; + case ICAL_MONTHLY_RECURRENCE: + *period_months = recurrence.interval; + break; + case ICAL_WEEKLY_RECURRENCE: + *period = recurrence.interval * 604800; + break; + case ICAL_DAILY_RECURRENCE: + *period = recurrence.interval * 86400; + break; + case ICAL_HOURLY_RECURRENCE: + *period = recurrence.interval * 3600; + break; + case ICAL_MINUTELY_RECURRENCE: + *period = recurrence.interval * 60; + break; + case ICAL_SECONDLY_RECURRENCE: + *period = recurrence.interval; + case ICAL_NO_RECURRENCE: + break; + default: + return -1; } /* @@ -945,8 +922,8 @@ icalendar_approximate_rrule_from_vcalendar (icalcomponent *vcalendar, array_pos = 0; while (recurrence.by_day[array_pos] != ICAL_RECURRENCE_ARRAY_MAX) { - int ical_day = icalrecurrencetype_day_day_of_week - (recurrence.by_day[array_pos]); + int ical_day = + icalrecurrencetype_day_day_of_week (recurrence.by_day[array_pos]); int mask_bit = -1; if (ical_day == 1) @@ -958,7 +935,7 @@ icalendar_approximate_rrule_from_vcalendar (icalcomponent *vcalendar, { *byday_mask |= (1 << mask_bit); } - array_pos ++; + array_pos++; } } @@ -975,10 +952,10 @@ icalendar_approximate_rrule_from_vcalendar (icalcomponent *vcalendar, * * @return GPtrArray with pointers to collected times or NULL on error. */ -static GPtrArray* +static GPtrArray * icalendar_times_from_vevent (icalcomponent *vevent, icalproperty_kind type) { - GPtrArray* times; + GPtrArray *times; icalproperty *date_prop; if (icalcomponent_isa (vevent) != ICAL_VEVENT_COMPONENT @@ -1029,9 +1006,7 @@ icalendar_time_matches_array (icaltimetype time, GPtrArray *times_array) if (times_array == NULL) return FALSE; - for (index = 0; - found == FALSE && index < times_array->len; - index++) + for (index = 0; found == FALSE && index < times_array->len; index++) { int compare_result; icaltimetype *array_time = g_ptr_array_index (times_array, index); @@ -1058,10 +1033,8 @@ icalendar_time_matches_array (icaltimetype time, GPtrArray *times_array) * @return The next or previous time as time_t. */ static time_t -icalendar_next_time_from_rdates (GPtrArray *rdates, - icaltimetype ref_time_ical, - icaltimezone *tz, - int periods_offset) +icalendar_next_time_from_rdates (GPtrArray *rdates, icaltimetype ref_time_ical, + icaltimezone *tz, int periods_offset) { int index; time_t ref_time, closest_time; @@ -1114,10 +1087,8 @@ static time_t icalendar_next_time_from_recurrence (struct icalrecurrencetype recurrence, icaltimetype dtstart, icaltimetype reference_time, - icaltimezone *tz, - GPtrArray *exdates, - GPtrArray *rdates, - int periods_offset) + icaltimezone *tz, GPtrArray *exdates, + GPtrArray *rdates, int periods_offset) { icalrecur_iterator *recur_iter; icaltimetype recur_time, prev_time, next_time; @@ -1251,8 +1222,7 @@ icalendar_next_time_from_vcalendar (icalcomponent *vcalendar, // Process only the first VEVENT // Others should be removed by icalendar_from_string - vevent = icalcomponent_get_first_component (vcalendar, - ICAL_VEVENT_COMPONENT); + vevent = icalcomponent_get_first_component (vcalendar, ICAL_VEVENT_COMPONENT); if (vevent == NULL) return 0; @@ -1261,7 +1231,7 @@ icalendar_next_time_from_vcalendar (icalcomponent *vcalendar, if (icaltime_is_null_time (dtstart)) return 0; - tz = (icaltimezone*) icaltime_get_timezone (dtstart); + tz = (icaltimezone *) icaltime_get_timezone (dtstart); if (tz == NULL) { tz = icalendar_timezone_from_string (default_tzid); @@ -1294,11 +1264,9 @@ icalendar_next_time_from_vcalendar (icalcomponent *vcalendar, icalrecurrencetype_clear (&recurrence); // Calculate next time. - next_time = icalendar_next_time_from_recurrence (recurrence, - dtstart_with_tz, - ical_reference_time, tz, - exdates, rdates, - periods_offset); + next_time = icalendar_next_time_from_recurrence ( + recurrence, dtstart_with_tz, ical_reference_time, tz, exdates, rdates, + periods_offset); // Cleanup g_ptr_array_free (exdates, TRUE); @@ -1321,19 +1289,15 @@ icalendar_next_time_from_vcalendar (icalcomponent *vcalendar, * @return The next or previous time as a time_t. */ time_t -icalendar_next_time_from_string (const char *ical_string, - time_t reference_time, - const char *default_tzid, - int periods_offset) +icalendar_next_time_from_string (const char *ical_string, time_t reference_time, + const char *default_tzid, int periods_offset) { time_t next_time; icalcomponent *ical_parsed; ical_parsed = icalcomponent_new_from_string (ical_string); - next_time = icalendar_next_time_from_vcalendar (ical_parsed, - reference_time, - default_tzid, - periods_offset); + next_time = icalendar_next_time_from_vcalendar (ical_parsed, reference_time, + default_tzid, periods_offset); icalcomponent_free (ical_parsed); return next_time; } @@ -1360,8 +1324,7 @@ icalendar_duration_from_vcalendar (icalcomponent *vcalendar) // Process only the first VEVENT // Others should be removed by icalendar_from_string - vevent = icalcomponent_get_first_component (vcalendar, - ICAL_VEVENT_COMPONENT); + vevent = icalcomponent_get_first_component (vcalendar, ICAL_VEVENT_COMPONENT); if (vevent == NULL) return 0; @@ -1397,8 +1360,7 @@ icalendar_first_time_from_vcalendar (icalcomponent *vcalendar, // Process only the first VEVENT // Others should be removed by icalendar_from_string - vevent = icalcomponent_get_first_component (vcalendar, - ICAL_VEVENT_COMPONENT); + vevent = icalcomponent_get_first_component (vcalendar, ICAL_VEVENT_COMPONENT); if (vevent == NULL) return 0; @@ -1407,7 +1369,7 @@ icalendar_first_time_from_vcalendar (icalcomponent *vcalendar, if (icaltime_is_null_time (dtstart)) return 0; - tz = (icaltimezone*) icaltime_get_timezone (dtstart); + tz = (icaltimezone *) icaltime_get_timezone (dtstart); if (tz == NULL) tz = default_tz; @@ -1443,20 +1405,20 @@ clean_hosts_string (const char *hosts) * - A final group of digits, separated with a slash "-" * (CIDR notation, e.g. "192.168.123.001/027) */ - ipv4_match_regex - = g_regex_new ("^[0-9]+(?:\\.[0-9]+){3}" - "(?:\\/[0-9]+|-[0-9]+(?:(?:\\.[0-9]+){3})?)?$", - 0, 0, NULL); + ipv4_match_regex = + g_regex_new ("^[0-9]+(?:\\.[0-9]+){3}" + "(?:\\/[0-9]+|-[0-9]+(?:(?:\\.[0-9]+){3})?)?$", + 0, 0, NULL); /* * Regular expression matching leading zeroes in groups of digits * separated by dots or other characters. * First line matches zeroes before non-zero numbers, e.g. "000" in "000120" * Second line matches groups of all zeroes except one, e.g. "00" in "000" */ - ipv4_replace_regex - = g_regex_new ("(?<=\\D|^)(0+)(?=(?:(?:[1-9]\\d*)(?:\\D|$)))" - "|(?<=\\D|^)(0+)(?=0(?:\\D|$))", - 0, 0, NULL); + ipv4_replace_regex = + g_regex_new ("(?<=\\D|^)(0+)(?=(?:(?:[1-9]\\d*)(?:\\D|$)))" + "|(?<=\\D|^)(0+)(?=0(?:\\D|$))", + 0, 0, NULL); new_hosts = g_string_new (""); hosts_split = g_strsplit (hosts, ",", -1); @@ -1472,8 +1434,8 @@ clean_hosts_string (const char *hosts) * with empty strings, * e.g. "000.001.002.003-004" becomes "0.1.2.3-4" */ - new_item = g_regex_replace (ipv4_replace_regex, - *item, -1, 0, "", 0, NULL); + new_item = + g_regex_replace (ipv4_replace_regex, *item, -1, 0, "", 0, NULL); g_string_append (new_hosts, new_item); g_free (new_item); } @@ -1491,3 +1453,52 @@ clean_hosts_string (const char *hosts) return g_string_free (new_hosts, FALSE); } + +/** + * @brief Join an array of gchar* with a separator, skipping NULL/empty entries. + * + * @param[in] errors GPtrArray of gchar* (may be NULL). + * @param[in] sep Separator string, default to "; " if NULL. + * @param[in] prefix Prefix string, default to "" if NULL. + * + * @return Newly allocated joined string, or NULL if no non-empty entries. + * Caller must g_free(). + */ +gchar * +concat_error_messages (const GPtrArray *errors, const gchar *sep, + const gchar *prefix) +{ + if (!errors || errors->len == 0) + return NULL; + + if (prefix == NULL) + prefix = ""; + + const gchar *use_sep = sep ? sep : "; "; + + GString *gs = NULL; + + for (guint i = 0; i < errors->len; ++i) + { + const gchar *m = g_ptr_array_index ((GPtrArray *) errors, i); + if (!m || !*m) + continue; + + if (!gs) + { + /* first non-empty: start with prefix */ + gs = g_string_new (prefix); + g_string_append (gs, m); + } + else + { + g_string_append (gs, use_sep); + g_string_append (gs, m); + } + } + + if (!gs) + return NULL; + + return g_string_free (gs, FALSE); +} diff --git a/src/manage_utils.h b/src/manage_utils.h index 2a05c1a4c7..dcf78a12b7 100644 --- a/src/manage_utils.h +++ b/src/manage_utils.h @@ -101,4 +101,8 @@ icalendar_first_time_from_vcalendar (icalcomponent *, icaltimezone *); gchar * clean_hosts_string (const char *); +gchar * +concat_error_messages (const GPtrArray *errors, const gchar *sep, + const gchar *prefix); + #endif /* not _GVMD_MANAGE_UTILS_H */ diff --git a/src/manage_utils_tests.c b/src/manage_utils_tests.c index 0ab7f6a97b..2c00ec195c 100644 --- a/src/manage_utils_tests.c +++ b/src/manage_utils_tests.c @@ -22,8 +22,12 @@ #include Describe (manage_utils); -BeforeEach (manage_utils) {} -AfterEach (manage_utils) {} +BeforeEach (manage_utils) +{ +} +AfterEach (manage_utils) +{ +} /* add_months */ @@ -73,44 +77,54 @@ verify_next (time_t next, time_t first, time_t now, int period) || next == get_next_time (first, now, 2 * 60, 2); } +static GPtrArray * +make_str_array (const char **vals, size_t n) +{ + GPtrArray *a = g_ptr_array_new_with_free_func (g_free); + for (size_t i = 0; i < n; ++i) + { + if (vals[i] == NULL) + g_ptr_array_add (a, NULL); + else + g_ptr_array_add (a, g_strdup (vals[i])); + } + return a; +} + Ensure (manage_utils, icalendar_next_time_from_string_utc) { time_t next, now; /* Start in past. */ now = time (NULL); - next = icalendar_next_time_from_string - ("BEGIN:VCALENDAR\n" - "VERSION:2.0\n" - "BEGIN:VEVENT\n" - "DTSTART:20200101T000000Z\n" - "RRULE:FREQ=MINUTELY;INTERVAL=2\n" - "DURATION:PT0S\n" - "UID:a486116b-8058-4b1e-9fc5-0eeec5948792\n" - "DTSTAMP:19700101T000000Z\n" - "END:VEVENT\n" - "END:VCALENDAR\n", - now, - "UTC", - 0); + next = icalendar_next_time_from_string ( + "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "BEGIN:VEVENT\n" + "DTSTART:20200101T000000Z\n" + "RRULE:FREQ=MINUTELY;INTERVAL=2\n" + "DURATION:PT0S\n" + "UID:a486116b-8058-4b1e-9fc5-0eeec5948792\n" + "DTSTAMP:19700101T000000Z\n" + "END:VEVENT\n" + "END:VCALENDAR\n", + now, "UTC", 0); assert_that (verify_next (next, EPOCH_2020JAN1_UTC, now, 2 * 60), is_equal_to (1)); /* Start in future. */ - next = icalendar_next_time_from_string - ("BEGIN:VCALENDAR\n" - "VERSION:2.0\n" - "BEGIN:VEVENT\n" - "DTSTART:20300101T000000Z\n" - "RRULE:FREQ=MINUTELY;INTERVAL=2\n" - "DURATION:PT0S\n" - "UID:a486116b-8058-4b1e-9fc5-0eeec5948792\n" - "DTSTAMP:19700101T000000Z\n" - "END:VEVENT\n" - "END:VCALENDAR\n", - now, - "UTC", - 0); + next = icalendar_next_time_from_string ( + "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "BEGIN:VEVENT\n" + "DTSTART:20300101T000000Z\n" + "RRULE:FREQ=MINUTELY;INTERVAL=2\n" + "DURATION:PT0S\n" + "UID:a486116b-8058-4b1e-9fc5-0eeec5948792\n" + "DTSTAMP:19700101T000000Z\n" + "END:VEVENT\n" + "END:VCALENDAR\n", + now, "UTC", 0); assert_that (next, is_equal_to (EPOCH_2030JAN1_UTC)); } @@ -120,35 +134,34 @@ Ensure (manage_utils, icalendar_next_time_from_string_tz) now = time (NULL); - next = icalendar_next_time_from_string - ("BEGIN:VCALENDAR\n" - "VERSION:2.0\n" - /* Timezone definition. */ - "BEGIN:VTIMEZONE\n" - "TZID:/freeassociation.sourceforge.net/Africa/Harare\n" - "X-LIC-LOCATION:Africa/Harare\n" - "BEGIN:STANDARD\n" - "TZNAME:CAT\n" - "DTSTART:19030301T000000\n" - "TZOFFSETFROM:+021020\n" - "TZOFFSETTO:+0200\n" - "END:STANDARD\n" - "END:VTIMEZONE\n" - /* Event. */ - "BEGIN:VEVENT\n" - "DTSTART;TZID=/freeassociation.sourceforge.net/Africa/Harare:\n" - " 20200101T000000\n" - "RRULE:FREQ=MINUTELY;INTERVAL=2\n" - "DURATION:PT0S\n" - "UID:a486116b-8058-4b1e-9fc5-0eeec5948792\n" - "DTSTAMP:19700101T000000Z\n" - "END:VEVENT\n" - "END:VCALENDAR\n", - now, - "Africa/Harare", - 0); - - assert_that (verify_next (next, EPOCH_2020JAN1_HAR, now, 2 * 60), is_equal_to (1)); + next = icalendar_next_time_from_string ( + "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + /* Timezone definition. */ + "BEGIN:VTIMEZONE\n" + "TZID:/freeassociation.sourceforge.net/Africa/Harare\n" + "X-LIC-LOCATION:Africa/Harare\n" + "BEGIN:STANDARD\n" + "TZNAME:CAT\n" + "DTSTART:19030301T000000\n" + "TZOFFSETFROM:+021020\n" + "TZOFFSETTO:+0200\n" + "END:STANDARD\n" + "END:VTIMEZONE\n" + /* Event. */ + "BEGIN:VEVENT\n" + "DTSTART;TZID=/freeassociation.sourceforge.net/Africa/Harare:\n" + " 20200101T000000\n" + "RRULE:FREQ=MINUTELY;INTERVAL=2\n" + "DURATION:PT0S\n" + "UID:a486116b-8058-4b1e-9fc5-0eeec5948792\n" + "DTSTAMP:19700101T000000Z\n" + "END:VEVENT\n" + "END:VCALENDAR\n", + now, "Africa/Harare", 0); + + assert_that (verify_next (next, EPOCH_2020JAN1_HAR, now, 2 * 60), + is_equal_to (1)); } /* Hosts test */ @@ -185,12 +198,114 @@ Ensure (manage_utils, clean_hosts_string_zeroes) // List of addresses and ranges clean_str = clean_hosts_string ("000.001.002.003, 040.050.060.070-80," " 123.012.001.001-123.012.001.010"); - assert_that (clean_str, - is_equal_to_string ("0.1.2.3, 40.50.60.70-80," - " 123.12.1.1-123.12.1.10")); + assert_that (clean_str, is_equal_to_string ("0.1.2.3, 40.50.60.70-80," + " 123.12.1.1-123.12.1.10")); g_free (clean_str); } +/* concat_error_messages tests */ + +Ensure (manage_utils, concat_error_messages_null_array_returns_null) +{ + gchar *s = concat_error_messages (NULL, NULL, "Validation failed for : "); + assert_that (s, is_null); +} + +Ensure (manage_utils, concat_error_messages_empty_array_returns_null) +{ + GPtrArray *arr = g_ptr_array_new_with_free_func (g_free); + gchar *s = concat_error_messages (arr, NULL, "Validation failed for : "); + assert_that (s, is_null); + g_ptr_array_free (arr, TRUE); +} + +Ensure (manage_utils, concat_error_messages_single_item_with_prefix) +{ + const char *vals[] = {"attempts must be >= 0"}; + GPtrArray *arr = make_str_array (vals, 1); + + gchar *s = concat_error_messages (arr, NULL, "Validation failed for : "); + assert_that ( + s, is_equal_to_string ("Validation failed for : attempts must be >= 0")); + + g_free (s); + g_ptr_array_free (arr, TRUE); +} + +Ensure (manage_utils, concat_error_messages_multiple_default_sep_and_prefix) +{ + const char *vals[] = {"period_in_seconds required", + "bulk_size must be positive", "cron invalid"}; + GPtrArray *arr = make_str_array (vals, 3); + + gchar *s = concat_error_messages (arr, NULL, "Validation failed for : "); + assert_that (s, is_equal_to_string ( + "Validation failed for : period_in_seconds required; " + "bulk_size must be positive; cron invalid")); + + g_free (s); + g_ptr_array_free (arr, TRUE); +} + +Ensure (manage_utils, concat_error_messages_custom_separator) +{ + const char *vals[] = {"a", "b", "c"}; + GPtrArray *arr = make_str_array (vals, 3); + + gchar *s = concat_error_messages (arr, " | ", "Validation failed for : "); + assert_that (s, is_equal_to_string ("Validation failed for : a | b | c")); + + g_free (s); + g_ptr_array_free (arr, TRUE); +} + +Ensure (manage_utils, concat_error_messages_skips_null_and_empty) +{ + const char *vals[] = {NULL, "", "first", "", "second", NULL}; + GPtrArray *arr = make_str_array (vals, sizeof (vals) / sizeof (vals[0])); + + gchar *s = concat_error_messages (arr, NULL, "Validation failed for : "); + assert_that (s, is_equal_to_string ("Validation failed for : first; second")); + + g_free (s); + g_ptr_array_free (arr, TRUE); +} + +Ensure (manage_utils, concat_error_messages_all_null_or_empty_returns_null) +{ + const char *vals[] = {NULL, "", NULL, ""}; + GPtrArray *arr = make_str_array (vals, sizeof (vals) / sizeof (vals[0])); + + gchar *s = concat_error_messages (arr, NULL, "Validation failed for : "); + assert_that (s, is_null); + + g_ptr_array_free (arr, TRUE); +} + +Ensure (manage_utils, concat_error_messages_null_prefix_means_no_prefix) +{ + const char *vals[] = {"x", "y"}; + GPtrArray *arr = make_str_array (vals, 2); + + gchar *s = concat_error_messages (arr, NULL, NULL); + assert_that (s, is_equal_to_string ("x; y")); + + g_free (s); + g_ptr_array_free (arr, TRUE); +} + +Ensure (manage_utils, concat_error_messages_empty_separator) +{ + const char *vals[] = {"aa", "bb", "cc"}; + GPtrArray *arr = make_str_array (vals, 3); + + gchar *s = concat_error_messages (arr, "", "P: "); + assert_that (s, is_equal_to_string ("P: aabbcc")); + + g_free (s); + g_ptr_array_free (arr, TRUE); +} + /* Test suite. */ int @@ -204,11 +319,32 @@ main (int argc, char **argv) add_test_with_context (suite, manage_utils, add_months_negative_months); add_test_with_context (suite, manage_utils, add_months_positive_months); - add_test_with_context (suite, manage_utils, icalendar_next_time_from_string_utc); - add_test_with_context (suite, manage_utils, icalendar_next_time_from_string_tz); + add_test_with_context (suite, manage_utils, + icalendar_next_time_from_string_utc); + add_test_with_context (suite, manage_utils, + icalendar_next_time_from_string_tz); add_test_with_context (suite, manage_utils, clean_hosts_string_zeroes); + add_test_with_context (suite, manage_utils, + concat_error_messages_null_array_returns_null); + add_test_with_context (suite, manage_utils, + concat_error_messages_empty_array_returns_null); + add_test_with_context (suite, manage_utils, + concat_error_messages_single_item_with_prefix); + add_test_with_context (suite, manage_utils, + concat_error_messages_multiple_default_sep_and_prefix); + add_test_with_context (suite, manage_utils, + concat_error_messages_custom_separator); + add_test_with_context (suite, manage_utils, + concat_error_messages_skips_null_and_empty); + add_test_with_context (suite, manage_utils, + concat_error_messages_all_null_or_empty_returns_null); + add_test_with_context (suite, manage_utils, + concat_error_messages_null_prefix_means_no_prefix); + add_test_with_context (suite, manage_utils, + concat_error_messages_empty_separator); + if (argc > 1) return run_single_test (suite, argv[1], create_text_reporter ()); diff --git a/src/schema_formats/XML/GMP.xml.in b/src/schema_formats/XML/GMP.xml.in index 28061cd503..5f92fa4eeb 100644 --- a/src/schema_formats/XML/GMP.xml.in +++ b/src/schema_formats/XML/GMP.xml.in @@ -9319,7 +9319,7 @@ END:VCALENDAR

The response includes detailed information about each agent, including metadata, - status, schedule, scanner association, and IP addresses. + status, config, scanner association, and IP addresses.

@@ -9383,16 +9383,6 @@ END:VCALENDAR integer Whether the agent is authorized (1) or not (0) - - min_interval - integer - Minimum interval between scans (in seconds) - - - heartbeat_interval - integer - Interval for agent heartbeat (in seconds) - connection_status text @@ -9404,9 +9394,34 @@ END:VCALENDAR Last time the agent was updated - schedule + last_updater_heartbeat + iso_time + Last heartbeat timestamp received from updater + + + updater_version + text + Updater version string + + + agent_version + text + Agent version string + + + operating_system + text + Operating system + + + architecture text - Scheduled update interval + CPU architecture (e.g., amd64) + + + update_to_latest + integer + Update-to-latest flag (1/0) comment @@ -9478,13 +9493,18 @@ END:VCALENDAR hostname agent_id authorized - min_interval - heartbeat_interval connection_status last_update + last_updater_heartbeat schedule scanner ip + updater_version + agent_version + operating_system + architecture + update_to_latest + config owner @@ -9555,16 +9575,6 @@ END:VCALENDAR Authorization flag integer - - min_interval - Minimum interval in seconds - integer - - - heartbeat_interval - Heartbeat interval in seconds - integer - connection_status Connection status of the agent @@ -9575,6 +9585,11 @@ END:VCALENDAR Timestamp of last update iso_time + + last_updater_heartbeat + Timestamp of last heartbeat received from updater + iso_time + schedule Agent schedule @@ -9601,6 +9616,116 @@ END:VCALENDAR IP addresses of the agent text + + updater_version + Updater version string (may be empty) + text + + + agent_version + Agent version string (may be empty) + text + + + operating_system + Operating system (may be empty) + text + + + architecture + CPU architecture (e.g., amd64) + text + + + update_to_latest + Flag to update to latest version (1/0) + integer + + + config + Structured agent-controller scan configuration + + agent_control + agent_script_executor + heartbeat + + + agent_control + retry + + retry + + attempts + delay_in_seconds + max_jitter_in_seconds + + + attempts + integer + + + delay_in_seconds + integer + + + max_jitter_in_seconds + integer + + + + + agent_script_executor + + bulk_size + bulk_throttle_time_in_ms + indexer_dir_depth + period_in_seconds + scheduler_cron_time + + + bulk_size + integer + + + bulk_throttle_time_in_ms + integer + + + indexer_dir_depth + integer + + + period_in_seconds + integer + + + scheduler_cron_time + cron + + cron + A cron expression defining execution schedule + text + + + + + heartbeat + + interval_in_seconds + miss_until_inactive + + + interval_in_seconds + Expected heartbeat interval from the updater/agent + integer + + + miss_until_inactive + Consecutive missed heartbeats before marking agent inactive + integer + + + filters @@ -9717,10 +9842,9 @@ END:VCALENDAR GAT-29 GAT-29-p0MPX0FT 1 - 2000 - 0 inactive 2025-05-23T08:25:17Z + 2025-05-23T08:24:59Z @every 8h Agent Controller @@ -9730,6 +9854,34 @@ END:VCALENDAR 192.168.79.157 fe80::fe3e:acb9:1b5b:27da 192.168.1.11 + 1.2.3 + 0.9.0 + Linux + amd64 + 0 + + + + 3 + 10 + 5 + + + + 50 + 250 + 3 + 3600 + + @every 8h + 0 0 * * * + + + + 600 + 1 + + first=1 rows=10 sort=name @@ -22663,6 +22815,9 @@ END:VCALENDAR configs tasks info + @IF_ENABLE_AGENTS@ + agent_control_config + @ENDIF_ENABLE_AGENTS@ owner @@ -22986,6 +23141,93 @@ END:VCALENDAR + @IF_ENABLE_AGENTS@ + + agent_control_config + Structured agent-controller scan configuration + + agent_control + agent_script_executor + heartbeat + + + agent_control + retry + + retry + + attempts + delay_in_seconds + max_jitter_in_seconds + + + attempts + integer + + + delay_in_seconds + integer + + + max_jitter_in_seconds + integer + + + + + agent_script_executor + + bulk_size + bulk_throttle_time_in_ms + indexer_dir_depth + period_in_seconds + scheduler_cron_time + + + bulk_size + integer + + + bulk_throttle_time_in_ms + integer + + + indexer_dir_depth + integer + + + period_in_seconds + integer + + + scheduler_cron_time + cron + + cron + A cron expression defining execution schedule + text + + + + + heartbeat + + interval_in_seconds + miss_until_inactive + + + interval_in_seconds + Expected heartbeat interval from the updater/agent + integer + + + miss_until_inactive + Consecutive missed heartbeats before marking agent inactive + integer + + + + @ENDIF_ENABLE_AGENTS@ filters @@ -23134,6 +23376,60 @@ END:VCALENDAR + @IF_ENABLE_AGENTS@ + + Get full details of a single Agent Control scanner + + + + + + + Default Scanner + + 2014-01-01T13:57:25+01:00 + 2014-01-18T12:07:36+01:00 + 0 + 1 + localhost + 9391 + 2 + ... + ... + + + Debian desktops + + ... + + + ... + + + + 5 + 60 + 10 + + + + 2 + 100 + 10 + 60 + + 0 23 * * * + + + + 600 + 1 + + + + + + @ENDIF_ENABLE_AGENTS@ get_schedules @@ -29110,6 +29406,136 @@ END:VCALENDAR @IF_ENABLE_AGENTS@ + + modify_agent_control_scan_config + Modify agents + +

+ The client uses the modify_agent_control_scan_config command to update the + existing agent control scan agent config. +

+
+ + + agent_control_id + uuid + 1 + + config + + + config + Structured agent-controller configuration + + agent_control + agent_script_executor + heartbeat + + + agent_control + retry + + retry + + attempts + delay_in_seconds + max_jitter_in_seconds + + attemptsinteger + delay_in_secondsinteger + max_jitter_in_secondsinteger + + + + agent_script_executor + + bulk_size + bulk_throttle_time_in_ms + indexer_dir_depth + period_in_seconds + scheduler_cron_time + + bulk_sizeinteger + bulk_throttle_time_in_msinteger + indexer_dir_depthinteger + period_in_secondsinteger + + scheduler_cron_time + item + + item + Cron expression + text + + + + + heartbeat + Heartbeat cadence and inactivity threshold + + interval_in_seconds + miss_until_inactive + + + interval_in_seconds + Expected updater/agent heartbeat interval + integer + + + miss_until_inactive + Consecutive missed heartbeats before marking agent inactive + integer + + + + + + + status + status + 1 + + + status_text + text + 1 + + + + + Modify agents + + + + + + 6 + 60 + 10 + + + + 2 + 300 + 100 + 1000 + + 0 */12 * * * + + + + 300 + 1 + + + example update + + + + + + +
modify_agent_group Modify an agent group @@ -29205,9 +29631,7 @@ END:VCALENDAR agents authorized - min_interval - heartbeat_interval - schedule + config comment @@ -29234,22 +29658,69 @@ END:VCALENDAR 0 - min_interval - Minimum interval between two scans - integer - 0 - - - heartbeat_interval - Expected heartbeat interval - integer - 0 - - - schedule - Execution schedule string (e.g., "@every 12h") - text - 0 + config + Structured agent-controller configuration + + agent_control + agent_script_executor + heartbeat + + + agent_control + retry + + retry + + attempts + delay_in_seconds + max_jitter_in_seconds + + attemptsinteger + delay_in_secondsinteger + max_jitter_in_secondsinteger + + + + agent_script_executor + + bulk_size + bulk_throttle_time_in_ms + indexer_dir_depth + period_in_seconds + scheduler_cron_time + + bulk_sizeinteger + bulk_throttle_time_in_msinteger + indexer_dir_depthinteger + period_in_secondsinteger + + scheduler_cron_time + item + + item + Cron expression + text + + + + + heartbeat + Heartbeat cadence and inactivity threshold + + interval_in_seconds + miss_until_inactive + + + interval_in_seconds + Expected updater/agent heartbeat interval + integer + + + miss_until_inactive + Consecutive missed heartbeats before marking agent inactive + integer + + comment @@ -29279,9 +29750,28 @@ END:VCALENDAR
1 - 1000 - 0 - @every 12h + + + + 6 + 60 + 10 + + + + 2 + 300 + 100 + 1000 + + 0 */12 * * * + + + + 300 + 1 + + example update