diff --git a/.docker/build.Dockerfile b/.docker/build.Dockerfile index a3fcbb304..885201235 100644 --- a/.docker/build.Dockerfile +++ b/.docker/build.Dockerfile @@ -2,7 +2,7 @@ ARG GVM_LIBS_VERSION=oldstable # We want gvm-libs to be ready so we use the build docker image of gvm-libs -FROM greenbone/gvm-libs:$GVM_LIBS_VERSION +FROM registry.community.greenbone.net/community/gvm-libs:${GVM_LIBS_VERSION} # This will make apt-get install without question ARG DEBIAN_FRONTEND=noninteractive diff --git a/.docker/prod.Dockerfile b/.docker/prod.Dockerfile index 204d14036..1872962b8 100644 --- a/.docker/prod.Dockerfile +++ b/.docker/prod.Dockerfile @@ -20,7 +20,7 @@ RUN mkdir /build && \ cmake -DCMAKE_BUILD_TYPE=Release $FEATURE_TOGGLE /source && \ make DESTDIR=/install install -FROM greenbone/gvm-libs:${GVM_LIBS_VERSION} +FROM registry.community.greenbone.net/community/gvm-libs:${GVM_LIBS_VERSION} ARG DEBIAN_FRONTEND=noninteractive diff --git a/.docker/start-gvmd.sh b/.docker/start-gvmd.sh index 7083b8f81..a05ca0824 100644 --- a/.docker/start-gvmd.sh +++ b/.docker/start-gvmd.sh @@ -18,7 +18,7 @@ [ -z "$USER" ] && USER="admin" [ -z "$PASSWORD" ] && PASSWORD="admin" -[ -z "$GVMD_ARGS" ] && GVMD_ARGS="--listen-mode=666" +[ -z "$GVMD_ARGS" ] && GVMD_ARGS="-f --listen-mode=666" [ -z "$GVMD_USER" ] && GVMD_USER="gvmd" [ -z "$PGRES_DATA" ] && PGRES_DATA="/var/lib/postgresql" @@ -49,6 +49,4 @@ gvmd --modify-setting 78eceaec-3385-11ea-b237-28d24461215b --value "$uid" echo "starting gvmd" gvmd $GVMD_ARGS || - (cat /var/log/gvm/gvmd.log && exit 1) - -tail -f /var/log/gvm/gvmd.log + (echo "Starting gvmd failed" && exit 1) \ No newline at end of file diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 470a23bb2..c74217b0c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -64,7 +64,7 @@ jobs: - name: Configure and run tests run: CTEST_OUTPUT_ON_FAILURE=1 cmake --build build -- tests test - name: Upload test coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v5 with: file: build/coverage/coverage.xml token: ${{ secrets.CODECOV_TOKEN }} diff --git a/CMakeLists.txt b/CMakeLists.txt index cf44b15e4..a279ac168 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ cmake_minimum_required (VERSION 3.0) message ("-- Configuring Greenbone Vulnerability Manager...") project (gvm - VERSION 23.7.1 + VERSION 24.0.1 LANGUAGES C) if (POLICY CMP0005) @@ -103,7 +103,7 @@ include (CPack) set (GVMD_DATABASE_VERSION 256) -set (GVMD_SCAP_DATABASE_VERSION 21) +set (GVMD_SCAP_DATABASE_VERSION 22) set (GVMD_CERT_DATABASE_VERSION 8) @@ -163,11 +163,13 @@ if (NOT GVMD_STATE_DIR) set (GVMD_STATE_DIR "${GVM_STATE_DIR}/gvmd") endif (NOT GVMD_STATE_DIR) -if (NOT GVM_LOG_DIR) - set (GVM_LOG_DIR "${LOCALSTATEDIR}/log/gvm") -else (NOT GVM_LOG_DIR) - set (GVM_LOG_DIR "${GVM_LOG_DIR}") -endif (NOT GVM_LOG_DIR) +if (NOT GVMD_LOG_FILE) + if (GVM_LOG_DIR) + set (GVMD_LOG_FILE "${GVM_LOG_DIR}/gvmd.log") + else (GVM_LOG_DIR) + set (GVMD_LOG_FILE "-") + endif (GVM_LOG_DIR) +endif (NOT GVMD_LOG_FILE) set (GVM_SCAP_RES_DIR "${GVM_DATA_DIR}/scap") set (GVM_CERT_RES_DIR "${GVM_DATA_DIR}/cert") @@ -247,8 +249,8 @@ if (NOT CVSS3_RATINGS) endif (NOT CVSS3_RATINGS) add_definitions (-DCVSS3_RATINGS=${CVSS3_RATINGS}) - message ("-- Install prefix: ${CMAKE_INSTALL_PREFIX}") +message ("-- Log file: ${GVMD_LOG_FILE}") ## Version @@ -402,7 +404,7 @@ install (FILES src/alert_methods/vFire/alert DESTINATION ${GVMD_DATA_DIR}/global_alert_methods/159f79a5-fce8-4ec5-aa49-7d17a77739a3/ PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install (CODE "file (MAKE_DIRECTORY \$ENV{DESTDIR}${GVMD_DATA_DIR}/wizards)") +install (CODE "file (MAKE_DIRECTORY \$ENV{DESTDIR}${GVMD_DATA_DIR}/wizards)") install (FILES src/wizards/quick_first_scan.xml src/wizards/get_tasks_deep.xml diff --git a/INSTALL.md b/INSTALL.md index ff9f1b144..a7b36c2bf 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -21,7 +21,7 @@ Prerequisites: Install these prerequisites on Debian GNU/Linux 'Buster' 10: - apt-get install gcc cmake libglib2.0-dev libgnutls28-dev libpq-dev postgresql-server-dev-11 pkg-config libical-dev xsltproc libgpgme-dev + apt-get install gcc cmake libcjson-dev libglib2.0-dev libgnutls28-dev libpq-dev postgresql-server-dev-11 pkg-config libical-dev xsltproc libgpgme-dev Prerequisites for building documentation: * Doxygen diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt index a9c1ce686..5756751d5 100644 --- a/config/CMakeLists.txt +++ b/config/CMakeLists.txt @@ -15,9 +15,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . - if (NOT SYSTEMD_SERVICE_DIR) - set (SYSTEMD_SERVICE_DIR "/lib/systemd/system") + set (SYSTEMD_SERVICE_DIR "${LIBDIR}/systemd/system") endif (NOT SYSTEMD_SERVICE_DIR) if (NOT LOGROTATE_DIR) diff --git a/doc/gvmd.8 b/doc/gvmd.8 index 7972955c6..1460e997e 100644 --- a/doc/gvmd.8 +++ b/doc/gvmd.8 @@ -109,6 +109,9 @@ File mode of the unix socket \fB--listen-owner=\fISTRING\fB\f1 Owner of the unix socket .TP +\fB--max-concurrent-scan-updates=\fINUMBER\fB\f1 +Maximum number of scan updates that can run at the same time. Default: 0 (unlimited). +.TP \fB--max-email-attachment-size=\fINUMBER\fB\f1 Maximum size of alert email attachments, in bytes. .TP @@ -121,9 +124,15 @@ Maximum size of user-defined message text in alert emails, in bytes. \fB--max-ips-per-target=\fINUMBER\fB\f1 Maximum number of IPs per target. .TP +\fB--mem-wait-retries=\fINUMBER\fB\f1 +How often to try waiting for available memory. Default: 30. Each retry will wait for 10 seconds. +.TP \fB-m, --migrate\f1 Migrate the database and exit. .TP +\fB--min-mem-feed-update=\fINUMBER\fB\f1 +Minimum memory in MiB for feed updates. Default: 0. Feed updates are skipped if less physical memory is available. +.TP \fB--modify-scanner=\fISCANNER-UUID\fB\f1 Modify scanner SCANNER-UUID and exit. .TP diff --git a/doc/gvmd.8.xml b/doc/gvmd.8.xml index 7c2165808..49bed2b70 100644 --- a/doc/gvmd.8.xml +++ b/doc/gvmd.8.xml @@ -262,6 +262,15 @@ along with this program. If not, see .

Owner of the unix socket

+ + + + tag text @@ -18060,6 +18357,26 @@ END:VCALENDAR iso_time Scan end time + + compliance_yes + integer + Number of compliance yes results + + + compliance_no + integer + Number of compliance no results + + + compliance_incomplete + integer + Number of compliance incomplete results + + + compliant + compliance_status + Compliance state of the report. Can be yes, no, incomplete or undefined + @@ -18081,7 +18398,7 @@ END:VCALENDAR uuid - format_id + config_id ID of requested report config uuid @@ -18129,6 +18446,17 @@ END:VCALENDAR boolean + + usage_type + Optional usage type to limit the reports to. Affects total count unlike filter + + + scan + audit + + + + @@ -18168,9 +18496,6 @@ END:VCALENDAR term name - filter - host - delta keywords @@ -18183,61 +18508,6 @@ END:VCALENDAR Filter name, if applicable text - - filter - A severity level that is included in the report - - - High - Medium - Low - Log - - - - - host - Single host selected for delta report - - ip - - - ip - IP address of the host - text - - - - delta - Delta states - - text - changed - gone - new - same - - - changed - Whether changed results are included - boolean - - - gone - Whether results that have vanished are included - boolean - - - new - Whether new results are included - boolean - - - same - Whether results that are equal are included - boolean - - keywords Filter broken down into keywords @@ -22907,8 +23177,11 @@ END:VCALENDAR timestamp scan_end + result_count severity + + compliance_count timestamp @@ -22954,6 +23227,32 @@ END:VCALENDAR severity Maximum severity of the report + + compliance_count + Complaince counts. Only for audit tasks + + yes + no + incomplete + undefined + + + yes + integer + + + no + integer + + + incomplete + integer + + + undefined + integer + + @@ -26474,12 +26773,14 @@ END:VCALENDAR name allow_insecure certificate + kdc key login password community auth_algorithm privacy + realm name @@ -26507,6 +26808,11 @@ END:VCALENDAR text + + kdc + text + The Kerberos KDC (key distribution center(s)) + key @@ -26593,6 +26899,11 @@ END:VCALENDAR + + realm + text + The Kerberos realm + diff --git a/src/sql.c b/src/sql.c index 0f08b56e1..33b0de1ca 100644 --- a/src/sql.c +++ b/src/sql.c @@ -150,38 +150,30 @@ sql_quote (const char* string) } /** - * @brief Quotes a string for use in SQL statements, also ASCII escaping it - * if it is not valid UTF-8. + * @brief Quotes a string for use in SQL statements, also ASCII escaping it. * - * @param[in] string String to quote, has to be \\0 terminated. + * The ASCII escaping excludes characters 0x80 - 0xFF for valid UTF-8 strings + * and includes them otherwise. + * + * @param[in] string String to quote, has to be \\0 terminated. + * @param[in] exceptions Optional exceptions for the escaping. * * @return Freshly allocated, quoted string. Free with g_free. */ gchar* -sql_ascii_escape_and_quote (const char* string) +sql_ascii_escape_and_quote (const char* string, const char* exceptions) { + gchar *escaped_string; gchar *quoted_string; assert (string); if (string == NULL) - { - return NULL; - } - else if (g_utf8_validate (string, -1, NULL)) - { - // Quote valid UTF-8 without ASCII escaping - quoted_string = sql_quote (string); - } - else - { - // Assume invalid UTF-8 uses a different, unknown encoding and - // ASCII-escape it. - gchar *escaped_string; - escaped_string = g_strescape (string, ""); - quoted_string = sql_quote (escaped_string); - g_free (escaped_string); - } + return NULL; + + escaped_string = strescape_check_utf8 (string, exceptions); + quoted_string = sql_quote (escaped_string); + g_free (escaped_string); return quoted_string; } @@ -216,7 +208,7 @@ sql_insert (const char *string) * * @return 0 success, 1 gave up (even when retry given), * 2 reserved (lock unavailable), 3 unique constraint violation, - * -1 error. + * 4 deadlock, -1 error. */ int sqlv (int retry, char* sql, va_list args) @@ -282,13 +274,14 @@ sql (char* sql, ...) continue; else if (ret == 4) { - if (deadlock_amount++ > DEADLOCK_THRESHOLD) - { - g_warning("%s: %d deadlocks detected, waiting and retrying %s", __func__, deadlock_amount, sql); - } - gvm_usleep (DEADLOCK_SLEEP); - continue; - } + if (deadlock_amount++ > DEADLOCK_THRESHOLD) + { + g_warning("%s: %d deadlocks detected, waiting and retrying %s", + __func__, deadlock_amount, sql); + } + gvm_usleep (DEADLOCK_SLEEP); + continue; + } else if (ret) abort(); break; @@ -363,6 +356,7 @@ int sql_x (char* sql, va_list args, sql_stmt_t** stmt_return) { int ret; + unsigned int deadlock_amount = 0; assert (stmt_return); @@ -400,6 +394,16 @@ sql_x (char* sql, va_list args, sql_stmt_t** stmt_return) sql_finalize (*stmt_return); continue; } + if (ret == -5) + { + if (deadlock_amount++ > DEADLOCK_THRESHOLD) + { + g_warning("%s: %d deadlocks detected, waiting and retrying %s", + __func__, deadlock_amount, sql); + } + gvm_usleep (DEADLOCK_SLEEP); + continue; + } break; } assert (ret == 1); diff --git a/src/sql.h b/src/sql.h index b0a225eff..08103a959 100644 --- a/src/sql.h +++ b/src/sql.h @@ -80,7 +80,7 @@ gchar * sql_quote (const char *); gchar * -sql_ascii_escape_and_quote (const char *); +sql_ascii_escape_and_quote (const char *, const char *); gchar * sql_insert (const char *); diff --git a/src/sql_pg.c b/src/sql_pg.c index cf8ed82c8..7f87cc1c9 100644 --- a/src/sql_pg.c +++ b/src/sql_pg.c @@ -607,7 +607,7 @@ sql_rollback () * * @param[in] table The table to lock. * @param[in] lock_timeout The lock timeout in milliseconds, 0 for unlimited. - * + * * @return 1 if locked, 0 if failed / timed out. */ int @@ -615,10 +615,10 @@ sql_table_lock_wait (const char *table, int lock_timeout) { int old_lock_timeout = sql_int ("SHOW lock_timeout;"); sql ("SET LOCAL lock_timeout = %d;", lock_timeout); - + // This requires the gvmd functions to be defined first. int ret = sql_int ("SELECT try_exclusive_lock_wait ('%s');", table); - + sql ("SET LOCAL lock_timeout = %d;", old_lock_timeout); return ret; } diff --git a/src/utils.c b/src/utils.c index 9c7ceb7cc..32588497b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -770,6 +770,73 @@ is_uuid (const char *uuid) return 1; } + +/* Strings. */ + +/** + * @brief Escape a string with exceptions for UTF-8 if it is valid UTF-8. + * + * If the string is valid UTF-8, characters in the range 0x80 - 0xFF + * are excluded so non-ASCII UTF-8 characters and any characters in the + * extra_exceptions are not escaped. + * This leaves the characters 0x01 - 0x1F and 0x7F to be escaped if they are + * not in the given extra_exceptions. + * + * For strings that are not valid UTF-8 all characters in the ranges + * + * @param[in] str The string to escape. + * @param[in] extra_exceptions Extra exc. + * + * @return Newly allocated escaped copy of the string. + */ +gchar * +strescape_check_utf8 (const char *str, const char *extra_exceptions) +{ + if (g_utf8_validate (str, -1, NULL)) + return strescape_without_utf8 (str, extra_exceptions); + else + return g_strescape (str, extra_exceptions); +} + +/** + * @brief Escape control characters in a string with exceptions for UTF-8. + * + * Characters in the range 0x80 - 0xFF are excluded so non-ASCII UTF-8 + * characters are not escaped. + * This leaves the characters 0x01 - 0x1F and 0x7F to be escaped if they are + * not in the given extra_exceptions. + * + * @param[in] str The string to escape. + * @param[in] extra_exceptions Extra exceptions to the escaping. + * + * @return Newly allocated escaped copy of the string. + */ +gchar * +strescape_without_utf8 (const char *str, const char *extra_exceptions) +{ + static const char *base_exceptions = + "\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217" + "\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237" + "\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257" + "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277" + "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317" + "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337" + "\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357" + "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377"; + gchar *exceptions = NULL; + gchar *escaped; + + if (extra_exceptions && strcmp (extra_exceptions, "")) + { + exceptions = g_strconcat (base_exceptions, + extra_exceptions ? extra_exceptions : "", + NULL); + } + escaped = g_strescape (str, exceptions ? exceptions : base_exceptions); + g_free (exceptions); + return escaped; +} + /* XML. */ @@ -959,3 +1026,26 @@ wait_for_pid (pid_t pid, const char *context) } } } + +/** + * @brief Get the available physical memory in bytes. + * + * @return The available memory + */ +guint64 +phys_mem_available () +{ + return (unsigned long long)(sysconf(_SC_AVPHYS_PAGES)) + * sysconf(_SC_PAGESIZE); +} + +/** + * @brief Get the total physical memory in bytes. + * + * @return The total memory + */ +guint64 +phys_mem_total () +{ + return sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE); +} diff --git a/src/utils.h b/src/utils.h index 1054dc0ca..10fcf59d8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -88,6 +88,12 @@ lockfile_locked (const gchar *); int is_uuid (const char *); +gchar * +strescape_check_utf8 (const char *, const char *); + +gchar * +strescape_without_utf8 (const char *, const char *); + int parse_xml_file (const gchar *, entity_t *); @@ -103,4 +109,10 @@ fork_with_handlers (); void wait_for_pid (pid_t, const char *); +guint64 +phys_mem_available (); + +guint64 +phys_mem_total (); + #endif /* not _GVMD_UTILS_H */ diff --git a/src/utils_tests.c b/src/utils_tests.c index 2a252a19d..46ff41bb4 100644 --- a/src/utils_tests.c +++ b/src/utils_tests.c @@ -109,6 +109,42 @@ Ensure (utils, gvm_sleep_sleep_for_1) assert_that (timespec_subtract (&end, &start), is_greater_than (NANOSECONDS - 1)); } +Ensure (utils, strescape_check_utf_8_no_exceptions) +{ + const char *utf8_input = "Äöü\n123\\UTF-8\x04"; + const char *utf8_expected = "Äöü\\n123\\\\UTF-8\\004"; + const char *cp850_input = "\x8E\x94\x81\n123\\CP850\x04"; + const char *cp850_expected = "\\216\\224\\201\\n123\\\\CP850\\004"; + + assert_that (g_utf8_validate (utf8_input, -1, NULL), is_true); + gchar *output = strescape_check_utf8 (utf8_input, NULL); + assert_that (output, is_equal_to_string (utf8_expected)); + g_free (output); + + assert_that (g_utf8_validate (cp850_input, -1, NULL), is_false); + output = strescape_check_utf8 (cp850_input, NULL); + assert_that (output, is_equal_to_string (cp850_expected)); + g_free (output); +} + +Ensure (utils, strescape_check_utf_8_with_exceptions) +{ + const char *utf8_input = "Äöü\n123\\UTF-8\x04"; + const char *utf8_expected = "Äöü\n123\\\\UTF-8\\004"; + const char *cp850_input = "\x8E\x94\x81\n123\\CP850\x04"; + const char *cp850_expected = "\\216\\224\\201\n123\\\\CP850\\004"; + + assert_that (g_utf8_validate (utf8_input, -1, NULL), is_true); + gchar *output = strescape_check_utf8 (utf8_input, "\t\n\r"); + assert_that (output, is_equal_to_string (utf8_expected)); + g_free (output); + + assert_that (g_utf8_validate (cp850_input, -1, NULL), is_false); + output = strescape_check_utf8 (cp850_input, "\t\n\r"); + assert_that (output, is_equal_to_string (cp850_expected)); + g_free (output); +} + /* Test suite. */ int @@ -129,6 +165,9 @@ main (int argc, char **argv) add_test_with_context (suite, utils, parse_iso_time_tz_with_fallback_tz); add_test_with_context (suite, utils, parse_iso_time_tz_variants); + add_test_with_context (suite, utils, strescape_check_utf_8_no_exceptions); + add_test_with_context (suite, utils, strescape_check_utf_8_with_exceptions); + if (argc > 1) return run_single_test (suite, argv[1], create_text_reporter ());