diff --git a/src/manage.c b/src/manage.c index c924ca36e..fca82209f 100644 --- a/src/manage.c +++ b/src/manage.c @@ -60,6 +60,7 @@ #include "manage_sql_nvts.h" #include "manage_sql_tickets.h" #include "manage_sql_tls_certificates.h" +#include "sql.h" #include "utils.h" #include @@ -86,9 +87,11 @@ #include #include #include +#include #include #include #include +#include #include #undef G_LOG_DOMAIN @@ -3106,82 +3109,206 @@ set_scanner_connection_retry (int new_retry) /* CVE tasks. */ -/** - * @brief Perform a CVE "scan" on a host. - * - * @param[in] task Task. - * @param[in] report The report to add the host, results and details to. - * @param[in] gvm_host Host. - * - * @return 0 success, 1 failed to get nthlast report for a host. - */ static int -cve_scan_host (task_t task, report_t report, gvm_host_t *gvm_host) +check_version (const gchar *target, const gchar *start_incl, const gchar *start_excl, const gchar *end_incl, const gchar *end_excl) { - report_host_t report_host; - gchar *ip, *host; - - assert (task); - assert (report); - - host = gvm_host_value_str (gvm_host); + int result; - ip = report_host_ip (host); - if (ip == NULL) - ip = g_strdup (host); - - g_debug ("%s: ip: %s", __func__, ip); + if (start_incl != NULL) + { + result = cmp_versions (start_incl, target); + if (result == -5) + return -1; + if (result > 0) + { + return 0; + } + } + if (start_excl != NULL) + { + result = cmp_versions (start_excl, target); + if (result == -5) + return -1; + if (result >= 0) + { + return 0; + } + } - /* Get the last report host that applies to the host IP address. */ + if (end_incl != NULL) + { + result = cmp_versions (end_incl, target); + if (result == -5) + return -1; + if (result < 0) + { + return 0; + } + } - if (host_nthlast_report_host (ip, &report_host, 1)) + if (end_excl != NULL) { - g_warning ("%s: Failed to get nthlast report", __func__); - g_free (ip); - return 1; + result = cmp_versions (end_excl, target); + if (result == -5) + return -1; + if (result <= 0) + { + return 0; + } } - g_debug ("%s: report_host: %llu", __func__, report_host); + return (1); +} - if (report_host) - { - iterator_t report_hosts; +static void +check_cpe_match_rule (long long int node, gboolean *match, gboolean *vulnerable, report_host_t report_host, const char *host_cpe) +{ + iterator_t cpe_match_node_childs; + gchar *operator; + iterator_t cpe_match_ranges; - /* Get the report_host for the host. */ + operator = sql_string ("SELECT operator FROM scap.cpe_match_nodes WHERE id = %llu", node); + init_cpe_match_node_childs_iterator (&cpe_match_node_childs, node); + while (next (&cpe_match_node_childs)) + { + long long int child_node; + child_node = cpe_match_node_childs_iterator_id (&cpe_match_node_childs); + check_cpe_match_rule (child_node, match, vulnerable, report_host, host_cpe); + if (strcmp (operator, "AND") == 0 && !(*match)) + return; + if (strcmp (operator, "OR") == 0 && (*match) && (*vulnerable)) + return; + } - init_report_host_iterator (&report_hosts, 0, NULL, report_host); - if (next (&report_hosts)) + init_cpe_match_range_iterator (&cpe_match_ranges, node); + while (next (&cpe_match_ranges)) + { + iterator_t cpe_host_details_products; + gchar *range_fs_cpe; + gchar *range_uri_product; + gchar *vsi, *vse, *vei, *vee; + range_fs_cpe = vsi = vse = vei = vee = NULL; + range_fs_cpe = g_strdup (cpe_match_range_iterator_cpe (&cpe_match_ranges)); + vsi = g_strdup (cpe_match_range_iterator_version_start_incl (&cpe_match_ranges)); + vse = g_strdup (cpe_match_range_iterator_version_start_excl (&cpe_match_ranges)); + vei = g_strdup (cpe_match_range_iterator_version_end_incl (&cpe_match_ranges)); + vee = g_strdup (cpe_match_range_iterator_version_end_excl (&cpe_match_ranges)); + range_uri_product = fs_cpe_to_uri_product (range_fs_cpe); + init_host_details_cpe_product_iterator (&cpe_host_details_products, range_uri_product, report_host); + while (next (&cpe_host_details_products)) { - iterator_t prognosis; - int prognosis_report_host, start_time; - GArray *results; - - /* Add report_host with prognosis results and host details. */ + cpe_struct_t source, target; + const char *host_details_cpe; + gboolean matches; + host_details_cpe = host_details_cpe_product_iterator_value (&cpe_host_details_products); + cpe_struct_init (&source); + cpe_struct_init (&target); + fs_cpe_to_cpe_struct (range_fs_cpe, &source); + uri_cpe_to_cpe_struct (host_details_cpe, &target); + matches = cpe_struct_match (&source, &target); + if (matches) + { + int result; + result = check_version (target.version, vsi, vse, vei, vee); + if (result == 1) + *match = TRUE; + } + cpe_struct_free (&source); + cpe_struct_free (&target); + } + if (*match && cpe_match_range_iterator_vulnerable (&cpe_match_ranges) == 1) + { + cpe_struct_t source, target; + cpe_struct_init (&source); + cpe_struct_init (&target); + fs_cpe_to_cpe_struct (range_fs_cpe, &source); + uri_cpe_to_cpe_struct (host_cpe, &target); + if (cpe_struct_match (&source, &target)) + *vulnerable = TRUE; + cpe_struct_free (&source); + cpe_struct_free (&target); + } + g_free (range_uri_product); + g_free (range_fs_cpe); + g_free (vsi); + g_free (vse); + g_free (vei); + g_free (vee); + if (strcmp (operator, "AND") == 0 && !(*match)) + return; + if (strcmp (operator, "OR") == 0 && (*match) && (*vulnerable)) + return; + } +} - results = g_array_new (TRUE, TRUE, sizeof (result_t)); - start_time = time (NULL); - prognosis_report_host = 0; - init_host_prognosis_iterator (&prognosis, report_host); - while (next (&prognosis)) +/** + * @brief Perform the json CVE "scan" for the found report host. + * + * @param[in] task Task. + * @param[in] report The report to add the host, results and details to. + * @param[in] report_host The report host. + * @param[in] ip The ip of the report host. + * @param[in] start_time The start time of the scan. + * + * @param[out] prognosis_report_host The report_host with prognosis results + * and host details. + * @param[out] results The results of the scan. + */ +static void +cve_scan_report_host_json (task_t task, + report_t report, + report_host_t report_host, + gchar *ip, + int start_time, + int *prognosis_report_host, + GArray *results) +{ + iterator_t host_details_cpe; + init_host_details_cpe_iterator (&host_details_cpe, report_host); + while (next (&host_details_cpe)) + { + iterator_t cpe_match_root_node; + iterator_t locations_iter; + result_t result; + char *cpe_product; + const char *host_cpe; + double severity; + + host_cpe = host_details_cpe_iterator_cpe (&host_details_cpe); + cpe_product = uri_cpe_to_fs_product (host_cpe); + init_cpe_match_nodes_iterator (&cpe_match_root_node, cpe_product); + while (next (&cpe_match_root_node)) + { + result_t root_node; + gboolean match, vulnerable; + const char *app, *cve; + + vulnerable = FALSE; + match = FALSE; + root_node = cpe_match_nodes_iterator_root_id (&cpe_match_root_node); + check_cpe_match_rule (root_node, &match, &vulnerable, report_host, host_cpe); + if (match && vulnerable) { - const char *app, *cve; - double severity; - gchar *desc; - iterator_t locations_iter; GString *locations; - result_t result; + gchar *desc; - if (prognosis_report_host == 0) - prognosis_report_host = manage_report_host_add (report, - ip, - start_time, - 0); + if (*prognosis_report_host == 0) + *prognosis_report_host = manage_report_host_add (report, + ip, + start_time, + 0); - severity = prognosis_iterator_cvss_double (&prognosis); + severity = sql_double ("SELECT severity FROM scap.cves, scap.cpe_match_nodes" + " WHERE scap.cves.id = scap.cpe_match_nodes.cve_id" + " AND scap.cpe_match_nodes.id = %llu;", + root_node); - app = prognosis_iterator_cpe (&prognosis); - cve = prognosis_iterator_cve (&prognosis); - locations = g_string_new(""); + app = host_cpe; + cve = sql_string ("SELECT name FROM scap.cves, scap.cpe_match_nodes" + " WHERE scap.cves.id = cpe_match_nodes.cve_id" + " AND scap.cpe_match_nodes.id = %llu;", + root_node); + locations = g_string_new (""); insert_report_host_detail (global_current_report, ip, "cve", cve, "CVE Scanner", "App", app, NULL); @@ -3201,7 +3328,9 @@ cve_scan_host (task_t task, report_t report, gvm_host_t *gvm_host) } if (locations->len) - g_string_append (locations, ", "); + { + g_string_append (locations, ", "); + } g_string_append (locations, location); insert_report_host_detail (report, ip, "cve", cve, @@ -3217,6 +3346,12 @@ cve_scan_host (task_t task, report_t report, gvm_host_t *gvm_host) cve, NULL); } + const char *description; + description = sql_string ("SELECT description FROM scap.cves, scap.cpe_match_nodes" + " WHERE scap.cves.id = scap.cpe_match_nodes.cve_id" + " AND scap.cpe_match_nodes.id = %llu;", + root_node); + desc = g_strdup_printf ("The host carries the product: %s\n" "It is vulnerable according to: %s.\n" "%s%s%s" @@ -3229,8 +3364,7 @@ cve_scan_host (task_t task, report_t report, gvm_host_t *gvm_host) : "", locations->len ? locations->str : "", locations->len ? ".\n" : "", - prognosis_iterator_description - (&prognosis)); + description); g_debug ("%s: making result with severity %1.1f desc [%s]", __func__, severity, desc); @@ -3241,9 +3375,165 @@ cve_scan_host (task_t task, report_t report, gvm_host_t *gvm_host) g_array_append_val (results, result); g_string_free (locations, TRUE); + } - cleanup_iterator (&prognosis); + } + g_free (cpe_product); + } + cleanup_iterator (&host_details_cpe); +} + +/** + * @brief Perform a CVE "scan" on a host. + * + * @param[in] task Task. + * @param[in] report The report to add the host, results and details to. + * @param[in] gvm_host Host. + * + * @return 0 success, 1 failed to get nthlast report for a host. + */ +static int +cve_scan_host (task_t task, report_t report, gvm_host_t *gvm_host) +{ + report_host_t report_host; + gchar *ip, *host; + + assert (task); + assert (report); + + host = gvm_host_value_str (gvm_host); + + ip = report_host_ip (host); + if (ip == NULL) + ip = g_strdup (host); + + g_debug ("%s: ip: %s", __func__, ip); + + /* Get the last report host that applies to the host IP address. */ + if (host_nthlast_report_host (ip, &report_host, 1)) + { + g_warning ("%s: Failed to get nthlast report", __func__); + g_free (ip); + return 1; + } + + g_debug ("%s: report_host: %llu", __func__, report_host); + + if (report_host) + { + iterator_t report_hosts; + + /* Get the report_host for the host. */ + + init_report_host_iterator (&report_hosts, 0, NULL, report_host); + if (next (&report_hosts)) + { + iterator_t prognosis; + int prognosis_report_host, start_time; + GArray *results; + + /* Add report_host with prognosis results and host details. */ + + results = g_array_new (TRUE, TRUE, sizeof (result_t)); + start_time = time (NULL); + prognosis_report_host = 0; + + if (sql_int64_0 ("SELECT count(1) FROM information_schema.tables" + " WHERE table_schema = 'scap'" + " AND table_name = 'cpe_match_nodes';") > 0) + { + // Use new JSON CVE scan + cve_scan_report_host_json (task, report, report_host, ip, + start_time, &prognosis_report_host, + results); + } + else + { + // Use XML CVE scan + init_host_prognosis_iterator (&prognosis, report_host); + while (next (&prognosis)) + { + const char *app, *cve; + double severity; + gchar *desc; + iterator_t locations_iter; + GString *locations; + result_t result; + + if (prognosis_report_host == 0) + prognosis_report_host = manage_report_host_add (report, + ip, + start_time, + 0); + + severity = prognosis_iterator_cvss_double (&prognosis); + + app = prognosis_iterator_cpe (&prognosis); + cve = prognosis_iterator_cve (&prognosis); + locations = g_string_new(""); + + insert_report_host_detail (global_current_report, ip, "cve", cve, + "CVE Scanner", "App", app, NULL); + + init_app_locations_iterator (&locations_iter, report_host, app); + + while (next (&locations_iter)) + { + const char *location; + location = app_locations_iterator_location (&locations_iter); + + if (location == NULL) + { + g_warning ("%s: Location is null for ip %s, app %s", + __func__, ip, app); + continue; + } + + if (locations->len) + g_string_append (locations, ", "); + g_string_append (locations, location); + + insert_report_host_detail (report, ip, "cve", cve, + "CVE Scanner", app, location, NULL); + + insert_report_host_detail (report, ip, "cve", cve, + "CVE Scanner", "detected_at", + location, NULL); + + insert_report_host_detail (report, ip, "cve", cve, + "CVE Scanner", "detected_by", + /* Detected by itself. */ + cve, NULL); + } + + desc = g_strdup_printf ("The host carries the product: %s\n" + "It is vulnerable according to: %s.\n" + "%s%s%s" + "\n" + "%s", + app, + cve, + locations->len + ? "The product was found at: " + : "", + locations->len ? locations->str : "", + locations->len ? ".\n" : "", + prognosis_iterator_description + (&prognosis)); + + g_debug ("%s: making result with severity %1.1f desc [%s]", + __func__, severity, desc); + + result = make_cve_result (task, ip, cve, severity, desc); + g_free (desc); + + g_array_append_val (results, result); + + g_string_free (locations, TRUE); + } + cleanup_iterator (&prognosis); + } report_add_results_array (report, results); g_array_free (results, TRUE); diff --git a/src/manage.h b/src/manage.h index a2cfc5775..df8f144a4 100644 --- a/src/manage.h +++ b/src/manage.h @@ -1688,7 +1688,52 @@ void init_app_locations_iterator (iterator_t*, report_host_t, const gchar *); const char * -app_locations_iterator_location (iterator_t *); +app_locations_iterator_location (iterator_t*); + +void +init_cpe_match_nodes_iterator (iterator_t*, const char *); + +long long int +cpe_match_nodes_iterator_root_id (iterator_t*); + +void +init_host_details_cpe_iterator (iterator_t*, report_host_t); + +const char* +host_details_cpe_iterator_cpe (iterator_t*); + +void +init_cpe_match_node_childs_iterator (iterator_t*, long long int); + +long long int +cpe_match_node_childs_iterator_id (iterator_t*); + +void +init_cpe_match_range_iterator (iterator_t*, long long int); + +const char* +cpe_match_range_iterator_cpe (iterator_t*); + +const char* +cpe_match_range_iterator_version_start_incl (iterator_t*); + +const char* +cpe_match_range_iterator_version_start_excl (iterator_t*); + +const char* +cpe_match_range_iterator_version_end_incl (iterator_t*); + +const char* +cpe_match_range_iterator_version_end_excl (iterator_t*); + +int +cpe_match_range_iterator_vulnerable (iterator_t*); + +void +init_host_details_cpe_product_iterator (iterator_t*, const char *, report_host_t); + +const char* +host_details_cpe_product_iterator_value (iterator_t*); void init_host_prognosis_iterator (iterator_t*, report_host_t); diff --git a/src/manage_pg.c b/src/manage_pg.c index 4bc218524..f53b9f601 100644 --- a/src/manage_pg.c +++ b/src/manage_pg.c @@ -1589,6 +1589,8 @@ manage_create_sql_functions () " THEN $1 = 0" " WHEN 'false'" " THEN $1 = -1" + " WHEN 'error'" + " THEN $1 = -3" " ELSE 0::boolean" " END);" "$$ LANGUAGE SQL" @@ -3545,6 +3547,7 @@ manage_db_init (const gchar *name) sql ("CREATE TABLE scap2.cpe_match_nodes" " (id SERIAL PRIMARY KEY," " parent_id INTEGER DEFAULT 0," + " root_id INTEGER DEFAULT 0," " cve_id INTEGER DEFAULT 0," " operator text);"); @@ -3552,11 +3555,11 @@ manage_db_init (const gchar *name) " (id SERIAL PRIMARY KEY," " node_id INTEGER DEFAULT 0," " vulnerable INTEGER DEFAULT 0," - " cpe text," - " version_start_incl text," - " version_start_excl text," - " version_end_incl text," - " version_end_excl text);"); + " cpe text DEFAULT NULL," + " version_start_incl text DEFAULT NULL," + " version_start_excl text DEFAULT NULL," + " version_end_incl text DEFAULT NULL," + " version_end_excl text DEFAULT NULL);"); sql ("CREATE TABLE scap2.cpe_details" " (id SERIAL PRIMARY KEY," diff --git a/src/manage_sql.c b/src/manage_sql.c index 2b87ba8ac..fc1bf3905 100644 --- a/src/manage_sql.c +++ b/src/manage_sql.c @@ -20446,6 +20446,198 @@ app_locations_iterator_location (iterator_t *iterator) return iterator_string (iterator, 0); } +/** + * @brief Initialize an iterator of CPEs for a report's host. + * + * @param[in] iterator Iterator. + * @param[in] report_host Report host. + */ +void +init_host_details_cpe_iterator (iterator_t *iterator, report_host_t report_host) +{ + init_iterator (iterator, + "SELECT DISTINCT LOWER (value) FROM report_host_details" + " WHERE name = 'App' and report_host = %llu;", + report_host); +} + +/** + * @brief Get a CPE from an CPE iterator. + * + * @param[in] iterator Iterator. + * + * @return The CPE. + */ +DEF_ACCESS (host_details_cpe_iterator_cpe, 0); + +/** + * @brief Initialize an iterator of CPEs for a product of a report's host. + * + * @param[in] iterator Iterator. + * @param[in] product The product for which to get the CPEs. + * @param[in] report_host Report host. + */ +void +init_host_details_cpe_product_iterator (iterator_t* iterator, const char *product, report_host_t report_host) +{ + gchar *quoted_product; + quoted_product = sql_quote (product); + init_iterator (iterator, + "SELECT DISTINCT LOWER (value) FROM report_host_details" + " WHERE name = 'App' AND report_host = %llu" + " AND value like '%s%s';", + report_host, quoted_product, "%"); + g_free (quoted_product); +} + +/** + * @brief Get a CPE from an CPE product iterator. + * + * @param[in] iterator Iterator. + * + * @return The CPE. + */ +DEF_ACCESS (host_details_cpe_product_iterator_value, 0); + +/** + * @brief Initialize an iterator of root_ids of CPE match nodes. + * + * @param[in] iterator Iterator. + * @param[in] cpe The cpe contained in the match nodes. + */ +void +init_cpe_match_nodes_iterator (iterator_t* iterator, const char *cpe) +{ + gchar *quoted_cpe; + quoted_cpe = sql_quote (cpe); + init_iterator (iterator, + "SELECT DISTINCT root_id" + " FROM scap.cpe_match_nodes, scap.cpe_match_range" + " WHERE cpe like '%s%%' AND scap.cpe_match_nodes.id = node_id;", + quoted_cpe); + g_free (quoted_cpe); +} + +/** + * @brief Get a root id from an CPE match node iterator. + * + * @param[in] iterator Iterator. + * + * @return The root id. + */ +long long int +cpe_match_nodes_iterator_root_id (iterator_t* iterator) +{ + return iterator_int64 (iterator, 0); +} + +/** + * @brief Initialize an iterator of childs of an CPE match node. + * + * @param[in] iterator Iterator. + * @param[in] node The match node with the childs. + */ +void +init_cpe_match_node_childs_iterator (iterator_t* iterator, long long int node) +{ + init_iterator (iterator, + "SELECT id FROM scap.cpe_match_nodes" + " WHERE parent_id = %llu;", + node); +} + +/** + * @brief Get a child from an CPE match node childs iterator. + * + * @param[in] iterator Iterator. + * + * @return The id of the child node. + */ +long long int +cpe_match_node_childs_iterator_id (iterator_t* iterator) +{ + return iterator_int64 (iterator, 0); +} + +/** + * @brief Initialize an iterator of match ranges of an CPE match node. + * + * @param[in] iterator Iterator. + * @param[in] node The match node with match ranges. + */ +void +init_cpe_match_range_iterator (iterator_t* iterator, long long int node) +{ + init_iterator (iterator, + "SELECT vulnerable, cpe, version_start_incl," + " version_start_excl, version_end_incl, version_end_excl" + " FROM scap.cpe_match_range" + " WHERE node_id = %llu;", + node); +} + +/** + * @brief Return if the CPE of the actual match node is vulnerable. + * + * @param[in] iterator Iterator. + * + * @return 1 if the match node is vulnerable, 0 otherwise. + */ +int +cpe_match_range_iterator_vulnerable (iterator_t* iterator) +{ + return iterator_int64 (iterator, 0); +} + +/** + * @brief Return the CPE of the actual match node. + * + * @param[in] iterator Iterator. + * + * @return The CPE of the actual match node. + */ +DEF_ACCESS (cpe_match_range_iterator_cpe, 1); + +/** + * @brief Return the start included version of the actual match node. + * + * @param[in] iterator Iterator. + * + * @return The start included version of the actual match node, if any. + * NULL otherwise. + */ +DEF_ACCESS (cpe_match_range_iterator_version_start_incl, 2); + +/** + * @brief Return the start excluded version of the actual match node. + * + * @param[in] iterator Iterator. + * + * @return The start excluded version of the actual match node, if any. + * NULL otherwise. + */ +DEF_ACCESS (cpe_match_range_iterator_version_start_excl, 3); + +/** + * @brief Return the end included version of the actual match node. + * + * @param[in] iterator Iterator. + * + * @return The end included version of the actual match node, if any. + * NULL otherwise. + */ +DEF_ACCESS (cpe_match_range_iterator_version_end_incl, 4); + +/** + * @brief Return the end excluded version of the actual match node. + * + * @param[in] iterator Iterator. + * + * @return The end excluded version of the actual match node, if any. + * NULL otherwise. + */ +DEF_ACCESS (cpe_match_range_iterator_version_end_excl, 5); + /** * @brief Initialise a report host prognosis iterator. * diff --git a/src/manage_sql_secinfo.c b/src/manage_sql_secinfo.c index 71cc6e055..cd95c4aff 100644 --- a/src/manage_sql_secinfo.c +++ b/src/manage_sql_secinfo.c @@ -3246,7 +3246,7 @@ insert_cve_from_entry (element_t entry, element_t last_modified, * @brief Save the node of a cve match rule tree. * * @param[in] parent_id The parent_id of the node. If this value is 0, - * this node is the root of the tree. + * the node is the root of the tree. * @param[in] cve_id The id of the CVE to which the tree belongs. * @param[in] operator The operator for the match rules. * @@ -3280,7 +3280,6 @@ add_cpe_match_rules (result_t id, cJSON *match_rules) cJSON *cpe_js; gboolean vulnerable = FALSE; - char * cpe = NULL; char * version_start_incl = NULL; char * version_start_excl = NULL; char * version_end_incl = NULL; @@ -3288,9 +3287,8 @@ add_cpe_match_rules (result_t id, cJSON *match_rules) cJSON_ArrayForEach(match_rule, match_rules) { - char *quoted_cpe; + char *sql_cpe = NULL; vulnerable = FALSE; - cpe = NULL; version_start_incl = NULL; version_start_excl = NULL; version_end_incl = NULL; @@ -3300,22 +3298,40 @@ add_cpe_match_rules (result_t id, cJSON *match_rules) vulnerable = TRUE; else vulnerable = FALSE; + cpe_js = cJSON_GetObjectItemCaseSensitive(match_rule, "cpe23Uri"); - if (cpe_js != NULL) - cpe = cpe_js->valuestring; - quoted_cpe = sql_quote (cpe); + if (cpe_js != NULL && strcmp (cpe_js->valuestring, "(null)")) + { + char *quoted_cpe = sql_quote (cpe_js->valuestring); + sql_cpe = g_strdup_printf ("'%s'", quoted_cpe); + g_free (quoted_cpe); + } + else + sql_cpe = g_strdup ("NULL"); + ver_se = cJSON_GetObjectItemCaseSensitive(match_rule, "versionStartIncluding"); - if (ver_se != NULL) - version_start_incl = ver_se->valuestring; + if (ver_se != NULL && strcmp (ver_se->valuestring, "(null)")) + version_start_incl = g_strdup_printf ("'%s'", ver_se->valuestring); + else + version_start_incl = g_strdup ("NULL"); + ver_se = cJSON_GetObjectItemCaseSensitive(match_rule, "versionStartExcluding"); - if (ver_se != NULL) - version_start_excl = ver_se->valuestring; + if (ver_se != NULL && strcmp (ver_se->valuestring, "(null)")) + version_start_excl = g_strdup_printf ("'%s'", ver_se->valuestring); + else + version_start_excl = g_strdup ("NULL"); + ver_se = cJSON_GetObjectItemCaseSensitive(match_rule, "versionEndIncluding"); - if (ver_se != NULL) - version_end_incl = ver_se->valuestring; + if (ver_se != NULL && strcmp (ver_se->valuestring, "(null)")) + version_end_incl = g_strdup_printf ("'%s'", ver_se->valuestring); + else + version_end_incl = g_strdup ("NULL"); + ver_se = cJSON_GetObjectItemCaseSensitive(match_rule, "versionEndExcluding"); - if (ver_se != NULL) - version_end_excl = ver_se->valuestring; + if (ver_se != NULL && strcmp (ver_se->valuestring, "(null)")) + version_end_excl = g_strdup_printf ("'%s'", ver_se->valuestring); + else + version_end_excl = g_strdup ("NULL"); sql ("INSERT INTO scap2.cpe_match_range" @@ -3323,30 +3339,51 @@ add_cpe_match_rules (result_t id, cJSON *match_rules) " version_start_incl, version_start_excl," " version_end_incl, version_end_excl)" " VALUES" - " (%llu, %d, '%s', '%s', '%s', '%s', '%s')", + " (%llu, %d, %s, %s, %s, %s, %s)", id, vulnerable ? 1 : 0, - quoted_cpe, - version_start_incl, - version_start_excl, - version_end_incl, - version_end_excl); - g_free (quoted_cpe); + sql_cpe ? sql_cpe : "", + version_start_incl ? version_start_incl : "", + version_start_excl ? version_start_excl : "", + version_end_incl ? version_end_incl : "", + version_end_excl ? version_end_excl : ""); + + g_free (sql_cpe); + g_free (version_start_incl); + g_free (version_start_excl); + g_free (version_end_incl); + g_free (version_end_excl); } } +/** + * @brief Set the root id for a node of a cve match rule tree. + * + * @param[in] id The id of the node for which the root id is to be set. + * @param[in] root_id The id of the root of the tree this node belongs to. + */ +static void +set_root_id (long int id, long int root_id) +{ + sql ("UPDATE scap2.cpe_match_nodes set root_id = %i" + " WHERE id = %i;", + root_id, + id); +} + /** * @brief Load and add recursively all nodes of a match rule tree for a * specific CVE. Build a match rule tree. * - * @param[in] parent_id The parent_id of the nodes to insert + * @param[in] parent_id The parent id of the nodes to insert * (0 for the root node). * @param[in] cveid The id of the CVE the tree belongs to. + * @param[in] root_id The root id of the nodes to insert. * @param[in] nodes The JSON object that contains the rules for a * specific tree level. */ static void -load_nodes (resource_t parent_id, resource_t cveid, cJSON *nodes) +load_nodes (resource_t parent_id, resource_t cveid, resource_t root_id, cJSON *nodes) { cJSON *node; resource_t id; @@ -3366,13 +3403,20 @@ load_nodes (resource_t parent_id, resource_t cveid, cJSON *nodes) cJSON_ArrayForEach(node, nodes) { operator = cJSON_GetObjectItemCaseSensitive(node, "operator"); - if (operator) + if (operator && operator->valuestring) id = save_node (parent_id, cveid, operator->valuestring); + else + return; + + if (parent_id == 0) + root_id = id; + set_root_id (id, root_id); + cpe_match_rules = cJSON_GetObjectItemCaseSensitive(node, "cpe_match"); if (cpe_match_rules) add_cpe_match_rules (id, cpe_match_rules); child_nodes = cJSON_GetObjectItemCaseSensitive(node, "children"); - load_nodes (id, cveid, child_nodes); + load_nodes (id, cveid, root_id, child_nodes); } } @@ -3550,7 +3594,7 @@ handle_json_cve_item (cJSON *item) g_warning("%s: nodes missing for %s.", __func__, cve_id); return -1; } - load_nodes (0, cve_db_id, nodes_json); + load_nodes (0, cve_db_id, 0, nodes_json); return 0; }