From 3b1a44eccfbae0e3a71b2943038a0e13b8fc9276 Mon Sep 17 00:00:00 2001 From: Juan Jose Nicola Date: Thu, 26 Sep 2024 11:21:19 -0300 Subject: [PATCH] Use json pull for parsing the feed metadata --- openvasd/CMakeLists.txt | 4 +- openvasd/jsonutils.c | 515 ------------------------------------- openvasd/jsonutils.h | 33 --- openvasd/openvasd.c | 550 +++++++++++++++++++++++++++++++++------- openvasd/openvasd.h | 26 ++ openvasd/vtparser.c | 417 ++++++++++++++++++++++++++++++ openvasd/vtparser.h | 20 ++ util/jsonpull.c | 1 + 8 files changed, 929 insertions(+), 637 deletions(-) delete mode 100644 openvasd/jsonutils.c delete mode 100644 openvasd/jsonutils.h create mode 100644 openvasd/vtparser.c create mode 100644 openvasd/vtparser.h diff --git a/openvasd/CMakeLists.txt b/openvasd/CMakeLists.txt index cad6595c..4907b468 100644 --- a/openvasd/CMakeLists.txt +++ b/openvasd/CMakeLists.txt @@ -23,8 +23,8 @@ pkg_check_modules (GLIB_JSON REQUIRED json-glib-1.0>=1.4.4) include_directories (${GLIB_INCLUDE_DIRS} ${GLIB_JSON_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS}) -set (FILES openvasd.c jsonutils.c) -set (HEADERS openvasd.h jsonutils.h) +set (FILES openvasd.c vtparser.c) +set (HEADERS openvasd.h vtparser.h) if (BUILD_STATIC) add_library (gvm_openvasd_static STATIC ${FILES}) diff --git a/openvasd/jsonutils.c b/openvasd/jsonutils.c deleted file mode 100644 index d636535f..00000000 --- a/openvasd/jsonutils.c +++ /dev/null @@ -1,515 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Greenbone AG - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -/** - * @file - * @brief Simple JSON reader. - */ - -#include "jsonutils.h" - -#include "../base/cvss.h" - -#include -#include -#include - -/** - * @brief VT categories - */ -typedef enum -{ - ACT_INIT = 0, - ACT_SCANNER, - ACT_SETTINGS, - ACT_GATHER_INFO, - ACT_ATTACK, - ACT_MIXED_ATTACK, - ACT_DESTRUCTIVE_ATTACK, - ACT_DENIAL, - ACT_KILL_HOST, - ACT_FLOOD, - ACT_END, -} nvt_category; - -/** - * @brief Get the VT category type given the category as string - * - * @param cat The category as string. - * - * @return Integer representing the category type. - */ -static int -get_category_from_name (const char *cat) -{ - if (!g_strcmp0 (cat, "init")) - return ACT_INIT; - else if (!g_strcmp0 (cat, "scanner")) - return ACT_SCANNER; - else if (!g_strcmp0 (cat, "settings")) - return ACT_SETTINGS; - else if (!g_strcmp0 (cat, "gather_info")) - return ACT_GATHER_INFO; - else if (!g_strcmp0 (cat, "attack")) - return ACT_ATTACK; - else if (!g_strcmp0 (cat, "mixed_attack")) - return ACT_MIXED_ATTACK; - else if (!g_strcmp0 (cat, "destructive_attack")) - return ACT_DESTRUCTIVE_ATTACK; - else if (!g_strcmp0 (cat, "denial")) - return ACT_DENIAL; - else if (!g_strcmp0 (cat, "kill_host")) - return ACT_KILL_HOST; - else if (!g_strcmp0 (cat, "flood")) - return ACT_FLOOD; - else if (!g_strcmp0 (cat, "end")) - return ACT_END; - - return -1; -} - -/** - * @brief Get a new json parser object. It must be free with - * gvm_close_jnode_parser() by the caller. - */ -jparser_t -gvm_parse_jnode (void) -{ - jparser_t parser; - parser = json_parser_new (); - return parser; -} - -/** - * @brief Create a JSON reader object - * - * @param[in] str JSON data as string to be read. - * @param[in] parse The JSON parser to be use for loading the JSON data. - * @param[out] read The JSON reader pointing to the begining of the data - * - * @return 0 on sucess, -1 otherwise. It must be free with - * gvm_close_jnode_parser() by the caller. - */ -int -gvm_read_jnode (const char *str, jparser_t parse, jreader_t *read) -{ - JsonParser *parser = parse; - - GError *err = NULL; - if (!json_parser_load_from_data (parser, str, strlen (str), &err)) - { - g_warning ("%s: Parsing json string: %s", __func__, err->message); - g_error_free (err); - g_object_unref (parser); - return -1; - } - - *read = (jreader_t) json_reader_new (json_parser_get_root (parser)); - return 0; -} - -/** - * @brief Free JSON reader object - * - * @param read JSON Reader to be free()'d. - */ -void -gvm_close_jnode_reader (jreader_t read) -{ - JsonReader *reader = read; - if (reader) - g_object_unref (reader); -} - -/** - * @brief Free JSON parser object - * - * @param parse JSON parser to be free()'d. - */ -void -gvm_close_jnode_parser (jparser_t parse) -{ - JsonParser *parser = parse; - - if (parser) - g_object_unref (parser); -} - -/** - * @brief Count the elements in an array. - * - * @param[in] read JSON reader object pointing to an array. - * - * @return Integer with the amount of elements in the array, -1 otherwise. - */ -int -gvm_jnode_count_elements (jreader_t read) -{ - JsonReader *reader = read; - - if (!json_reader_is_array (reader)) - { - // No results. No information. - return -1; - } - - return json_reader_count_elements (reader); -} - -/** - * @brief Parse a VT element given in json format. - * - * @param[in] reader JSON reader object pointing to an VT element. - * - * @return nvti structur containing the VT metadata, NULL otherwise. - * The nvti struct must be free() by the caller - */ -nvti_t * -gvm_jnode_parse_vt (jreader_t reader) -{ - nvti_t *nvt = NULL; - static int element_index = 0; - - if (!json_reader_read_element (reader, element_index)) - { - g_debug ("%s: Array empty, array end or error", __func__); - return NULL; - } - - element_index++; - - if (!json_reader_is_object (reader)) - { - g_debug ("%s: Error reading VT object", __func__); - return NULL; - } - - nvt = nvti_new (); - - if (json_reader_read_member (reader, "oid")) - { - nvti_set_oid (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - else - { - g_warning ("%s: Missing OID", __func__); - json_reader_end_member (reader); - nvti_free (nvt); - return NULL; - } - - if (json_reader_read_member (reader, "name")) - { - nvti_set_name (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - else - { - g_warning ("%s: VT missing NAME", __func__); - json_reader_end_member (reader); - nvti_free (nvt); - return NULL; - } - - if (json_reader_read_member (reader, "family")) - { - nvti_set_family (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - else - { - g_warning ("%s: VT missing FAMILY", __func__); - json_reader_end_member (reader); - nvti_free (nvt); - return NULL; - } - - if (json_reader_read_member (reader, "category")) - { - nvti_set_category ( - nvt, get_category_from_name (json_reader_get_string_value (reader))); - json_reader_end_member (reader); - } - else - { - g_warning ("%s: VT missing CATEGORY", __func__); - json_reader_end_member (reader); - nvti_free (nvt); - return NULL; - } - - json_reader_read_member (reader, "tag"); // begin tag - - json_reader_read_member (reader, "affected"); - nvti_set_affected (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - json_reader_read_member (reader, "creation_date"); - nvti_set_creation_time (nvt, json_reader_get_int_value (reader)); - json_reader_end_member (reader); - - json_reader_read_member (reader, "last_modification"); - nvti_set_modification_time (nvt, json_reader_get_int_value (reader)); - json_reader_end_member (reader); - - json_reader_read_member (reader, "insight"); - nvti_set_insight (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - json_reader_read_member (reader, "impact"); - nvti_set_impact (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - json_reader_read_member (reader, "qod"); - nvti_set_qod (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - json_reader_read_member (reader, "qod_type"); - nvti_set_qod_type (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - if (json_reader_read_member (reader, "solution")) - { - nvti_set_solution (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - if (json_reader_read_member (reader, "solution_type")) - { - nvti_set_solution_type (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - else - { - g_debug ("%s: SOLUTION: missing type for OID: %s", __func__, - nvti_oid (nvt)); - json_reader_end_member (reader); - } - json_reader_read_member (reader, "solution_method"); - nvti_set_solution_method (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - - json_reader_read_member (reader, "summary"); - nvti_set_summary (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - json_reader_read_member (reader, "vuldetect"); - nvti_set_detection (nvt, json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - // Parse severity - char *severity_vector; - - json_reader_read_member (reader, "severity_vector"); - severity_vector = g_strdup (json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - if (!severity_vector) - { - json_reader_read_member (reader, "cvss_base_vector"); - severity_vector = g_strdup (json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - - if (severity_vector) - { - char *severity_origin, *severity_type; - char *cvss_base; - - time_t severity_date; - double cvss_base_dbl; - - if (g_strrstr (severity_vector, "CVSS:3")) - severity_type = g_strdup ("cvss_base_v3"); - else - severity_type = g_strdup ("cvss_base_v2"); - - cvss_base_dbl = get_cvss_score_from_base_metrics (severity_vector); - - json_reader_read_member (reader, "severity_date"); - severity_date = json_reader_get_int_value (reader); - json_reader_end_member (reader); - - json_reader_read_member (reader, "severity_origin"); - severity_origin = g_strdup (json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - nvti_add_vtseverity (nvt, vtseverity_new (severity_type, severity_origin, - severity_date, cvss_base_dbl, - severity_vector)); - - nvti_add_tag (nvt, "cvss_base_vector", severity_vector); - - cvss_base = g_strdup_printf ( - "%.1f", get_cvss_score_from_base_metrics (severity_vector)); - nvti_set_cvss_base (nvt, cvss_base); - - g_free (cvss_base); - g_free (severity_vector); - g_free (severity_origin); - g_free (severity_type); - // end parsing severity - } - else - { - g_warning ("%s: SEVERITY missing value element", __func__); - nvti_free (nvt); - return NULL; - } - - json_reader_end_member (reader); // end tag - - // Parse references - if (json_reader_read_member (reader, "references")) - { - if (!json_reader_is_array (reader)) - { - g_debug ("%s: Error reading VT/REFS array", __func__); - json_reader_end_member (reader); - } - else - { - int count = json_reader_count_elements (reader); - for (int j = 0; j < count; j++) - { - char *id, *class; - json_reader_read_element (reader, j); - if (!json_reader_is_object (reader)) - { - g_debug ("%s: Error reading VT/REFS reference object", - __func__); - json_reader_end_element (reader); - continue; - } - if (!json_reader_read_member (reader, "class")) - { - g_warning ("%s: REF missing type attribute", __func__); - json_reader_end_member (reader); - json_reader_end_element (reader); - continue; - } - else - { - class = g_strdup (json_reader_get_string_value (reader)); - json_reader_end_member (reader); - - if (!json_reader_read_member (reader, "id")) - { - g_warning ("%s: REF missing ID attribute", __func__); - json_reader_end_member (reader); - json_reader_end_element (reader); - g_free (class); - continue; - } - - id = g_strdup (json_reader_get_string_value (reader)); - nvti_add_vtref (nvt, vtref_new (class, id, NULL)); - json_reader_end_member (reader); - json_reader_end_element (reader); - g_free (class); - g_free (id); - } - } - } - } - json_reader_end_member (reader); // End references - - // Parse preferences - if (json_reader_read_member (reader, "preferences")) - { - if (!json_reader_is_array (reader)) - { - g_debug ("%s: Error reading VT/REFS array", __func__); - json_reader_end_member (reader); - } - else - { - int count = json_reader_count_elements (reader); - for (int j = 0; j < count; j++) - { - char *class, *name, *default_val; - int id; - - json_reader_read_element (reader, j); - if (!json_reader_is_object (reader)) - { - g_debug ("%s: Error reading VT/PREFS preference object", - __func__); - json_reader_end_element (reader); - continue; - } - - if (!json_reader_read_member (reader, "class")) - { - g_warning ("%s: PREF missing type attribute", __func__); - json_reader_end_member (reader); - json_reader_end_element (reader); - continue; - } - else - { - class = g_strdup (json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - - if (!json_reader_read_member (reader, "id")) - { - g_warning ("%s: PREF missing id attribute", __func__); - json_reader_end_member (reader); - json_reader_end_element (reader); - g_free (class); - continue; - } - else - { - id = json_reader_get_int_value (reader); - json_reader_end_member (reader); - } - - if (!json_reader_read_member (reader, "name")) - { - g_warning ("%s: PREF missing name attribute", __func__); - json_reader_end_member (reader); - json_reader_end_element (reader); - g_free (class); - continue; - } - else - { - name = g_strdup (json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - - if (!json_reader_read_member (reader, "default")) - { - g_warning ("%s: PREF missing default value attribute", - __func__); - json_reader_end_member (reader); - json_reader_end_element (reader); - g_free (class); - g_free (name); - continue; - } - else - { - default_val = - g_strdup (json_reader_get_string_value (reader)); - json_reader_end_member (reader); - } - - nvti_add_pref (nvt, nvtpref_new (id, name, class, default_val)); - json_reader_end_element (reader); // finish preference - g_free (class); - g_free (name); - g_free (default_val); - } - } - } - json_reader_end_member (reader); // End preferences - json_reader_end_element (reader); // End vt - - return nvt; -} diff --git a/openvasd/jsonutils.h b/openvasd/jsonutils.h deleted file mode 100644 index 9b55e3d8..00000000 --- a/openvasd/jsonutils.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-FileCopyrightText: 2024 Greenbone AG - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -/** - * @file - * @brief Simple JSON reader - */ - -#ifndef _GVM_JSONUTILS_H -#define _GVM_JSONUTILS_H - -#include "../base/nvti.h" - -typedef void *jreader_t; -typedef void *jparser_t; - -int -gvm_read_jnode (const char *, jparser_t, jreader_t *); - -jparser_t -gvm_parse_jnode (void); - -void gvm_close_jnode_reader (jreader_t); - -void gvm_close_jnode_parser (jparser_t); - -int gvm_jnode_count_elements (jreader_t); - -nvti_t *gvm_jnode_parse_vt (jreader_t); - -#endif diff --git a/openvasd/openvasd.c b/openvasd/openvasd.c index 70393b42..35957185 100644 --- a/openvasd/openvasd.c +++ b/openvasd/openvasd.c @@ -15,7 +15,9 @@ #include #include +#include #include +#include #include #include @@ -26,6 +28,7 @@ #define G_LOG_DOMAIN "libgvm ovd" #define RESP_CODE_ERR -1 +#define RESP_CODE_OK 0 /** * @brief Struct holding the data for connecting with Openvasd. @@ -226,20 +229,12 @@ openvasd_response_free (openvasd_resp_t resp) resp = NULL; } -/** @brief Define a string struct for storing the response. - */ -struct string -{ - char *ptr; - size_t len; -}; - /** @brief Initialize the string struct to hold the response * * @param s[in/out] The string struct to be initialized */ -static void -init_string (struct string *s) +void +init_stringstream (stringstream *s) { s->len = 0; s->ptr = g_malloc0 (s->len + 1); @@ -259,7 +254,7 @@ init_string (struct string *s) static size_t response_callback_fn (void *ptr, size_t size, size_t nmemb, void *struct_string) { - struct string *s = struct_string; + stringstream *s = struct_string; size_t new_len = s->len + size * nmemb; char *ptr_aux = g_realloc (s->ptr, new_len + 1); s->ptr = ptr_aux; @@ -275,44 +270,42 @@ response_callback_fn (void *ptr, size_t size, size_t nmemb, void *struct_string) return size * nmemb; } -/** @brief Send request +/** @brief Create a CURL handler * * @param[in] conn struct holding the openvasd connector information * @param[in] method request method (e.g. GET) * @param[in] path Path to the resource (e.g. /vts) * @param[in] data String containing the request body in json format (scan * action, scan config) - * @param[in] header_name If this field is set, is looked in the header and - * its value is returned + * @param[out] resp Structure holding the body response, filled by the + * callback function + * @param[out] err On error, this variable is filled with an error message + * in json format. * - * @return Return struct containing the http response code and the response - * body. In case of error the struct is filled with code RESP_CODE_ERR (-1) and - * a message. NULL on memory related error. Response must be free()'ed by the - * caller with openvasd_response_free() + * @return a CURL handler, or NULL on error. */ -static openvasd_resp_t -openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, - char *path, char *data, const char *header_name) +static CURL * +handler (openvasd_connector_t *conn, openvasd_req_method_t method, char *path, + char *data, stringstream *resp, char **err) { CURL *curl; GString *url = NULL; - long http_code = RESP_CODE_ERR; - struct string resp; struct curl_slist *customheader = NULL; GString *xapikey = NULL; - openvasd_resp_t response; - response = g_malloc0 (sizeof (struct openvasd_response)); - if (response == NULL) - return NULL; + if (!conn) + { + *err = g_strdup ("{\"error\": \"Missing openvasd connector\"}"); + g_warning ("%s: Missing openvasd connector", __func__); + return NULL; + } if ((curl = curl_easy_init ()) == NULL) { - response->code = http_code; - response->body = + *err = g_strdup ("{\"error\": \"Not possible to initialize curl library\"}"); g_warning ("%s: Not possible to initialize curl library", __func__); - return response; + return NULL; } url = g_string_new (g_strdup ((*conn)->server)); @@ -327,15 +320,14 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, if (path != NULL && path[0] != '\0') g_string_append (url, path); - g_debug ("%s: URL: %s", __func__, url->str); // Set URL + g_debug ("%s: URL: %s", __func__, url->str); if (curl_easy_setopt (curl, CURLOPT_URL, g_strdup (url->str)) != CURLE_OK) { g_warning ("%s: Not possible to set the URL", __func__); curl_easy_cleanup (curl); - response->code = http_code; - response->body = g_strdup ("{\"error\": \"Not possible to set URL\"}"); - return response; + *err = g_strdup ("{\"error\": \"Not possible to set URL\"}"); + return NULL; } g_string_free (url, TRUE); @@ -353,10 +345,9 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, { g_warning ("%s: Not possible to set the CA certificate", __func__); curl_easy_cleanup (curl); - response->code = http_code; - response->body = + *err = g_strdup ("{\"error\": \"Not possible to set CA certificate\"}"); - return response; + return NULL; } } else @@ -365,6 +356,7 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt (curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2); + g_debug ("%s: Server certificate verification disabled.", __func__); } // Client certificate @@ -380,10 +372,9 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, g_warning ("%s: Not possible to set the Client certificate", __func__); curl_easy_cleanup (curl); - response->code = http_code; - response->body = g_strdup ( + *err = g_strdup ( "{\"error\": \"Not possible to set Client certificate\"}"); - return response; + return NULL; } blob.data = (*conn)->key; blob.len = strlen ((*conn)->key); @@ -394,10 +385,9 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, g_warning ("%s: Not possible to set the Client private key", __func__); curl_easy_cleanup (curl); - response->code = http_code; - response->body = g_strdup ( + *err = g_strdup ( "{\"error\": \"Not possible to set Client private key\"}"); - return response; + return NULL; } } @@ -438,16 +428,37 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, curl_easy_setopt (curl, CURLOPT_HTTPHEADER, customheader); // Init the struct where the response is stored and set the callback function - init_string (&resp); curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, response_callback_fn); - curl_easy_setopt (curl, CURLOPT_WRITEDATA, &resp); + curl_easy_setopt (curl, CURLOPT_WRITEDATA, resp); + + return curl; +} + +/** @brief Send request + * + * @param[in] curl The CURL handler to perform an request. + * @param[in] header_name If this field is set, is looked in the header and + * its value is returned inside the response. + * @param[out] response The response struct to be filled with the response + * code and the header value. + * + * @return Return struct containing the http response code and the response + * body. In case of error the struct is filled with code RESP_CODE_ERR (-1) and + * a message. NULL on memory related error. Response must be free()'ed by the + * caller with openvasd_response_free() + */ +static openvasd_resp_t +openvasd_send_request (CURL *curl, const char *header_name, + openvasd_resp_t response) +{ + long http_code = RESP_CODE_ERR; int ret = CURLE_OK; if ((ret = curl_easy_perform (curl)) != CURLE_OK) { g_warning ("%s: Error sending request: %d", __func__, ret); curl_easy_cleanup (curl); - g_free (resp.ptr); + curl = NULL; response->code = http_code; response->body = g_strdup ("{\"error\": \"Error sending request\"}"); return response; @@ -462,10 +473,8 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, response->header = g_strdup (hname->value); } curl_easy_cleanup (curl); - g_debug ("%ld - Server response %s", http_code, resp.ptr); + curl = NULL; response->code = http_code; - response->body = g_strdup (resp.ptr); - g_free (resp.ptr); return response; } @@ -480,7 +489,114 @@ openvasd_send_request (openvasd_connector_t *conn, openvasd_req_method_t method, openvasd_resp_t openvasd_get_version (openvasd_connector_t *conn) { - return openvasd_send_request (conn, HEAD, "/", NULL, NULL); + char *err = NULL; + CURL *hnd = NULL; + openvasd_resp_t response = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; + + init_stringstream (&resp); + if ((hnd = handler (conn, HEAD, "/", NULL, &resp, &err)) == NULL) + { + response->code = RESP_CODE_ERR; + response->body = err; + g_free (resp.ptr); + return response; + } + + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + + g_free (resp.ptr); + return response; +} +/** + * @brief Wrapps a CURLM * handler + */ +curlm_t +curlm_handler_new (void) +{ + CURLM *h = NULL; + return h; +} + +/** + * @brief Initialized an curl multiperform handler which allows fetch feed + * metadata chunk by chunk. + * + * @param conn[in] Connector struct with the data necessary for the connection + * @param mhnd[out] The curl multiperform handler + * @param resp[out] The stringstream struct for the write callback function. + * + * @return The response. Null on error. + */ +openvasd_resp_t +openvasd_get_vts_stream_init (openvasd_connector_t *conn, curlm_t *mhnd, + stringstream *resp) +{ + GString *path; + openvasd_resp_t response = NULL; + char *err = NULL; + CURL *hnd = NULL; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; + + path = g_string_new ("/vts?information=1"); + if ((hnd = handler (conn, GET, path->str, NULL, resp, &err)) == NULL) + { + g_string_free (path, TRUE); + g_free (resp->ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } + g_string_free (path, TRUE); + + *mhnd = curl_multi_init (); + curl_multi_add_handle (*mhnd, hnd); + + response->code = RESP_CODE_OK; + return response; +} + +/** + * @brief Get a new feed metadata chunk. This function must be call until the + * return value is 0, meaning there is no more data to fetch. + * + * @param[in] conn Connector struct with the data necessary for the connection + * @param[in/out] mhnd Curl multiperfom for requesting the feed metadata + * @param[out] The stringstream struct for the write callback function. + * + * @return greather than 0 if the handler is still getting data. 0 if the + * transmision finished. -1 on error + */ +int +openvasd_get_vts_stream (curlm_t *mhnd) +{ + static int running = 0; + + if (!(*mhnd)) + { + return -1; + } + + CURLMcode mc = curl_multi_perform (*mhnd, &running); + if (!mc && running) + /* wait for activity, timeout or "nothing" */ + mc = curl_multi_poll (*mhnd, NULL, 0, 5000, NULL); + if (mc != CURLM_OK) + { + g_warning ("%s: error on curl_multi_poll(): %d\n", __func__, mc); + return -1; + } + + return running; } /** @@ -496,17 +612,32 @@ openvasd_get_vts (openvasd_connector_t *conn) { GString *path; openvasd_resp_t response = NULL; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; - path = g_string_new ("/vts?information=1"); + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; - response = openvasd_send_request (conn, GET, path->str, NULL, NULL); + init_stringstream (&resp); + path = g_string_new ("/vts?information=1"); + if ((hnd = handler (conn, GET, path->str, NULL, &resp, &err)) == NULL) + { + g_string_free (path, TRUE); + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } g_string_free (path, TRUE); - if (response == NULL) - return NULL; - else if (response->code == RESP_CODE_ERR) - return response; + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + + g_free (resp.ptr); return response; } @@ -524,13 +655,36 @@ openvasd_start_scan (openvasd_connector_t *conn, char *data) openvasd_resp_t response = NULL; cJSON *parser = NULL; GString *path; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; - response = openvasd_send_request (conn, POST, "/scans", data, NULL); - + response = g_malloc0 (sizeof (struct openvasd_response)); if (response == NULL) return NULL; - else if (response->code == RESP_CODE_ERR) - return response; + + init_stringstream (&resp); + if ((hnd = handler (conn, POST, "/scans", data, &resp, &err)) == NULL) + { + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } + + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response == NULL || response->code == RESP_CODE_ERR) + { + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + if (response->body == NULL) + response->body = + g_strdup ("{\"error\": \"Storing scan configuration\"}"); + g_warning ("%s: Error storing scan configuration ", __func__); + return response; + } // Get the Scan ID parser = cJSON_Parse (response->body); @@ -549,7 +703,7 @@ openvasd_start_scan (openvasd_connector_t *conn, char *data) g_warning ("%s: Parsing json string to get the scan ID", __func__); } response->code = RESP_CODE_ERR; - + g_free (resp.ptr); goto cleanup_start_scan; } @@ -568,15 +722,41 @@ openvasd_start_scan (openvasd_connector_t *conn, char *data) response->body = g_strdup ("{\"error\": \"Missing scan ID\"}"); g_string_free (path, TRUE); g_warning ("%s: Missing scan ID", __func__); + g_free (resp.ptr); goto cleanup_start_scan; } - - response = openvasd_send_request (conn, POST, path->str, - "{\"action\": \"start\"}", NULL); + g_free (resp.ptr); + init_stringstream (&resp); + if ((hnd = handler (conn, POST, path->str, "{\"action\": \"start\"}", &resp, + &err)) + == NULL) + { + g_free (resp.ptr); + g_string_free (path, TRUE); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } g_string_free (path, TRUE); + + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response == NULL || response->code == RESP_CODE_ERR) + { + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + if (response->body == NULL) + response->body = g_strdup ("{\"error\": \"Starting the scan.\"}"); + g_warning ("%s: Error starting the scan.", __func__); + return response; + } + cleanup_start_scan: cJSON_Delete (parser); + response->body = g_strdup (resp.ptr); + g_free (resp.ptr); return response; } @@ -585,6 +765,13 @@ openvasd_stop_scan (openvasd_connector_t *conn) { openvasd_resp_t response = NULL; GString *path; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; // Stop the scan path = g_string_new ("/scans"); @@ -602,10 +789,24 @@ openvasd_stop_scan (openvasd_connector_t *conn) return response; } - response = openvasd_send_request (conn, POST, path->str, - "{\"action\": \"stop\"}", NULL); + init_stringstream (&resp); + if ((hnd = + handler (conn, POST, path->str, "{\"action\": \"stop\"}", &resp, &err)) + == NULL) + { + g_string_free (path, TRUE); + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } g_string_free (path, TRUE); + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + + g_free (resp.ptr); return response; } @@ -614,6 +815,13 @@ openvasd_get_scan_results (openvasd_connector_t *conn, long first, long last) { openvasd_resp_t response = NULL; GString *path = NULL; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; path = g_string_new ("/scans"); if ((*conn)->scan_id != NULL && (*conn)->scan_id[0] != '\0') @@ -629,7 +837,6 @@ openvasd_get_scan_results (openvasd_connector_t *conn, long first, long last) } else { - response = g_malloc0 (sizeof (struct openvasd_response)); response->code = RESP_CODE_ERR; response->body = g_strdup ("{\"error\": \"Missing scan ID\"}"); g_string_free (path, TRUE); @@ -637,17 +844,28 @@ openvasd_get_scan_results (openvasd_connector_t *conn, long first, long last) return response; } - response = openvasd_send_request (conn, GET, path->str, NULL, NULL); - if (response->code == RESP_CODE_ERR) + init_stringstream (&resp); + if ((hnd = handler (conn, GET, path->str, NULL, &resp, &err)) == NULL) { - response->body = - g_strdup ("{\"error\": \"Not possible to get scan results\"}"); g_string_free (path, TRUE); - g_warning ("%s: Not possible to get scan results", __func__); + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; return response; } g_string_free (path, TRUE); + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response->code == RESP_CODE_ERR) + { + g_warning ("%s: Not possible to get scan results", __func__); + response->body = + g_strdup ("{\"error\": \"Not possible to get scan results\"}"); + } + g_free (resp.ptr); + return response; } @@ -886,6 +1104,13 @@ openvasd_get_scan_status (openvasd_connector_t *conn) { openvasd_resp_t response = NULL; GString *path = NULL; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; path = g_string_new ("/scans"); if ((*conn)->scan_id != NULL && (*conn)->scan_id[0] != '\0') @@ -896,7 +1121,6 @@ openvasd_get_scan_status (openvasd_connector_t *conn) } else { - response = g_malloc0 (sizeof (struct openvasd_response)); response->code = RESP_CODE_ERR; response->body = g_strdup ("{\"error\": \"Missing scan ID\"}"); g_string_free (path, TRUE); @@ -904,17 +1128,28 @@ openvasd_get_scan_status (openvasd_connector_t *conn) return response; } - response = openvasd_send_request (conn, GET, path->str, NULL, NULL); - if (response->code == RESP_CODE_ERR) + init_stringstream (&resp); + if ((hnd = handler (conn, GET, path->str, NULL, &resp, &err)) == NULL) { - response->body = - g_strdup ("{\"error\": \"Not possible to get scan status\"}"); g_string_free (path, TRUE); - g_warning ("%s: Not possible to get scan status", __func__); + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; return response; } g_string_free (path, TRUE); + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response->code == RESP_CODE_ERR) + { + response->body = + g_strdup ("{\"error\": \"Not possible to get scan status\"}"); + g_warning ("%s: Not possible to get scan status", __func__); + } + + g_free (resp.ptr); return response; } @@ -1059,7 +1294,7 @@ openvasd_parsed_scan_status (openvasd_connector_t *conn) { cJSON *parser = NULL; cJSON *status = NULL; - openvasd_resp_t resp; + openvasd_resp_t resp = NULL; gchar *status_val = NULL; time_t start_time = 0, end_time = 0; int progress = -1; @@ -1114,6 +1349,13 @@ openvasd_delete_scan (openvasd_connector_t *conn) { openvasd_resp_t response = NULL; GString *path; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; // Stop the scan path = g_string_new ("/scans"); @@ -1124,7 +1366,6 @@ openvasd_delete_scan (openvasd_connector_t *conn) } else { - response = g_malloc0 (sizeof (struct openvasd_response)); response->code = RESP_CODE_ERR; response->body = g_strdup ("{\"error\": \"Missing scan ID\"}"); g_string_free (path, TRUE); @@ -1132,35 +1373,170 @@ openvasd_delete_scan (openvasd_connector_t *conn) return response; } - response = openvasd_send_request (conn, DELETE, path->str, NULL, NULL); + init_stringstream (&resp); + if ((hnd = handler (conn, DELETE, path->str, NULL, &resp, &err)) == NULL) + { + g_string_free (path, TRUE); + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } g_string_free (path, TRUE); + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response->code == RESP_CODE_ERR) + { + response->body = + g_strdup ("{\"error\": \"Not possible to delete scan.\"}"); + g_warning ("%s: Not possible to delete scan", __func__); + } + + g_free (resp.ptr); return response; } openvasd_resp_t openvasd_get_health_alive (openvasd_connector_t *conn) { - return openvasd_send_request (conn, GET, "/health/alive", NULL, NULL); + openvasd_resp_t response = NULL; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; + + init_stringstream (&resp); + if ((hnd = handler (conn, GET, "/health/alive", NULL, &resp, &err)) == NULL) + { + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } + + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response->code == RESP_CODE_ERR) + { + response->body = + g_strdup ("{\"error\": \"Not possible to get health information.\"}"); + g_warning ("%s: Not possible to get health information", __func__); + } + + g_free (resp.ptr); + return response; } openvasd_resp_t openvasd_get_health_ready (openvasd_connector_t *conn) { - return openvasd_send_request (conn, GET, "/health/ready", NULL, - "feed-version"); + openvasd_resp_t response = NULL; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; + + init_stringstream (&resp); + if ((hnd = handler (conn, GET, "/health/ready", NULL, &resp, &err)) == NULL) + { + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } + + openvasd_send_request (hnd, "feed-version", response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response->code == RESP_CODE_ERR) + { + response->body = + g_strdup ("{\"error\": \"Not possible to get health information.\"}"); + g_warning ("%s: Not possible to get health information", __func__); + } + + g_free (resp.ptr); + return response; } openvasd_resp_t openvasd_get_health_started (openvasd_connector_t *conn) { - return openvasd_send_request (conn, GET, "/health/started", NULL, NULL); + openvasd_resp_t response = NULL; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; + + init_stringstream (&resp); + if ((hnd = handler (conn, GET, "/health/started", NULL, &resp, &err)) == NULL) + { + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } + + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response->code == RESP_CODE_ERR) + { + response->body = + g_strdup ("{\"error\": \"Not possible to get health information.\"}"); + g_warning ("%s: Not possible to get health information", __func__); + } + + g_free (resp.ptr); + return response; } openvasd_resp_t openvasd_get_scan_preferences (openvasd_connector_t *conn) { - return openvasd_send_request (conn, GET, "/scans/preferences", NULL, NULL); + openvasd_resp_t response = NULL; + char *err = NULL; + CURL *hnd = NULL; + stringstream resp; + + response = g_malloc0 (sizeof (struct openvasd_response)); + if (response == NULL) + return NULL; + + init_stringstream (&resp); + if ((hnd = handler (conn, GET, "/scans/preferences", NULL, &resp, &err)) + == NULL) + { + g_free (resp.ptr); + response->code = RESP_CODE_ERR; + response->body = err; + return response; + } + + openvasd_send_request (hnd, NULL, response); + if (response != NULL && response->code != RESP_CODE_ERR) + response->body = g_strdup (resp.ptr); + else if (response->code == RESP_CODE_ERR) + { + response->body = + g_strdup ("{\"error\": \"Not possible to get scans preferences.\"}"); + g_warning ("%s: Not possible to get scans_preferences", __func__); + } + + g_free (resp.ptr); + return response; } /** @@ -1292,7 +1668,7 @@ openvasd_parsed_scans_preferences (openvasd_connector_t *conn, GSList **params) cJSON *param_obj = NULL; int err = 0; - resp = openvasd_send_request (conn, GET, "/scans/preferences", NULL, NULL); + resp = openvasd_get_scan_preferences (conn); if (resp->code != 200) return -1; diff --git a/openvasd/openvasd.h b/openvasd/openvasd.h index 185b0255..0d6b21f4 100644 --- a/openvasd/openvasd.h +++ b/openvasd/openvasd.h @@ -12,6 +12,7 @@ #define _GVM_OPENVASD_H #include +#include #include /** @brief Struct to hold an scan result */ @@ -269,4 +270,29 @@ openvasd_vt_single_add_value (openvasd_vt_single_t *, const char *, gchar * openvasd_build_scan_config_json (openvasd_target_t *, GHashTable *, GSList *); +/* Curl multiperform wrapper */ + +typedef void *curlm_t; + +curlm_t +curlm_handler_new (void); + +/** @brief Define a string struct for storing the response. + */ +typedef struct string +{ + char *ptr; + size_t len; +} stringstream; + +void +init_stringstream (stringstream *s); + +openvasd_resp_t +openvasd_get_vts_stream_init (openvasd_connector_t *, curlm_t *, + stringstream *); + +int +openvasd_get_vts_stream (curlm_t *); + #endif diff --git a/openvasd/vtparser.c b/openvasd/vtparser.c new file mode 100644 index 00000000..cf1c3503 --- /dev/null +++ b/openvasd/vtparser.c @@ -0,0 +1,417 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/** + * @file + * @brief Simple JSON reader. + */ + +#include "vtparser.h" + +#include "../base/cvss.h" +#include "../util/jsonpull.h" + +#include +#include +#include + +/** + * @brief VT categories + */ +typedef enum +{ + ACT_INIT = 0, + ACT_SCANNER, + ACT_SETTINGS, + ACT_GATHER_INFO, + ACT_ATTACK, + ACT_MIXED_ATTACK, + ACT_DESTRUCTIVE_ATTACK, + ACT_DENIAL, + ACT_KILL_HOST, + ACT_FLOOD, + ACT_END, +} nvt_category; + +/** + * @brief Get the VT category type given the category as string + * + * @param cat The category as string. + * + * @return Integer representing the category type. + */ +static int +get_category_from_name (const char *cat) +{ + if (!g_strcmp0 (cat, "init")) + return ACT_INIT; + else if (!g_strcmp0 (cat, "scanner")) + return ACT_SCANNER; + else if (!g_strcmp0 (cat, "settings")) + return ACT_SETTINGS; + else if (!g_strcmp0 (cat, "gather_info")) + return ACT_GATHER_INFO; + else if (!g_strcmp0 (cat, "attack")) + return ACT_ATTACK; + else if (!g_strcmp0 (cat, "mixed_attack")) + return ACT_MIXED_ATTACK; + else if (!g_strcmp0 (cat, "destructive_attack")) + return ACT_DESTRUCTIVE_ATTACK; + else if (!g_strcmp0 (cat, "denial")) + return ACT_DENIAL; + else if (!g_strcmp0 (cat, "kill_host")) + return ACT_KILL_HOST; + else if (!g_strcmp0 (cat, "flood")) + return ACT_FLOOD; + else if (!g_strcmp0 (cat, "end")) + return ACT_END; + + return -1; +} + +static void +add_tags_to_nvt (nvti_t *nvt, cJSON *tag_obj) +{ + if (cJSON_IsObject (tag_obj)) + { + cJSON *item = NULL; + if ((item = cJSON_GetObjectItem (tag_obj, "affected")) != NULL + && cJSON_IsString (item)) + nvti_set_affected (nvt, item->valuestring); + + if ((item = cJSON_GetObjectItem (tag_obj, "creation_date")) != NULL + && cJSON_IsNumber (item)) + nvti_set_creation_time (nvt, item->valuedouble); + + if ((item = cJSON_GetObjectItem (tag_obj, "last_modification")) != NULL + && cJSON_IsNumber (item)) + nvti_set_modification_time (nvt, item->valuedouble); + + if ((item = cJSON_GetObjectItem (tag_obj, "insight")) != NULL + && cJSON_IsString (item)) + nvti_set_insight (nvt, item->valuestring); + + if ((item = cJSON_GetObjectItem (tag_obj, "impact")) != NULL + && cJSON_IsString (item)) + nvti_set_impact (nvt, item->valuestring); + + if ((item = cJSON_GetObjectItem (tag_obj, "qod")) != NULL + && cJSON_IsString (item)) + nvti_set_qod (nvt, item->valuestring); + + if ((item = cJSON_GetObjectItem (tag_obj, "qod_type")) != NULL + && cJSON_IsString (item)) + nvti_set_qod_type (nvt, item->valuestring); + + if ((item = cJSON_GetObjectItem (tag_obj, "solution")) != NULL + && cJSON_IsString (item)) + { + nvti_set_solution (nvt, item->valuestring); + + if ((item = cJSON_GetObjectItem (tag_obj, "solution_type")) != NULL + && cJSON_IsString (item)) + nvti_set_solution_type (nvt, item->valuestring); + else + g_debug ("%s: SOLUTION: missing type for OID: %s", __func__, + nvti_oid (nvt)); + if ((item = cJSON_GetObjectItem (tag_obj, "solution_method")) != NULL + && cJSON_IsString (item)) + nvti_set_solution_method (nvt, item->valuestring); + } + + if ((item = cJSON_GetObjectItem (tag_obj, "summary")) != NULL + && cJSON_IsString (item)) + nvti_set_summary (nvt, item->valuestring); + + if ((item = cJSON_GetObjectItem (tag_obj, "vuldetect")) != NULL + && cJSON_IsString (item)) + nvti_set_detection (nvt, item->valuestring); + + // Parse severity + char *severity_vector = NULL; + + if ((item = cJSON_GetObjectItem (tag_obj, "severity_vector")) != NULL + && cJSON_IsString (item)) + severity_vector = item->valuestring; + + if (!severity_vector) + { + if ((item = cJSON_GetObjectItem (tag_obj, "cvss_base_vector")) != NULL + && cJSON_IsString (item)) + severity_vector = item->valuestring; + } + + if (severity_vector) + { + char *severity_origin = NULL, *severity_type = NULL; + char *cvss_base; + + time_t severity_date = 0; + double cvss_base_dbl; + + if (g_strrstr (severity_vector, "CVSS:3")) + severity_type = g_strdup ("cvss_base_v3"); + else + severity_type = g_strdup ("cvss_base_v2"); + + cvss_base_dbl = get_cvss_score_from_base_metrics (severity_vector); + + if ((item = cJSON_GetObjectItem (tag_obj, "severity_date")) != NULL + && cJSON_IsNumber (item)) + severity_date = item->valuedouble; + + if ((item = cJSON_GetObjectItem (tag_obj, "severity_origin")) != NULL + && cJSON_IsString (item)) + severity_origin = item->valuestring; + + nvti_add_vtseverity ( + nvt, vtseverity_new (severity_type, severity_origin, severity_date, + cvss_base_dbl, severity_vector)); + + nvti_add_tag (nvt, "cvss_base_vector", severity_vector); + + cvss_base = g_strdup_printf ( + "%.1f", get_cvss_score_from_base_metrics (severity_vector)); + nvti_set_cvss_base (nvt, cvss_base); + + g_free (cvss_base); + g_free (severity_type); + // end parsing severity + } + else + { + g_warning ("%s: SEVERITY missing value element", __func__); + nvti_free (nvt); + nvt = NULL; + } + } // end tag +} + +static void +parse_references (nvti_t *nvt, cJSON *vt_obj) +{ + cJSON *item = NULL; + if ((item = cJSON_GetObjectItem (vt_obj, "references")) != NULL + && cJSON_IsArray (item)) + { + cJSON *ref_obj; + cJSON *ref_item; + cJSON_ArrayForEach (ref_obj, item) + { + char *id, *class; + + if (!cJSON_IsObject (ref_obj)) + { + g_debug ("%s: Error reading VT/REFS reference object", __func__); + continue; + } + + if ((ref_item = cJSON_GetObjectItem (ref_obj, "class")) != NULL + && cJSON_IsString (ref_item)) + { + class = ref_item->valuestring; + if ((ref_item = cJSON_GetObjectItem (ref_obj, "id")) == NULL + && !cJSON_IsString (ref_item)) + { + g_warning ("%s: REF missing ID attribute", __func__); + continue; + } + + id = ref_item->valuestring; + nvti_add_vtref (nvt, vtref_new (class, id, NULL)); + } + else + { + g_warning ("%s: REF missing type attribute", __func__); + continue; + } + } + } // end references +} + +static void +add_preferences_to_nvt (nvti_t *nvt, cJSON *vt_obj) +{ + cJSON *item = NULL; + if ((item = cJSON_GetObjectItem (vt_obj, "preferences")) != NULL) + { + if (!cJSON_IsArray (item)) + g_debug ("%s: Error reading VT/REFS array", __func__); + else + { + cJSON *prefs_obj = NULL; + cJSON *prefs_item = NULL; + + cJSON_ArrayForEach (prefs_obj, item) + { + char *class, *name, *default_val; + int id; + if (!cJSON_IsObject (prefs_obj)) + { + g_debug ("%s: Error reading VT/PREFS preference object", + __func__); + continue; + } + + if ((prefs_item = cJSON_GetObjectItem (prefs_obj, "class")) == NULL + || !cJSON_IsString (prefs_item)) + { + g_warning ("%s: PREF missing type attribute", __func__); + continue; + } + class = prefs_item->valuestring; + + if ((prefs_item = cJSON_GetObjectItem (prefs_obj, "id")) == NULL + || !cJSON_IsNumber (prefs_item)) + { + g_warning ("%s: PREF missing id attribute", __func__); + continue; + } + id = prefs_item->valueint; + + if ((prefs_item = cJSON_GetObjectItem (prefs_obj, "name")) == NULL + || !cJSON_IsString (prefs_item)) + { + g_warning ("%s: PREF missing name attribute", __func__); + continue; + } + name = prefs_item->valuestring; + + if ((prefs_item = cJSON_GetObjectItem (prefs_obj, "default")) + == NULL + || !cJSON_IsString (prefs_item)) + { + g_warning ("%s: PREF missing name attribute", __func__); + continue; + } + default_val = prefs_item->valuestring; + + nvtpref_t *prefs = nvtpref_new (id, name, class, default_val); + nvti_add_pref (nvt, prefs); + nvtpref_free(prefs); + } // end each prefs + } // end prefs array + } // end preferences +} + +/** + * @brief Parse a VT element given in json format. + * + * @param[in] reader JSON reader object pointing to an VT element. + * + * @return nvti structur containing the VT metadata, NULL otherwise. + * The nvti struct must be free() by the caller + */ +nvti_t * +openvasd_parse_vt (gvm_json_pull_parser_t *parser, gvm_json_pull_event_t *event) +{ + nvti_t *nvt = NULL; + cJSON *vt_obj = NULL; + cJSON *item = NULL; + char *error_message = NULL; + + gvm_json_pull_parser_next (parser, event); + + // Handle start/end of json array + char * path = gvm_json_path_to_string (event->path); + if (!g_strcmp0 (path, "$") + && event->type == GVM_JSON_PULL_EVENT_ARRAY_START) + { + gvm_json_pull_parser_next (parser, event); + g_debug ("%s: Start parsing feed", __func__); + } + else if (!g_strcmp0 (path, "$") + && event->type == GVM_JSON_PULL_EVENT_ARRAY_END) + { + g_debug ("%s: Finish parsing feed", __func__); + gvm_json_pull_parser_cleanup (parser); + return NULL; + } + g_free(path); + + // It is an NVT object + if (event->type != GVM_JSON_PULL_EVENT_OBJECT_START) + { + g_message ("%s: Error reading VT object", __func__); + gvm_json_pull_parser_cleanup (parser); + return NULL; + } + + vt_obj = gvm_json_pull_expand_container (parser, &error_message); + if (!cJSON_IsObject (vt_obj)) + { + g_free(error_message); + gvm_json_pull_parser_cleanup (parser); + cJSON_free(vt_obj); + return NULL; + } + + // g_debug("JSON VT: %s", cJSON_Print(vt_obj)); + nvt = nvti_new (); + + if ((item = cJSON_GetObjectItem (vt_obj, "oid")) != NULL + && cJSON_IsString (item)) + nvti_set_oid (nvt, item->valuestring); + else + { + g_warning ("%s: VT missing OID", __func__); + cJSON_free (vt_obj); + g_free(error_message); + nvti_free (nvt); + return NULL; + } + + if ((item = cJSON_GetObjectItem (vt_obj, "name")) != NULL + && cJSON_IsString (item)) + nvti_set_name (nvt, item->valuestring); + else + { + g_warning ("%s: VT missing NAME", __func__); + cJSON_free (vt_obj); + g_free(error_message); + nvti_free (nvt); + return NULL; + } + + if ((item = cJSON_GetObjectItem (vt_obj, "family")) != NULL + && cJSON_IsString (item)) + nvti_set_family (nvt, item->valuestring); + else + { + g_warning ("%s: VT missing FAMILY", __func__); + cJSON_free (vt_obj); + g_free(error_message); + nvti_free (nvt); + return NULL; + } + + if ((item = cJSON_GetObjectItem (vt_obj, "category")) != NULL + && cJSON_IsString (item)) + nvti_set_category (nvt, get_category_from_name (item->valuestring)); + else + { + g_warning ("%s: VT missing CATEGORY", __func__); + cJSON_free (vt_obj); + g_free(error_message); + nvti_free (nvt); + return NULL; + } + + cJSON *tag_obj = cJSON_GetObjectItem (vt_obj, "tag"); + if (tag_obj) + add_tags_to_nvt (nvt, tag_obj); + if (!nvt) + { + cJSON_free (vt_obj); + g_free(error_message); + return NULL; + } + + parse_references (nvt, vt_obj); + add_preferences_to_nvt (nvt, vt_obj); + cJSON_free (vt_obj); + g_free(error_message); + return nvt; +} diff --git a/openvasd/vtparser.h b/openvasd/vtparser.h new file mode 100644 index 00000000..cb5f5ae1 --- /dev/null +++ b/openvasd/vtparser.h @@ -0,0 +1,20 @@ +/* SPDX-FileCopyrightText: 2024 Greenbone AG + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/** + * @file + * @brief Simple JSON reader + */ + +#ifndef _GVM_JSONUTILS_H +#define _GVM_JSONUTILS_H + +#include "../base/nvti.h" +#include "../util/jsonpull.h" + +nvti_t * +openvasd_parse_vt (gvm_json_pull_parser_t *, gvm_json_pull_event_t *); + +#endif diff --git a/util/jsonpull.c b/util/jsonpull.c index 03dddf42..69787175 100644 --- a/util/jsonpull.c +++ b/util/jsonpull.c @@ -754,6 +754,7 @@ gvm_json_pull_parser_next (gvm_json_pull_parser_t *parser, return; } } + event->path = parser->path; // Delayed addition to path after a container start element