/* Copyright (C) 2009-2021 Greenbone Networks GmbH
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
/**
* @file manage_sql.c
* @brief The Greenbone Vulnerability Manager management library.
*/
/**
* @brief Enable extra GNU functions.
*/
#define _GNU_SOURCE
#include "manage_sql.h"
#include "manage_port_lists.h"
#include "manage_report_formats.h"
#include "manage_sql_secinfo.h"
#include "manage_sql_nvts.h"
#include "manage_tickets.h"
#include "manage_sql_configs.h"
#include "manage_sql_port_lists.h"
#include "manage_sql_report_formats.h"
#include "manage_sql_tickets.h"
#include "manage_sql_tls_certificates.h"
#include "manage_acl.h"
#include "manage_authentication.h"
#include "lsc_user.h"
#include "sql.h"
#include "utils.h"
/* TODO This is for buffer_get_filter_xml, for print_report_xml_start. We
* should not be generating XML in here, that should be done in gmp_*.c. */
#include "gmp_get.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#undef G_LOG_DOMAIN
/**
* @brief GLib log domain.
*/
#define G_LOG_DOMAIN "md manage"
/**
* @brief Number of retries for
* LOCK TABLE .. IN ACCESS EXLUSIVE MODE NOWAIT
* statements.
*/
#define LOCK_RETRIES 16
/**
* @brief Time of delay between two lock retries.
*/
#define LOCK_RETRY_DELAY 2
#ifdef DEBUG_FUNCTION_NAMES
#include
void __cyg_profile_func_enter (void *, void *)
__attribute__((no_instrument_function));
void
__cyg_profile_func_enter (void *func, void *caller)
{
Dl_info info;
if (dladdr (func, &info))
g_debug ("TTT: enter %p %s",
(int*) func,
info.dli_sname ? info.dli_sname : "?");
else
g_debug ("TTT: enter %p", (int*) func);
}
void __cyg_profile_func_exit (void *, void *)
__attribute__((no_instrument_function));
void
__cyg_profile_func_exit (void *func, void *caller)
{
Dl_info info;
if (dladdr (func, &info))
g_debug ("TTT: exit %p %s",
(int*) func,
info.dli_sname ? info.dli_sname : "?");
else
g_debug ("TTT: exit %p", (int*) func);
}
#endif
/* Headers from backend specific manage_xxx.c file. */
int
manage_create_sql_functions ();
void
create_tables ();
void
check_db_sequences ();
int
check_db_extensions ();
static int
check_db_encryption_key ();
void
manage_attach_databases ();
/* Headers for symbols defined in manage.c which are private to libmanage. */
/**
* @brief Flag to force authentication to succeed.
*
* 1 if set via scheduler, 2 if set via event, else 0.
*/
extern int authenticate_allow_all;
const char *threat_message_type (const char *);
int delete_reports (task_t);
int
stop_task_internal (task_t);
int
validate_username (const gchar *);
void
set_task_interrupted (task_t, const gchar *);
/* Static headers. */
static int
report_counts_cache_exists (report_t, int, int);
static void report_severity_data (report_t, const char *, const get_data_t *,
severity_data_t*, severity_data_t*);
static int cache_report_counts (report_t, int, int, severity_data_t*);
static char*
task_owner_uuid (task_t);
gchar*
clean_hosts (const char *, int*);
static gboolean
find_user_by_name (const char *, user_t *user);
static gboolean
find_role_with_permission (const char *, role_t *, const char *);
static int
user_ensure_in_db (const gchar *, const gchar *);
static int
set_password (const gchar *, const gchar *, const gchar *, gchar **);
static void
permissions_set_subjects (const char *, resource_t, resource_t, int);
static resource_t
permission_resource (permission_t);
static resource_t
permission_subject (permission_t);
static char *
permission_subject_type (permission_t);
static int
role_is_predefined (role_t);
static int
role_is_predefined_id (const char *);
static int
task_second_last_report (task_t, report_t *);
static gchar *
new_secinfo_message (event_t, const void*, alert_t);
static gchar *
new_secinfo_list (event_t, const void*, alert_t, int*);
static void
check_for_new_scap ();
static void
check_for_new_cert ();
static void
check_for_updated_scap ();
static void
check_for_updated_cert ();
static int
report_counts_id_full (report_t, int *, int *, int *, int *, int *,
double *, const get_data_t*, const char* ,
int *, int *, int *, int *, int *, double *);
static gboolean
find_group_with_permission (const char *, group_t *, const char *);
static gchar*
vulns_extra_where ();
static int
task_last_report_any_status (task_t, report_t *);
static int
task_report_previous (task_t task, report_t, report_t *);
static gboolean
find_trash_task (const char*, task_t*);
static gboolean
find_trash_report_with_permission (const char *, report_t *, const char *);
static int
cleanup_schedule_times ();
static char *
permission_name (permission_t);
static void
cache_permissions_for_resource (const char *, resource_t, GArray*);
static void
cache_all_permissions_for_users (GArray*);
static void
report_cache_counts (report_t, int, int, const char*);
static int
report_host_dead (report_host_t);
static int
report_host_result_count (report_host_t);
static int
set_credential_data (credential_t, const char*, const char*);
static void
set_credential_name (credential_t, const char *);
static void
set_credential_comment (credential_t, const char *);
static void
set_credential_login (credential_t, const char *);
static void
set_credential_certificate (credential_t, const char *);
static void
set_credential_auth_algorithm (credential_t, const char *);
static void
set_credential_private_key (credential_t, const char *, const char *);
static void
set_credential_password (credential_t, const char *);
static void
set_credential_snmp_secret (credential_t, const char *, const char *,
const char *);
static int
setting_value_int (const char *, int *);
static int
setting_auto_cache_rebuild_int ();
static double
setting_default_severity_dbl ();
static int
setting_dynamic_severity_int ();
static char *
setting_timezone ();
static double
task_severity_double (task_t, int, int, int);
static char*
target_comment (target_t);
static column_t *
type_select_columns (const char *type);
static column_t *
type_where_columns (const char *type);
static char*
trash_filter_uuid (filter_t);
static char*
trash_filter_name (filter_t);
static char*
trash_target_comment (target_t);
static int
user_resources_in_use (user_t,
const char *, int(*)(resource_t),
const char *, int(*)(resource_t));
static const char**
type_filter_columns (const char *);
static int
type_build_select (const char *, const char *, const get_data_t *,
gboolean, gboolean, const char *, const char *,
const char *, gchar **);
/* Variables. */
/**
* @brief Function to fork a connection that will accept GMP requests.
*/
static manage_connection_forker_t manage_fork_connection;
/**
* @brief Max number of hosts per target.
*/
static int max_hosts = MANAGE_MAX_HOSTS;
/**
* @brief Default max number of bytes of reports included in email alerts.
*/
#define MAX_CONTENT_LENGTH 20000
/**
* @brief Maximum number of bytes of reports included in email alerts.
*
* A value less or equal to 0 allows any size.
*/
static int max_content_length = MAX_CONTENT_LENGTH;
/**
* @brief Default max number of bytes of reports attached to email alerts.
*/
#define MAX_ATTACH_LENGTH 1048576
/**
* @brief Maximum number of bytes of reports attached to email alerts.
*
* A value less or equal to 0 allows any size.
*/
static int max_attach_length = MAX_ATTACH_LENGTH;
/**
* @brief Default max number of bytes of user-defined message in email alerts.
*/
#define MAX_EMAIL_MESSAGE_LENGTH 2000
/**
* @brief Maximum number of bytes of user-defined message text in email alerts.
*
* A value less or equal to 0 allows any size.
*/
static int max_email_message_length = MAX_EMAIL_MESSAGE_LENGTH;
/**
* @brief Memory cache of NVT information from the database.
*/
static nvtis_t* nvti_cache = NULL;
/**
* @brief Name of the database file.
*/
db_conn_info_t gvmd_db_conn_info = { NULL, NULL, NULL };
/**
* @brief Whether a transaction has been opened and not committed yet.
*/
static gboolean in_transaction;
/**
* @brief Time of reception of the currently processed message.
*/
static struct timeval last_msg;
/**
* @brief The VT verification collation override
*/
static gchar *vt_verification_collation = NULL;
/* GMP commands. */
/**
* @brief The GMP command list.
*/
command_t gmp_commands[]
= {{"AUTHENTICATE", "Authenticate with the manager." },
{"CREATE_ALERT", "Create an alert."},
{"CREATE_ASSET", "Create an asset."},
{"CREATE_CONFIG", "Create a config."},
{"CREATE_CREDENTIAL", "Create a credential."},
{"CREATE_FILTER", "Create a filter."},
{"CREATE_GROUP", "Create a group."},
{"CREATE_NOTE", "Create a note."},
{"CREATE_OVERRIDE", "Create an override."},
{"CREATE_PERMISSION", "Create a permission."},
{"CREATE_PORT_LIST", "Create a port list."},
{"CREATE_PORT_RANGE", "Create a port range in a port list."},
{"CREATE_REPORT", "Create a report."},
{"CREATE_REPORT_FORMAT", "Create a report format."},
{"CREATE_ROLE", "Create a role."},
{"CREATE_SCANNER", "Create a scanner."},
{"CREATE_SCHEDULE", "Create a schedule."},
{"CREATE_TAG", "Create a tag."},
{"CREATE_TARGET", "Create a target."},
{"CREATE_TASK", "Create a task."},
{"CREATE_TICKET", "Create a ticket."},
{"CREATE_TLS_CERTIFICATE", "Create a TLS certificate."},
{"CREATE_USER", "Create a new user."},
{"DELETE_ALERT", "Delete an alert."},
{"DELETE_ASSET", "Delete an asset."},
{"DELETE_CONFIG", "Delete a config."},
{"DELETE_CREDENTIAL", "Delete a credential."},
{"DELETE_FILTER", "Delete a filter."},
{"DELETE_GROUP", "Delete a group."},
{"DELETE_NOTE", "Delete a note."},
{"DELETE_OVERRIDE", "Delete an override."},
{"DELETE_PERMISSION", "Delete a permission."},
{"DELETE_PORT_LIST", "Delete a port list."},
{"DELETE_PORT_RANGE", "Delete a port range."},
{"DELETE_REPORT", "Delete a report."},
{"DELETE_REPORT_FORMAT", "Delete a report format."},
{"DELETE_ROLE", "Delete a role."},
{"DELETE_SCANNER", "Delete a scanner."},
{"DELETE_SCHEDULE", "Delete a schedule."},
{"DELETE_TAG", "Delete a tag."},
{"DELETE_TARGET", "Delete a target."},
{"DELETE_TASK", "Delete a task."},
{"DELETE_TICKET", "Delete a ticket."},
{"DELETE_TLS_CERTIFICATE", "Delete a TLS certificate."},
{"DELETE_USER", "Delete an existing user."},
{"DESCRIBE_AUTH", "Get details about the used authentication methods."},
{"EMPTY_TRASHCAN", "Empty the trashcan."},
{"GET_AGGREGATES", "Get aggregates of resources."},
{"GET_ALERTS", "Get all alerts."},
{"GET_ASSETS", "Get all assets."},
{"GET_CONFIGS", "Get all configs."},
{"GET_CREDENTIALS", "Get all credentials."},
{"GET_FEEDS", "Get details of one or all feeds this Manager uses."},
{"GET_FILTERS", "Get all filters."},
{"GET_GROUPS", "Get all groups."},
{"GET_INFO", "Get raw information for a given item."},
{"GET_NOTES", "Get all notes."},
{"GET_NVTS", "Get one or all available NVTs."},
{"GET_NVT_FAMILIES", "Get a list of all NVT families."},
{"GET_OVERRIDES", "Get all overrides."},
{"GET_PERMISSIONS", "Get all permissions."},
{"GET_PORT_LISTS", "Get all port lists."},
{"GET_PREFERENCES", "Get preferences for all available NVTs."},
{"GET_REPORTS", "Get all reports."},
{"GET_REPORT_FORMATS", "Get all report formats."},
{"GET_RESULTS", "Get results."},
{"GET_ROLES", "Get all roles."},
{"GET_SCANNERS", "Get all scanners."},
{"GET_SCHEDULES", "Get all schedules."},
{"GET_SETTINGS", "Get all settings."},
{"GET_SYSTEM_REPORTS", "Get all system reports."},
{"GET_TAGS", "Get all tags."},
{"GET_TARGETS", "Get all targets."},
{"GET_TASKS", "Get all tasks."},
{"GET_TICKETS", "Get all tickets."},
{"GET_TLS_CERTIFICATES", "Get all TLS certificates."},
{"GET_USERS", "Get all users."},
{"GET_VERSION", "Get the Greenbone Management Protocol version."},
{"GET_VULNS", "Get all vulnerabilities."},
{"HELP", "Get this help text."},
{"MODIFY_ALERT", "Modify an existing alert."},
{"MODIFY_ASSET", "Modify an existing asset."},
{"MODIFY_AUTH", "Modify the authentication methods."},
{"MODIFY_CONFIG", "Update an existing config."},
{"MODIFY_CREDENTIAL", "Modify an existing credential."},
{"MODIFY_FILTER", "Modify an existing filter."},
{"MODIFY_GROUP", "Modify an existing group."},
{"MODIFY_NOTE", "Modify an existing note."},
{"MODIFY_OVERRIDE", "Modify an existing override."},
{"MODIFY_PERMISSION", "Modify an existing permission."},
{"MODIFY_PORT_LIST", "Modify an existing port list."},
{"MODIFY_REPORT_FORMAT", "Modify an existing report format."},
{"MODIFY_ROLE", "Modify an existing role."},
{"MODIFY_SCANNER", "Modify an existing scanner."},
{"MODIFY_SCHEDULE", "Modify an existing schedule."},
{"MODIFY_SETTING", "Modify an existing setting."},
{"MODIFY_TAG", "Modify an existing tag."},
{"MODIFY_TARGET", "Modify an existing target."},
{"MODIFY_TASK", "Update an existing task."},
{"MODIFY_TICKET", "Modify an existing ticket."},
{"MODIFY_TLS_CERTIFICATE", "Modify an existing TLS certificate."},
{"MODIFY_USER", "Modify a user."},
{"MOVE_TASK", "Assign task to another slave scanner, even while running."},
{"RESTORE", "Restore a resource."},
{"RESUME_TASK", "Resume a stopped task."},
{"RUN_WIZARD", "Run a wizard."},
{"START_TASK", "Manually start an existing task."},
{"STOP_TASK", "Stop a running task."},
{"SYNC_CONFIG", "Synchronize a config with a scanner."},
{"TEST_ALERT", "Run an alert."},
{"VERIFY_REPORT_FORMAT", "Verify a report format."},
{"VERIFY_SCANNER", "Verify a scanner."},
{NULL, NULL}};
/**
* @brief Check whether a command name is valid.
*
* @param[in] name Command name.
*
* @return 1 yes, 0 no.
*/
int
valid_gmp_command (const char* name)
{
command_t *command;
command = gmp_commands;
while (command[0].name)
if (strcasecmp (command[0].name, name) == 0)
return 1;
else
command++;
return 0;
}
/**
* @brief Get the type associated with a GMP command.
*
* @param[in] name Command name.
*
* @return Freshly allocated type name if any, else NULL.
*/
static gchar *
gmp_command_type (const char* name)
{
const char *under;
under = strchr (name, '_');
if (under && (strlen (under) > 1))
{
gchar *command;
under++;
command = g_strdup (under);
if (command[strlen (command) - 1] == 's')
command[strlen (command) - 1] = '\0';
if (valid_type (command))
return command;
g_free (command);
}
return NULL;
}
/**
* @brief Check whether a GMP command takes a resource.
*
* MODIFY_TARGET, for example, takes a target.
*
* @param[in] name Command name.
*
* @return 1 if takes resource, else 0.
*/
static int
gmp_command_takes_resource (const char* name)
{
assert (name);
return strcasecmp (name, "AUTHENTICATE")
&& strcasestr (name, "CREATE_") != name
&& strcasestr (name, "DESCRIBE_") != name
&& strcasecmp (name, "EMPTY_TRASHCAN")
&& strcasecmp (name, "GET_VERSION")
&& strcasecmp (name, "HELP")
&& strcasecmp (name, "RUN_WIZARD")
&& strcasestr (name, "SYNC_") != name;
}
/* General helpers. */
/**
* @brief Check if a resource with a certain name exists already.
*
* Conflicting resource can be global or owned by the current user.
*
* @param[in] name Name of resource to check for.
* @param[in] type Type of resource.
* @param[in] resource Resource to ignore, 0 otherwise.
*
* @return Whether resource with name exists.
*/
gboolean
resource_with_name_exists (const char *name, const char *type,
resource_t resource)
{
int ret;
char *quoted_name, *quoted_type;
assert (type);
if (!name)
return 0;
quoted_name = sql_quote (name);
quoted_type = sql_quote (type);
if (resource)
ret = sql_int ("SELECT COUNT(*) FROM %ss"
" WHERE name = '%s'"
" AND id != %llu"
" AND " ACL_USER_OWNS () ";",
quoted_type, quoted_name, resource,
current_credentials.uuid);
else
ret = sql_int ("SELECT COUNT(*) FROM %ss"
" WHERE name = '%s'"
" AND " ACL_USER_OWNS () ";",
quoted_type, quoted_name, current_credentials.uuid);
g_free (quoted_name);
g_free (quoted_type);
return !!ret;
}
/**
* @brief Check if a resource with a certain name exists already.
*
* Conflicting resource can be owned by anybody.
*
* @param[in] name Name of resource to check for.
* @param[in] type Type of resource.
* @param[in] resource Resource to ignore, 0 otherwise.
*
* @return Whether resource with name exists.
*/
static gboolean
resource_with_name_exists_global (const char *name, const char *type,
resource_t resource)
{
int ret;
char *quoted_name, *quoted_type;
assert (type);
if (!name)
return 0;
quoted_name = sql_quote (name);
quoted_type = sql_quote (type);
if (resource)
ret = sql_int ("SELECT COUNT(*) FROM %ss"
" WHERE name = '%s'"
" AND id != %llu;",
quoted_type, quoted_name, resource);
else
ret = sql_int ("SELECT COUNT(*) FROM %ss"
" WHERE name = '%s';",
quoted_type, quoted_name);
g_free (quoted_name);
g_free (quoted_type);
return !!ret;
}
/**
* @brief Ensure a string is in an array.
*
* @param[in] array Array.
* @param[in] string String. Copied into array.
*/
static void
array_add_new_string (array_t *array, const gchar *string)
{
guint index;
for (index = 0; index < array->len; index++)
if (strcmp (g_ptr_array_index (array, index), string) == 0)
return;
array_add (array, g_strdup (string));
}
/**
* @brief Find a resource in the trashcan given a UUID.
*
* @param[in] type Type of resource.
* @param[in] uuid UUID of resource.
* @param[out] resource Resource return, 0 if successfully failed to find
* resource.
*
* @return FALSE on success (including if failed to find resource), TRUE on
* error.
*/
gboolean
find_trash (const char *type, const char *uuid, resource_t *resource)
{
gchar *quoted_uuid;
if (!uuid)
return FALSE;
quoted_uuid = sql_quote (uuid);
if (acl_user_owns_trash_uuid (type, quoted_uuid) == 0)
{
g_free (quoted_uuid);
*resource = 0;
return FALSE;
}
switch (sql_int64 (resource,
"SELECT id FROM %ss_trash WHERE uuid = '%s';",
type,
quoted_uuid))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*resource = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_uuid);
return TRUE;
break;
}
g_free (quoted_uuid);
return FALSE;
}
/**
* @brief Convert an ISO time into seconds since epoch.
*
* If no offset is specified, the timezone of the current user is used.
* If there is no current user timezone, UTC is used.
*
* @param[in] text_time Time as text in ISO format: 2011-11-03T09:23:28+02:00.
*
* @return Time since epoch. 0 on error.
*/
int
parse_iso_time (const char *text_time)
{
return parse_iso_time_tz (text_time, current_credentials.timezone);
}
/**
* @brief Find a string in an array.
*
* @param[in] array Array.
* @param[in] string String.
*
* @return The string from the array if found, else NULL.
*/
static gchar*
array_find_string (array_t *array, const gchar *string)
{
guint index;
for (index = 0; index < array->len; index++)
{
gchar *ele;
ele = (gchar*) g_ptr_array_index (array, index);
if (ele && (strcmp (ele, string) == 0))
return ele;
}
return NULL;
}
/**
* @brief Find a string in a glib style string vector.
*
* @param[in] vector Vector.
* @param[in] string String.
*
* @return The string from the vector if found, else NULL.
*/
static const gchar*
vector_find_string (const gchar **vector, const gchar *string)
{
if (vector == NULL)
return NULL;
while (*vector)
if (strcmp (*vector, string) == 0)
return *vector;
else
vector++;
return NULL;
}
/**
* @brief Find a filter string in a glib style string vector.
*
* @param[in] vector Vector.
* @param[in] string String.
*
* @return 1 if found, 2 if found with underscore prefix, else NULL.
*/
static int
vector_find_filter (const gchar **vector, const gchar *string)
{
gchar *underscore;
if (vector_find_string (vector, string))
return 1;
underscore = g_strdup_printf ("_%s", string);
if (vector_find_string (vector, underscore))
{
g_free (underscore);
return 2;
}
g_free (underscore);
return 0;
}
/**
* @brief Get last time NVT alerts were checked.
*
* @return Last check time.
*/
static int
nvts_check_time ()
{
return sql_int ("SELECT"
" CASE WHEN EXISTS (SELECT * FROM meta"
" WHERE name = 'nvts_check_time')"
" THEN CAST ((SELECT value FROM meta"
" WHERE name = 'nvts_check_time')"
" AS INTEGER)"
" ELSE 0"
" END;");
}
/**
* @brief Get last time SCAP SecInfo alerts were checked.
*
* @return Last time SCAP was checked.
*/
static int
scap_check_time ()
{
return sql_int ("SELECT"
" CASE WHEN EXISTS (SELECT * FROM meta"
" WHERE name = 'scap_check_time')"
" THEN CAST ((SELECT value FROM meta"
" WHERE name = 'scap_check_time')"
" AS INTEGER)"
" ELSE 0"
" END;");
}
/**
* @brief Get last time CERT SecInfo alerts were checked.
*
* @return Last time CERT was checked.
*/
static int
cert_check_time ()
{
return sql_int ("SELECT"
" CASE WHEN EXISTS (SELECT * FROM meta"
" WHERE name = 'cert_check_time')"
" THEN CAST ((SELECT value FROM meta"
" WHERE name = 'cert_check_time')"
" AS INTEGER)"
" ELSE 0"
" END;");
}
/**
* @brief Setup for an option process.
*
* @param[in] log_config Log configuration.
* @param[in] database Database.
*
* @return 0 success, -1 error, -2 database is wrong version,
* -3 database needs to be initialised from server.
*/
int
manage_option_setup (GSList *log_config, const db_conn_info_t *database)
{
int ret;
if (gvm_auth_init ())
{
fprintf (stderr, "Authentication init failed\n");
return -1;
}
ret = init_manage_helper (log_config, database,
MANAGE_ABSOLUTE_MAX_IPS_PER_TARGET);
assert (ret != -4);
switch (ret)
{
case 0:
break;
case -2:
fprintf (stderr, "Database is wrong version.\n");
return ret;
case -3:
fprintf (stderr, "Database must be initialised.\n");
return ret;
default:
fprintf (stderr, "Internal error.\n");
return ret;
}
init_manage_process (database);
return 0;
}
/**
* @brief Cleanup for an option process.
*/
void
manage_option_cleanup ()
{
cleanup_manage_process (TRUE);
}
/**
* @brief Copy an array of columns.
*
* @param[in] columns Columns.
*
* @return Freshly allocated array.
*/
static column_t *
column_array_copy (column_t *columns)
{
column_t *point, *start;
int count;
count = 1;
point = columns;
while (point->select)
{
point++;
count++;
}
g_debug ("%s: %i", __func__, count);
start = g_malloc0 (sizeof (column_t) * count);
point = start;
while (columns->select)
{
point->select = g_strdup (columns->select);
point->filter = columns->filter ? g_strdup (columns->filter) : NULL;
point->type = columns->type;
point++;
columns++;
}
return start;
}
/**
* @brief Free an array of columns.
*
* @param[in] columns Columns.
*/
static void
column_array_free (column_t *columns)
{
while (columns->filter)
{
g_free (columns->select);
g_free (columns->filter);
columns++;
}
g_free (columns);
}
/**
* @brief Set the select clause of a column in an array of columns.
*
* Frees the existing select clause.
*
* @param[in] columns Columns.
* @param[in] filter Filter term name.
* @param[in] select Select clause.
*/
static void
column_array_set (column_t *columns, const gchar *filter, gchar *select)
{
while (columns->select)
{
if (columns->filter && (strcmp (columns->filter, filter) == 0))
{
g_free (columns->select);
columns->select = select;
break;
}
columns++;
}
}
/* Filter utilities. */
/**
* @brief Get the symbol of a keyword relation.
*
* @param[in] relation Relation.
*
* @return Relation symbol.
*/
const char *
keyword_relation_symbol (keyword_relation_t relation)
{
switch (relation)
{
case KEYWORD_RELATION_APPROX: return "~";
case KEYWORD_RELATION_COLUMN_ABOVE: return ">";
case KEYWORD_RELATION_COLUMN_APPROX: return "~";
case KEYWORD_RELATION_COLUMN_EQUAL: return "=";
case KEYWORD_RELATION_COLUMN_BELOW: return "<";
case KEYWORD_RELATION_COLUMN_REGEXP: return ":";
default: return "";
}
}
/**
* @brief Free a keyword.
*
* @param[in] keyword Filter keyword.
*/
static void
keyword_free (keyword_t* keyword)
{
g_free (keyword->string);
g_free (keyword->column);
}
/**
* @brief Get whether a keyword is special (like "and").
*
* @param[in] keyword Keyword.
*
* @return 1 if special, else 0.
*/
int
keyword_special (keyword_t *keyword)
{
if (keyword->string)
return (strcmp (keyword->string, "and") == 0)
|| (strcmp (keyword->string, "or") == 0)
|| (strcmp (keyword->string, "not") == 0)
|| (strcmp (keyword->string, "re") == 0)
|| (strcmp (keyword->string, "regexp") == 0);
return 0;
}
/**
* @brief Parse a filter column relation.
*
* @param[in] relation Filter relation.
*
* @return keyword relation
*/
static keyword_relation_t
parse_column_relation (const char relation)
{
switch (relation)
{
case '=': return KEYWORD_RELATION_COLUMN_EQUAL;
case '~': return KEYWORD_RELATION_COLUMN_APPROX;
case '>': return KEYWORD_RELATION_COLUMN_ABOVE;
case '<': return KEYWORD_RELATION_COLUMN_BELOW;
case ':': return KEYWORD_RELATION_COLUMN_REGEXP;
default: return KEYWORD_RELATION_COLUMN_APPROX;
}
}
/**
* @brief Parse a filter keyword.
*
* @param[in] keyword Filter keyword.
*/
static void
parse_keyword (keyword_t* keyword)
{
gchar *string;
int digits;
if (keyword->column == NULL && keyword->equal == 0)
{
keyword->relation = KEYWORD_RELATION_APPROX;
keyword->type = KEYWORD_TYPE_STRING;
return;
}
/* Special values to substitute */
if (keyword->column
&& (strcasecmp (keyword->column, "severity") == 0
|| strcasecmp (keyword->column, "new_severity") == 0))
{
if (strcasecmp (keyword->string, "Log") == 0)
{
keyword->double_value = SEVERITY_LOG;
keyword->type = KEYWORD_TYPE_DOUBLE;
return;
}
if (strcasecmp (keyword->string, "False Positive") == 0)
{
keyword->double_value = SEVERITY_FP;
keyword->type = KEYWORD_TYPE_DOUBLE;
return;
}
else if (strcasecmp (keyword->string, "Error") == 0)
{
keyword->double_value = SEVERITY_ERROR;
keyword->type = KEYWORD_TYPE_DOUBLE;
return;
}
}
/* The type. */
string = keyword->string;
if (*string == '\0')
{
keyword->type = KEYWORD_TYPE_STRING;
return;
}
if (*string && *string == '-' && strlen (string) > 1) string++;
digits = 0;
while (*string && isdigit (*string))
{
digits = 1;
string++;
}
if (digits == 0)
keyword->type = KEYWORD_TYPE_STRING;
else if (*string)
{
struct tm date;
gchar next;
int parsed_integer;
double parsed_double;
char dummy[2];
memset (&date, 0, sizeof (date));
next = *(string + 1);
if (next == '\0' && *string == 's')
{
time_t now;
now = time (NULL);
keyword->integer_value = now + atoi (keyword->string);
keyword->type = KEYWORD_TYPE_INTEGER;
}
else if (next == '\0' && *string == 'm')
{
time_t now;
now = time (NULL);
keyword->integer_value = now + (atoi (keyword->string) * 60);
keyword->type = KEYWORD_TYPE_INTEGER;
}
else if (next == '\0' && *string == 'h')
{
time_t now;
now = time (NULL);
keyword->integer_value = now + (atoi (keyword->string) * 3600);
keyword->type = KEYWORD_TYPE_INTEGER;
}
else if (next == '\0' && *string == 'd')
{
time_t now;
now = time (NULL);
keyword->integer_value = now + (atoi (keyword->string) * 86400);
keyword->type = KEYWORD_TYPE_INTEGER;
}
else if (next == '\0' && *string == 'w')
{
time_t now;
now = time (NULL);
keyword->integer_value = now + atoi (keyword->string) * 604800;
keyword->type = KEYWORD_TYPE_INTEGER;
}
else if (next == '\0' && *string == 'M')
{
time_t now;
now = time (NULL);
keyword->integer_value = add_months (now, atoi (keyword->string));
keyword->type = KEYWORD_TYPE_INTEGER;
}
else if (next == '\0' && *string == 'y')
{
time_t now;
now = time (NULL);
keyword->integer_value = add_months (now,
atoi (keyword->string) * 12);
keyword->type = KEYWORD_TYPE_INTEGER;
}
// Add cases for t%H:%M although it is incorrect sometimes it is easier
// to call filter.lower on the frontend then it can happen that the
// T indicator is lowered as well.
else if (strptime (keyword->string, "%Y-%m-%dt%H:%M", &date))
{
keyword->integer_value = mktime (&date);
keyword->type = KEYWORD_TYPE_INTEGER;
g_debug ("Parsed Y-m-dtH:M %s to timestamp %d.",
keyword->string, keyword->integer_value);
}
else if (strptime (keyword->string, "%Y-%m-%dt%Hh%M", &date))
{
keyword->integer_value = mktime (&date);
keyword->type = KEYWORD_TYPE_INTEGER;
g_debug ("Parsed Y-m-dtHhM %s to timestamp %d.",
keyword->string, keyword->integer_value);
}
else if (strptime (keyword->string, "%Y-%m-%dT%H:%M", &date))
{
keyword->integer_value = mktime (&date);
keyword->type = KEYWORD_TYPE_INTEGER;
g_debug ("Parsed Y-m-dTH:M %s to timestamp %d.",
keyword->string, keyword->integer_value);
}
// Add T%Hh%M for downwards compatible filter
else if (strptime (keyword->string, "%Y-%m-%dT%Hh%M", &date))
{
keyword->integer_value = mktime (&date);
keyword->type = KEYWORD_TYPE_INTEGER;
g_debug ("Parsed Y-m-dTHhM %s to timestamp %d.",
keyword->string, keyword->integer_value);
}
else if (memset (&date, 0, sizeof (date)),
strptime (keyword->string, "%Y-%m-%d", &date))
{
keyword->integer_value = mktime (&date);
keyword->type = KEYWORD_TYPE_INTEGER;
g_debug ("Parsed Y-m-d %s to timestamp %d.",
keyword->string, keyword->integer_value);
}
else if (sscanf (keyword->string, "%d%1s", &parsed_integer, dummy) == 1)
{
keyword->integer_value = parsed_integer;
keyword->type = KEYWORD_TYPE_INTEGER;
}
else if (sscanf (keyword->string, "%lf%1s", &parsed_double, dummy) == 1
&& parsed_double <= DBL_MAX)
{
keyword->double_value = parsed_double;
keyword->type = KEYWORD_TYPE_DOUBLE;
}
else
keyword->type = KEYWORD_TYPE_STRING;
}
else
{
keyword->integer_value = atoi (keyword->string);
keyword->type = KEYWORD_TYPE_INTEGER;
}
}
/**
* @brief Cleans up keywords with special conditions and relations.
*
* @param[in] keyword Keyword to clean up.
*/
static void
cleanup_keyword (keyword_t *keyword)
{
if (keyword->column == NULL)
return;
if (strcasecmp (keyword->column, "first") == 0)
{
/* "first" must be >= 1 */
if (keyword->integer_value <= 0)
{
g_free (keyword->string);
keyword->integer_value = 1;
keyword->string = g_strdup ("1");
}
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
}
else if (strcasecmp (keyword->column, "rows") == 0)
{
/* rows must be >= 1 or a special value (-1 or -2) */
if (keyword->integer_value == 0)
{
g_free (keyword->string);
keyword->integer_value = 1;
keyword->string = g_strdup ("1");
}
else if (keyword->integer_value < -2)
{
g_free (keyword->string);
keyword->integer_value = -1;
keyword->string = g_strdup ("-1");
}
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
}
else if (strcasecmp (keyword->column, "min_qod") == 0)
{
/* min_qod must be a percentage (between 0 and 100) */
if (keyword->integer_value < 0)
{
g_free (keyword->string);
keyword->integer_value = 0;
keyword->string = g_strdup ("0");
}
else if (keyword->integer_value > 100)
{
g_free (keyword->string);
keyword->integer_value = 100;
keyword->string = g_strdup ("100");
}
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
}
else if (strcasecmp (keyword->column, "apply_overrides") == 0
|| strcasecmp (keyword->column, "overrides") == 0
|| strcasecmp (keyword->column, "notes") == 0
|| strcasecmp (keyword->column, "result_hosts_only") == 0)
{
/* Boolean options (0 or 1) */
if (keyword->integer_value != 0 && keyword->integer_value != 1)
{
g_free (keyword->string);
keyword->integer_value = 1;
keyword->string = g_strdup ("1");
}
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
}
else if (strcasecmp (keyword->column, "delta_states") == 0
|| strcasecmp (keyword->column, "levels") == 0
|| strcasecmp (keyword->column, "sort") == 0
|| strcasecmp (keyword->column, "sort-reverse") == 0)
{
/* Text options */
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
}
}
/**
* @brief Check whether a keyword has any effect in the filter.
*
* Some keywords are redundant, like a second sort= keyword.
*
* @param[in] array Array of existing keywords.
* @param[in] keyword Keyword under consideration.
*
* @return 0 no, 1 yes.
*/
static int
keyword_applies (array_t *array, const keyword_t *keyword)
{
if (keyword->column
&& ((strcmp (keyword->column, "sort") == 0)
|| (strcmp (keyword->column, "sort-reverse") == 0))
&& (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column
&& ((strcmp (item->column, "sort") == 0)
|| (strcmp (item->column, "sort-reverse") == 0)))
return 0;
}
return 1;
}
if (keyword->column
&& (strcmp (keyword->column, "first") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "first") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "rows") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "rows") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "apply_overrides") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "apply_overrides") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "delta_states") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "delta_states") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "levels") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "levels") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "min_qod") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "min_qod") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "notes") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "notes") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "overrides") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "overrides") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "result_hosts_only") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "result_hosts_only") == 0))
return 0;
}
}
if (keyword->column
&& (strcmp (keyword->column, "timezone") == 0))
{
int index;
index = array->len;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (array, index);
if (item->column && (strcmp (item->column, "timezone") == 0))
return 0;
}
}
return 1;
}
/**
* @brief Free a split filter.
*
* @param[in] split Split filter.
*/
void
filter_free (array_t *split)
{
keyword_t **point;
for (point = (keyword_t**) split->pdata; *point; point++)
keyword_free (*point);
array_free (split);
}
/**
* @brief Flag to control the default sorting produced by split_filter.
*
* If this is true, and the filter does not specify a sort field, then
* split_filter will not insert a default sort term, so that the random
* (and fast) table order in the database will be used.
*/
static int table_order_if_sort_not_specified = 0;
/**
* @brief Ensure filter parts contains the special keywords.
*
* @param[in] parts Array of keyword strings.
* @param[in] given_filter Filter term.
*/
void
split_filter_add_specials (array_t *parts, const gchar* given_filter)
{
int index, first, max, sort;
keyword_t *keyword;
index = parts->len;
first = max = sort = 0;
while (index--)
{
keyword_t *item;
item = (keyword_t*) g_ptr_array_index (parts, index);
if (item->column && (strcmp (item->column, "first") == 0))
first = 1;
else if (item->column && (strcmp (item->column, "rows") == 0))
max = 1;
else if (item->column
&& ((strcmp (item->column, "sort") == 0)
|| (strcmp (item->column, "sort-reverse") == 0)))
sort = 1;
}
if (first == 0)
{
keyword = g_malloc0 (sizeof (keyword_t));
keyword->column = g_strdup ("first");
keyword->string = g_strdup ("1");
keyword->type = KEYWORD_TYPE_STRING;
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
array_add (parts, keyword);
}
if (max == 0)
{
keyword = g_malloc0 (sizeof (keyword_t));
keyword->column = g_strdup ("rows");
keyword->string = g_strdup ("-2");
keyword->type = KEYWORD_TYPE_STRING;
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
array_add (parts, keyword);
}
if (table_order_if_sort_not_specified == 0 && sort == 0)
{
keyword = g_malloc0 (sizeof (keyword_t));
keyword->column = g_strdup ("sort");
keyword->string = g_strdup ("name");
keyword->type = KEYWORD_TYPE_STRING;
keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
array_add (parts, keyword);
}
}
/**
* @brief Split the filter term into parts.
*
* @param[in] given_filter Filter term.
*
* @return Array of strings, the parts.
*/
array_t *
split_filter (const gchar* given_filter)
{
int in_quote, between;
array_t *parts;
const gchar *current_part, *filter;
keyword_t *keyword;
assert (given_filter);
/* Collect the filter terms in an array. */
filter = given_filter;
parts = make_array ();
in_quote = 0;
between = 1;
keyword = NULL;
current_part = filter; /* To silence compiler warning. */
while (*filter)
{
switch (*filter)
{
case '=':
case '~':
if (between)
{
/* Empty index. Start a part. */
keyword = g_malloc0 (sizeof (keyword_t));
if (*filter == '=')
keyword->equal = 1;
else
keyword->approx = 1;
current_part = filter + 1;
between = 0;
break;
}
case ':':
case '>':
case '<':
if (between)
{
/* Empty index. Start a part. */
keyword = g_malloc0 (sizeof (keyword_t));
current_part = filter;
between = 0;
break;
}
if (in_quote)
break;
/* End of an index. */
if (keyword == NULL)
{
assert (0);
break;
}
if (keyword->column)
/* Already had an index char. */
break;
if (filter <= (current_part - 1))
{
assert (0);
break;
}
keyword->column = g_strndup (current_part,
filter - current_part);
current_part = filter + 1;
keyword->relation = parse_column_relation(*filter);
break;
case ' ':
case '\t':
case '\n':
case '\r':
if (in_quote || between)
break;
/* End of a part. */
if (keyword == NULL)
{
assert (0);
break;
}
keyword->string = g_strndup (current_part, filter - current_part);
parse_keyword (keyword);
cleanup_keyword (keyword);
if (keyword_applies (parts, keyword))
array_add (parts, keyword);
keyword = NULL;
between = 1;
break;
case '"':
if (in_quote)
{
/* End of a quoted part. */
if (keyword == NULL)
{
assert (0);
break;
}
keyword->quoted = 1;
keyword->string = g_strndup (current_part,
filter - current_part);
parse_keyword (keyword);
cleanup_keyword (keyword);
if (keyword_applies (parts, keyword))
array_add (parts, keyword);
keyword = NULL;
in_quote = 0;
between = 1;
}
else if (between)
{
/* Start of a quoted part. */
keyword = g_malloc0 (sizeof (keyword_t));
in_quote = 1;
current_part = filter + 1;
between = 0;
}
else if (keyword->column && filter == current_part)
{
/* A quoted index. */
in_quote = 1;
current_part++;
}
else if ((keyword->equal || keyword->approx)
&& filter == current_part)
{
/* A quoted exact term, like ="abc"
* or a prefixed approximate term, like ~"abc". */
in_quote = 1;
current_part++;
}
/* Else just a quote in a keyword, like ab"cd. */
break;
default:
if (between)
{
/* Start of a part. */
keyword = g_malloc0 (sizeof (keyword_t));
current_part = filter;
between = 0;
}
break;
}
filter++;
}
if (between == 0)
{
if (keyword == NULL)
assert (0);
else
{
keyword->quoted = in_quote;
keyword->string = g_strdup (current_part);
parse_keyword (keyword);
cleanup_keyword (keyword);
if (keyword_applies (parts, keyword))
array_add (parts, keyword);
keyword = NULL;
}
}
assert (keyword == NULL);
/* Make sure the special keywords appear in the array. */
split_filter_add_specials (parts, given_filter);
array_add (parts, NULL);
return parts;
}
/**
* @brief Get info from a filter.
*
* It's up to the caller to ensure that max is adjusted for Max Rows Per Page
* (by calling manage_max_rows).
*
* @param[in] filter Filter.
* @param[out] first Number of first item.
* @param[out] max Max number of rows.
* @param[out] sort_field Sort field.
* @param[out] sort_order Sort order.
*/
void
manage_filter_controls (const gchar *filter, int *first, int *max,
gchar **sort_field, int *sort_order)
{
keyword_t **point;
array_t *split;
if (filter == NULL)
{
if (first)
*first = 1;
if (max)
*max = -2;
if (sort_field)
*sort_field = g_strdup ("name");
if (sort_order)
*sort_order = 1;
return;
}
split = split_filter (filter);
point = (keyword_t**) split->pdata;
if (first)
{
*first = 1;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column && (strcmp (keyword->column, "first") == 0))
{
*first = atoi (keyword->string);
if (*first < 0)
*first = 0;
break;
}
point++;
}
}
point = (keyword_t**) split->pdata;
if (max)
{
*max = -2;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column && (strcmp (keyword->column, "rows") == 0))
{
*max = atoi (keyword->string);
if (*max == -2)
setting_value_int (SETTING_UUID_ROWS_PER_PAGE, max);
else if (*max < 1)
*max = -1;
break;
}
point++;
}
}
point = (keyword_t**) split->pdata;
if (sort_field || sort_order)
{
if (sort_field) *sort_field = NULL;
if (sort_order) *sort_order = 1;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column
&& (strcmp (keyword->column, "sort") == 0))
{
if (sort_field) *sort_field = g_strdup (keyword->string);
if (sort_order) *sort_order = 1;
break;
}
if (keyword->column
&& (strcmp (keyword->column, "sort-reverse") == 0))
{
if (sort_field) *sort_field = g_strdup (keyword->string);
if (sort_order) *sort_order = 0;
break;
}
point++;
}
if (sort_field && (*sort_field == NULL))
*sort_field = g_strdup ("name");
}
filter_free (split);
return;
}
/**
* @brief Get an int column from a filter split.
*
* @param[in] point Filter split.
* @param[in] column Name of column.
* @param[out] val Value of column.
*
* @return 0 success, 1 fail.
*/
static int
filter_control_int (keyword_t **point, const char *column, int *val)
{
if (val)
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column
&& (strcmp (keyword->column, column) == 0))
{
*val = atoi (keyword->string);
return 0;
}
point++;
}
return 1;
}
/**
* @brief Get a string column from a filter split.
*
* @param[in] point Filter split.
* @param[in] column Name of column.
* @param[out] string Value of column, freshly allocated.
*
* @return 0 success, 1 fail.
*/
static int
filter_control_str (keyword_t **point, const char *column, gchar **string)
{
if (string)
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column
&& (strcmp (keyword->column, column) == 0))
{
*string = g_strdup (keyword->string);
return 0;
}
point++;
}
return 1;
}
/**
* @brief Get info from a result filter for a report.
*
* It's up to the caller to ensure that max is adjusted for Max Rows Per Page
* (by calling manage_max_rows).
*
* @param[in] filter Filter.
* @param[out] first Number of first item.
* @param[out] max Max number of rows.
* @param[out] sort_field Sort field.
* @param[out] sort_order Sort order.
* @param[out] result_hosts_only Whether to show only hosts with results.
* @param[out] min_qod Minimum QoD base of included results. All
* results if NULL.
* @param[out] levels String describing threat levels (message types)
* to include in count (for example, "hmlg" for
* High, Medium, Low and loG). All levels if NULL.
* @param[out] delta_states String describing delta states to include in count
* (for example, "sngc" Same, New, Gone and Changed).
* All levels if NULL.
* @param[out] search_phrase Phrase that results must include. All results
* if NULL or "".
* @param[out] search_phrase_exact Whether search phrase is exact.
* @param[out] notes Whether to include notes.
* @param[out] overrides Whether to include overrides.
* @param[out] apply_overrides Whether to apply overrides.
* @param[out] zone Timezone.
*/
void
manage_report_filter_controls (const gchar *filter, int *first, int *max,
gchar **sort_field, int *sort_order,
int *result_hosts_only, gchar **min_qod,
gchar **levels, gchar **delta_states,
gchar **search_phrase, int *search_phrase_exact,
int *notes, int *overrides,
int *apply_overrides, gchar **zone)
{
keyword_t **point;
array_t *split;
int val;
gchar *string;
if (filter == NULL)
return;
split = split_filter (filter);
point = (keyword_t**) split->pdata;
if (first)
{
*first = 1;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column && (strcmp (keyword->column, "first") == 0))
{
*first = atoi (keyword->string);
if (*first < 0)
*first = 0;
break;
}
point++;
}
/* Switch from 1 to 0 indexing. */
(*first)--;
}
point = (keyword_t**) split->pdata;
if (max)
{
*max = 100;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column && (strcmp (keyword->column, "rows") == 0))
{
*max = atoi (keyword->string);
if (*max == -2)
setting_value_int (SETTING_UUID_ROWS_PER_PAGE, max);
else if (*max < 1)
*max = -1;
break;
}
point++;
}
}
point = (keyword_t**) split->pdata;
if (sort_field || sort_order)
{
if (sort_field) *sort_field = NULL;
if (sort_order) *sort_order = 1;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column
&& (strcmp (keyword->column, "sort") == 0))
{
if (sort_field) *sort_field = g_strdup (keyword->string);
if (sort_order) *sort_order = 1;
break;
}
if (keyword->column
&& (strcmp (keyword->column, "sort-reverse") == 0))
{
if (sort_field) *sort_field = g_strdup (keyword->string);
if (sort_order) *sort_order = 0;
break;
}
point++;
}
if (sort_field && (*sort_field == NULL))
*sort_field = g_strdup ("name"); /* NVT name. */
}
if (search_phrase)
{
GString *phrase;
phrase = g_string_new ("");
point = (keyword_t**) split->pdata;
if (search_phrase_exact)
*search_phrase_exact = 0;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column == NULL)
{
if (search_phrase_exact && keyword->equal)
/* If one term is "exact" then the search is "exact", because
* for reports the filter terms are combined into a single
* search term. */
*search_phrase_exact = 1;
g_string_append_printf (phrase, "%s ", keyword->string);
}
point++;
}
*search_phrase = g_strchomp (phrase->str);
g_string_free (phrase, FALSE);
}
if (result_hosts_only)
{
if (filter_control_int ((keyword_t **) split->pdata,
"result_hosts_only",
&val))
*result_hosts_only = 1;
else
*result_hosts_only = val;
}
if (notes)
{
if (filter_control_int ((keyword_t **) split->pdata,
"notes",
&val))
*notes = 1;
else
*notes = val;
}
if (overrides)
{
if (filter_control_int ((keyword_t **) split->pdata,
"overrides",
&val))
*overrides = 1;
else
*overrides = val;
}
if (apply_overrides)
{
if (filter_control_int ((keyword_t **) split->pdata,
"apply_overrides",
&val))
{
if (filter_control_int ((keyword_t **) split->pdata,
"overrides",
&val))
*apply_overrides = 1;
else
*apply_overrides = val;
}
else
*apply_overrides = val;
}
if (delta_states)
{
if (filter_control_str ((keyword_t **) split->pdata,
"delta_states",
&string))
*delta_states = NULL;
else
*delta_states = string;
}
if (levels)
{
if (filter_control_str ((keyword_t **) split->pdata,
"levels",
&string))
*levels = NULL;
else
*levels = string;
}
if (min_qod)
{
if (filter_control_str ((keyword_t **) split->pdata,
"min_qod",
&string))
*min_qod = NULL;
else
*min_qod = string;
}
if (zone)
{
if (filter_control_str ((keyword_t **) split->pdata,
"timezone",
&string))
*zone = NULL;
else
*zone = string;
}
filter_free (split);
return;
}
/**
* @brief Append relation to filter.
*
* @param[in] clean Filter.
* @param[in] keyword Keyword
* @param[in] relation Relation char.
*/
static void
append_relation (GString *clean, keyword_t *keyword, const char relation)
{
if (strcmp (keyword->column, "rows") == 0)
{
int max;
if (strcmp (keyword->string, "-2") == 0)
setting_value_int (SETTING_UUID_ROWS_PER_PAGE, &max);
else
max = atoi (keyword->string);
g_string_append_printf (clean,
" %s%c%i",
keyword->column,
relation,
manage_max_rows (max));
}
else if (keyword->quoted)
g_string_append_printf (clean,
" %s%c\"%s\"",
keyword->column,
relation,
keyword->string);
else
g_string_append_printf (clean,
" %s%c%s",
keyword->column,
relation,
keyword->string);
}
/**
* @brief Clean a filter, removing a keyword in the process.
*
* @param[in] filter Filter.
* @param[in] column Keyword to remove, or NULL.
*
* @return Cleaned filter.
*/
gchar *
manage_clean_filter_remove (const gchar *filter, const gchar *column)
{
GString *clean;
keyword_t **point;
array_t *split;
if (filter == NULL)
return g_strdup ("");
clean = g_string_new ("");
split = split_filter (filter);
point = (keyword_t**) split->pdata;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column
&& column
&& strlen (column)
&& ((strcasecmp (keyword->column, column) == 0)
|| (keyword->column[0] == '_'
&& strcasecmp (keyword->column + 1, column) == 0)))
{
/* Remove this keyword. */;
}
else if (keyword->column)
switch (keyword->relation)
{
case KEYWORD_RELATION_COLUMN_EQUAL:
append_relation (clean, keyword, '=');
break;
case KEYWORD_RELATION_COLUMN_APPROX:
append_relation (clean, keyword, '~');
break;
case KEYWORD_RELATION_COLUMN_ABOVE:
append_relation (clean, keyword, '>');
break;
case KEYWORD_RELATION_COLUMN_BELOW:
append_relation (clean, keyword, '<');
break;
case KEYWORD_RELATION_COLUMN_REGEXP:
append_relation (clean, keyword, ':');
break;
case KEYWORD_RELATION_APPROX:
if (keyword->quoted)
g_string_append_printf (clean, " \"%s\"", keyword->string);
else
g_string_append_printf (clean, " %s", keyword->string);
break;
}
else
{
const char *relation_symbol;
if (keyword->equal)
relation_symbol = "=";
else if (keyword->approx)
relation_symbol = "~";
else
relation_symbol = "";
if (keyword->quoted)
g_string_append_printf (clean, " %s\"%s\"",
relation_symbol,
keyword->string);
else
g_string_append_printf (clean, " %s%s",
relation_symbol,
keyword->string);
}
point++;
}
filter_free (split);
return g_strstrip (g_string_free (clean, FALSE));
}
/**
* @brief Clean a filter.
*
* @param[in] filter Filter.
*
* @return Cleaned filter.
*/
gchar *
manage_clean_filter (const gchar *filter)
{
return manage_clean_filter_remove (filter, NULL);
}
/**
* @brief Return SQL join words for filter_clause.
*
* @param[in] first Whether keyword is first.
* @param[in] last_was_and Whether last keyword was "and".
* @param[in] last_was_not Whether last keyword was "not".
*
* @return SQL join words.
*/
static const char *
get_join (int first, int last_was_and, int last_was_not)
{
const char *pre;
if (first)
{
if (last_was_not)
pre = "NOT ";
else
pre = "";
}
else
{
if (last_was_and)
{
if (last_was_not)
pre = " AND NOT ";
else
pre = " AND ";
}
else
{
if (last_was_not)
pre = " OR NOT ";
else
pre = " OR ";
}
}
return pre;
}
/**
* @brief Get the column expression for a filter column.
*
* @param[in] select_columns SELECT columns.
* @param[in] filter_column Filter column.
* @param[out] type Type of returned column.
*
* @return Column for the SELECT statement.
*/
static gchar *
columns_select_column_single (column_t *select_columns,
const char *filter_column,
keyword_type_t* type)
{
column_t *columns;
if (type)
*type = KEYWORD_TYPE_UNKNOWN;
if (select_columns == NULL)
return NULL;
columns = select_columns;
while ((*columns).select)
{
if ((*columns).filter
&& strcmp ((*columns).filter, filter_column) == 0)
{
if (type)
*type = (*columns).type;
return (*columns).select;
}
if ((*columns).filter
&& *((*columns).filter)
&& *((*columns).filter) == '_'
&& strcmp (((*columns).filter) + 1, filter_column) == 0)
{
if (type)
*type = (*columns).type;
return (*columns).select;
}
columns++;
}
columns = select_columns;
while ((*columns).select)
{
if (strcmp ((*columns).select, filter_column) == 0)
{
if (type)
*type = (*columns).type;
return (*columns).select;
}
columns++;
}
return NULL;
}
/**
* @brief Get the selection term for a filter column.
*
* @param[in] select_columns SELECT columns.
* @param[in] where_columns WHERE "columns".
* @param[in] filter_column Filter column.
*
* @return Column for the SELECT statement.
*/
static gchar *
columns_select_column (column_t *select_columns,
column_t *where_columns,
const char *filter_column)
{
gchar *column;
column = columns_select_column_single (select_columns, filter_column, NULL);
if (column)
return column;
return columns_select_column_single (where_columns, filter_column, NULL);
}
/**
* @brief Get the selection term for a filter column.
*
* @param[in] select_columns SELECT columns.
* @param[in] where_columns WHERE "columns".
* @param[in] filter_column Filter column.
* @param[out] type Type of the returned column.
*
* @return Column for the SELECT statement.
*/
static gchar *
columns_select_column_with_type (column_t *select_columns,
column_t *where_columns,
const char *filter_column,
keyword_type_t* type)
{
gchar *column;
column = columns_select_column_single (select_columns, filter_column, type);
if (column)
return column;
return columns_select_column_single (where_columns, filter_column, type);
}
/**
* @brief Return column list for SELECT statement.
*
* @param[in] select_columns SELECT columns.
*
* @return Column list for the SELECT statement.
*/
gchar *
columns_build_select (column_t *select_columns)
{
if (select_columns == NULL)
return g_strdup ("''");
if ((*select_columns).select)
{
column_t *columns;
GString *select;
columns = select_columns;
select = g_string_new ("");
g_string_append (select, (*columns).select);
if ((*columns).filter)
g_string_append_printf (select, " AS %s", (*columns).filter);
columns++;
while ((*columns).select)
{
g_string_append_printf (select, ", %s", (*columns).select);
if ((*columns).filter)
g_string_append_printf (select, " AS %s", (*columns).filter);
columns++;
}
return g_string_free (select, FALSE);
}
return g_strdup ("''");
}
/**
* @brief Check whether a keyword applies to a column.
*
* @param[in] keyword Keyword.
* @param[in] column Column.
*
* @return 1 if applies, else 0.
*/
static int
keyword_applies_to_column (keyword_t *keyword, const char* column)
{
if ((strcmp (column, "threat") == 0)
&& (strstr ("None", keyword->string) == NULL)
&& (strstr ("False Positive", keyword->string) == NULL)
&& (strstr ("Error", keyword->string) == NULL)
&& (strstr ("Alarm", keyword->string) == NULL)
&& (strstr ("High", keyword->string) == NULL)
&& (strstr ("Medium", keyword->string) == NULL)
&& (strstr ("Low", keyword->string) == NULL)
&& (strstr ("Log", keyword->string) == NULL))
return 0;
if ((strcmp (column, "trend") == 0)
&& (strstr ("more", keyword->string) == NULL)
&& (strstr ("less", keyword->string) == NULL)
&& (strstr ("up", keyword->string) == NULL)
&& (strstr ("down", keyword->string) == NULL)
&& (strstr ("same", keyword->string) == NULL))
return 0;
if ((strcmp (column, "status") == 0)
&& (strstr ("Delete Requested", keyword->string) == NULL)
&& (strstr ("Ultimate Delete Requested", keyword->string) == NULL)
&& (strstr ("Done", keyword->string) == NULL)
&& (strstr ("New", keyword->string) == NULL)
&& (strstr ("Running", keyword->string) == NULL)
&& (strstr ("Queued", keyword->string) == NULL)
&& (strstr ("Stop Requested", keyword->string) == NULL)
&& (strstr ("Stopped", keyword->string) == NULL)
&& (strstr ("Interrupted", keyword->string) == NULL))
return 0;
return 1;
}
/**
* @brief Append parts for a "tag" keyword to a filter clause.
*
* @param[in,out] clause Buffer for the filter clause to append to.
* @param[in] keyword The keyword to create the filter clause part for.
* @param[in] type The resource type.
* @param[in] first_keyword Whether keyword is first.
* @param[in] last_was_and Whether last keyword was "and".
* @param[in] last_was_not Whether last keyword was "not".
*/
static void
filter_clause_append_tag (GString *clause, keyword_t *keyword,
const char *type, int first_keyword,
int last_was_and, int last_was_not)
{
gchar *quoted_keyword;
gchar **tag_split, *tag_name, *tag_value;
int value_given;
quoted_keyword = sql_quote (keyword->string);
tag_split = g_strsplit (quoted_keyword, "=", 2);
tag_name = g_strdup (tag_split[0] ? tag_split[0] : "");
if (tag_split[0] && tag_split[1])
{
tag_value = g_strdup (tag_split[1]);
value_given = 1;
}
else
{
tag_value = g_strdup ("");
value_given = 0;
}
if (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL
|| keyword->relation == KEYWORD_RELATION_COLUMN_ABOVE
|| keyword->relation == KEYWORD_RELATION_COLUMN_BELOW)
{
g_string_append_printf
(clause,
"%s"
"(EXISTS"
" (SELECT * FROM tags"
" WHERE tags.name = '%s'"
" AND tags.active != 0"
" AND user_has_access_uuid (CAST ('tag' AS text),"
" CAST (tags.uuid AS text),"
" CAST ('get_tags' AS text),"
" 0)"
" AND EXISTS (SELECT * FROM tag_resources"
" WHERE tag_resources.resource_uuid"
" = %ss.uuid"
" AND tag_resources.resource_type"
" = '%s'"
" AND tag = tags.id)"
" %s%s%s))",
get_join (first_keyword, last_was_and,
last_was_not),
tag_name,
type,
type,
(value_given
? "AND tags.value = '"
: ""),
value_given ? tag_value : "",
(value_given
? "'"
: ""));
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_APPROX)
{
g_string_append_printf
(clause,
"%s"
"(EXISTS"
" (SELECT * FROM tags"
" WHERE tags.name %s '%%%%%s%%%%'"
" AND tags.active != 0"
" AND user_has_access_uuid (CAST ('tag' AS text),"
" CAST (tags.uuid AS text),"
" CAST ('get_tags' AS text),"
" 0)"
" AND EXISTS (SELECT * FROM tag_resources"
" WHERE tag_resources.resource_uuid"
" = %ss.uuid"
" AND tag_resources.resource_type"
" = '%s'"
" AND tag = tags.id)"
" AND tags.value %s '%%%%%s%%%%'))",
get_join (first_keyword, last_was_and,
last_was_not),
sql_ilike_op (),
tag_name,
type,
type,
sql_ilike_op (),
tag_value);
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_REGEXP)
{
g_string_append_printf
(clause,
"%s"
"(EXISTS"
" (SELECT * FROM tags"
" WHERE tags.name %s '%s'"
" AND tags.active != 0"
" AND user_has_access_uuid (CAST ('tag' AS text),"
" CAST (tags.uuid AS text),"
" CAST ('get_tags' AS text),"
" 0)"
" AND EXISTS (SELECT * FROM tag_resources"
" WHERE tag_resources.resource_uuid"
" = %ss.uuid"
" AND tag_resources.resource_type"
" = '%s'"
" AND tag = tags.id)"
" AND tags.value"
" %s '%s'))",
get_join (first_keyword, last_was_and,
last_was_not),
sql_regexp_op (),
tag_name,
type,
type,
sql_regexp_op (),
tag_value);
}
g_free (quoted_keyword);
g_strfreev(tag_split);
g_free(tag_name);
g_free(tag_value);
}
/**
* @brief Append parts for a "tag_id" keyword to a filter clause.
*
* @param[in,out] clause Buffer for the filter clause to append to.
* @param[in] keyword The keyword to create the filter clause part for.
* @param[in] type The resource type.
* @param[in] first_keyword Whether keyword is first.
* @param[in] last_was_and Whether last keyword was "and".
* @param[in] last_was_not Whether last keyword was "not".
*/
static void
filter_clause_append_tag_id (GString *clause, keyword_t *keyword,
const char *type, int first_keyword,
int last_was_and, int last_was_not)
{
gchar *quoted_keyword;
quoted_keyword = sql_quote (keyword->string);
if (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL
|| keyword->relation == KEYWORD_RELATION_COLUMN_ABOVE
|| keyword->relation == KEYWORD_RELATION_COLUMN_BELOW)
{
g_string_append_printf
(clause,
"%s"
"(EXISTS"
" (SELECT * FROM tags"
" WHERE tags.uuid = '%s'"
" AND user_has_access_uuid (CAST ('tag' AS text),"
" CAST (tags.uuid AS text),"
" CAST ('get_tags' AS text),"
" 0)"
" AND EXISTS (SELECT * FROM tag_resources"
" WHERE tag_resources.resource_uuid"
" = %ss.uuid"
" AND tag_resources.resource_type"
" = '%s'"
" AND tag = tags.id)))",
get_join (first_keyword, last_was_and,
last_was_not),
quoted_keyword,
type,
type);
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_APPROX)
{
g_string_append_printf
(clause,
"%s"
"(EXISTS"
" (SELECT * FROM tags"
" WHERE tags.uuid %s '%%%%%s%%%%'"
" AND tags.active != 0"
" AND user_has_access_uuid (CAST ('tag' AS text),"
" CAST (tags.uuid AS text),"
" CAST ('get_tags' AS text),"
" 0)"
" AND EXISTS (SELECT * FROM tag_resources"
" WHERE tag_resources.resource_uuid"
" = %ss.uuid"
" AND tag_resources.resource_type"
" = '%s'"
" AND tag = tags.id)))",
get_join (first_keyword, last_was_and,
last_was_not),
sql_ilike_op (),
quoted_keyword,
type,
type);
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_REGEXP)
{
g_string_append_printf
(clause,
"%s"
"(EXISTS"
" (SELECT * FROM tags"
" WHERE tags.uuid %s '%s'"
" AND tags.active != 0"
" AND user_has_access_uuid (CAST ('tag' AS text),"
" CAST (tags.uuid AS text),"
" CAST ('get_tags' AS text),"
" 0)"
" AND EXISTS (SELECT * FROM tag_resources"
" WHERE tag_resources.resource_uuid"
" = %ss.uuid"
" AND tag_resources.resource_type"
" = '%s'"
" AND tag = tags.id)))",
get_join (first_keyword, last_was_and,
last_was_not),
sql_regexp_op (),
quoted_keyword,
type,
type);
}
g_free (quoted_keyword);
}
/**
* @brief Return SQL WHERE clause for restricting a SELECT to a filter term.
*
* @param[in] type Resource type.
* @param[in] filter Filter term.
* @param[in] filter_columns Filter columns.
* @param[in] select_columns SELECT columns.
* @param[in] where_columns Columns in SQL that only appear in WHERE clause.
* @param[out] trash Whether the trash table is being queried.
* @param[out] order_return If given then order clause.
* @param[out] first_return If given then first row.
* @param[out] max_return If given then max rows.
* @param[out] permissions When given then permissions string vector.
* @param[out] owner_filter When given then value of owner keyword.
*
* @return WHERE clause for filter if one is required, else NULL.
*/
gchar *
filter_clause (const char* type, const char* filter,
const char **filter_columns, column_t *select_columns,
column_t *where_columns, int trash, gchar **order_return,
int *first_return, int *max_return, array_t **permissions,
gchar **owner_filter)
{
GString *clause, *order;
keyword_t **point;
int first_keyword, first_order, last_was_and, last_was_not, last_was_re, skip;
array_t *split;
if (filter == NULL)
filter = "";
while (*filter && isspace (*filter)) filter++;
if (permissions)
*permissions = make_array ();
if (owner_filter)
*owner_filter = NULL;
/* Add SQL to the clause for each keyword or phrase. */
if (max_return)
*max_return = -2;
clause = g_string_new ("");
order = g_string_new ("");
/* NB This may add terms that are missing, like "sort". */
split = split_filter (filter);
point = (keyword_t**) split->pdata;
first_keyword = 1;
last_was_and = 0;
last_was_not = 0;
last_was_re = 0;
first_order = 1;
while (*point)
{
gchar *quoted_keyword;
int index;
keyword_t *keyword;
skip = 0;
keyword = *point;
if ((keyword->column == NULL)
&& (strlen (keyword->string) == 0))
{
point++;
continue;
}
if ((keyword->column == NULL)
&& (strcasecmp (keyword->string, "or") == 0))
{
point++;
continue;
}
if ((keyword->column == NULL)
&& (strcasecmp (keyword->string, "and") == 0))
{
last_was_and = 1;
point++;
continue;
}
if ((keyword->column == NULL)
&& (strcasecmp (keyword->string, "not") == 0))
{
last_was_not = 1;
point++;
continue;
}
if ((keyword->column == NULL)
&& (strcasecmp (keyword->string, "re") == 0))
{
last_was_re = 1;
point++;
continue;
}
if ((keyword->column == NULL)
&& (strcasecmp (keyword->string, "regexp") == 0))
{
last_was_re = 1;
point++;
continue;
}
/* Check for ordering parts, like sort=name or sort-reverse=string. */
if (keyword->column && (strcasecmp (keyword->column, "sort") == 0))
{
if (vector_find_filter (filter_columns, keyword->string) == 0)
{
point++;
continue;
}
if (first_order)
{
if ((strcmp (type, "report") == 0)
&& (strcmp (keyword->string, "status") == 0))
g_string_append_printf
(order,
" ORDER BY"
" (CASE WHEN (SELECT target = 0 FROM tasks"
" WHERE tasks.id = task)"
" THEN 'Container'"
" ELSE run_status_name (scan_run_status)"
" || (SELECT CAST (temp / 100 AS text)"
" || CAST (temp / 10 AS text)"
" || CAST (temp %% 10 as text)"
" FROM (SELECT report_progress (id) AS temp)"
" AS temp_sub)"
" END)"
" ASC");
else if ((strcmp (type, "task") == 0)
&& (strcmp (keyword->string, "status") == 0))
g_string_append_printf
(order,
" ORDER BY"
" (CASE WHEN target = 0"
" THEN 'Container'"
" ELSE run_status_name (run_status)"
" || (SELECT CAST (temp / 100 AS text)"
" || CAST (temp / 10 AS text)"
" || CAST (temp %% 10 as text)"
" FROM (SELECT report_progress (id) AS temp"
" FROM reports"
" WHERE task = tasks.id"
" ORDER BY date DESC LIMIT 1)"
" AS temp_sub)"
" END)"
" ASC");
else if ((strcmp (type, "task") == 0)
&& (strcmp (keyword->string, "threat") == 0))
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY order_threat (%s) ASC",
column);
}
else if (strcmp (keyword->string, "severity") == 0
|| strcmp (keyword->string, "original_severity") == 0
|| strcmp (keyword->string, "cvss") == 0
|| strcmp (keyword->string, "cvss_base") == 0
|| strcmp (keyword->string, "max_cvss") == 0
|| strcmp (keyword->string, "fp_per_host") == 0
|| strcmp (keyword->string, "log_per_host") == 0
|| strcmp (keyword->string, "low_per_host") == 0
|| strcmp (keyword->string, "medium_per_host") == 0
|| strcmp (keyword->string, "high_per_host") == 0)
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
g_string_append_printf (order,
" ORDER BY CASE CAST (%s AS text)"
" WHEN '' THEN '-Infinity'::real"
" ELSE coalesce(%s::real,"
" '-Infinity'::real)"
" END ASC",
column,
column);
}
else if (strcmp (keyword->string, "roles") == 0)
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY"
" CASE WHEN %s %s 'Admin.*'"
" THEN '0' || %s"
" ELSE '1' || %s END ASC",
column,
sql_regexp_op (),
column,
column);
}
else if ((strcmp (keyword->string, "created") == 0)
|| (strcmp (keyword->string, "modified") == 0)
|| (strcmp (keyword->string, "published") == 0)
|| (strcmp (keyword->string, "qod") == 0)
|| (strcmp (keyword->string, "cves") == 0)
|| (strcmp (keyword->string, "high") == 0)
|| (strcmp (keyword->string, "medium") == 0)
|| (strcmp (keyword->string, "low") == 0)
|| (strcmp (keyword->string, "log") == 0)
|| (strcmp (keyword->string, "false_positive") == 0)
|| (strcmp (keyword->string, "hosts") == 0)
|| (strcmp (keyword->string, "result_hosts") == 0)
|| (strcmp (keyword->string, "results") == 0)
|| (strcmp (keyword->string, "latest_severity") == 0)
|| (strcmp (keyword->string, "highest_severity") == 0)
|| (strcmp (keyword->string, "average_severity") == 0))
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY %s ASC",
column);
}
else if ((strcmp (keyword->string, "ips") == 0)
|| (strcmp (keyword->string, "total") == 0)
|| (strcmp (keyword->string, "tcp") == 0)
|| (strcmp (keyword->string, "udp") == 0))
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY CAST (%s AS INTEGER) ASC",
column);
}
else if (strcmp (keyword->string, "ip") == 0
|| strcmp (keyword->string, "host") == 0)
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY order_inet (%s) ASC",
column);
}
else if ((strcmp (type, "note")
&& strcmp (type, "override"))
|| (strcmp (keyword->string, "nvt")
&& strcmp (keyword->string, "name")))
{
gchar *column;
keyword_type_t column_type;
column = columns_select_column_with_type (select_columns,
where_columns,
keyword->string,
&column_type);
assert (column);
if (column_type == KEYWORD_TYPE_INTEGER)
g_string_append_printf (order,
" ORDER BY"
" cast (%s AS bigint) ASC",
column);
else if (column_type == KEYWORD_TYPE_DOUBLE)
g_string_append_printf (order,
" ORDER BY"
" cast (%s AS real) ASC",
column);
else
g_string_append_printf (order, " ORDER BY lower (%s) ASC",
column);
}
else
/* Special case for notes text sorting. */
g_string_append_printf (order,
" ORDER BY nvt ASC,"
" lower (%ss%s.text) ASC",
type,
trash ? "_trash" : "");
first_order = 0;
}
else
/* To help the client split_filter restricts the filter to one
* sorting term, preventing this from happening. */
g_string_append_printf (order, ", %s ASC",
keyword->string);
point++;
continue;
}
else if (keyword->column
&& (strcasecmp (keyword->column, "sort-reverse") == 0))
{
if (vector_find_filter (filter_columns, keyword->string) == 0)
{
point++;
continue;
}
if (first_order)
{
if ((strcmp (type, "report") == 0)
&& (strcmp (keyword->string, "status") == 0))
g_string_append_printf
(order,
" ORDER BY"
" (CASE WHEN (SELECT target = 0 FROM tasks"
" WHERE tasks.id = task)"
" THEN 'Container'"
" ELSE run_status_name (scan_run_status)"
" || (SELECT CAST (temp / 100 AS text)"
" || CAST (temp / 10 AS text)"
" || CAST (temp %% 10 as text)"
" FROM (SELECT report_progress (id) AS temp)"
" AS temp_sub)"
" END)"
" DESC");
else if ((strcmp (type, "task") == 0)
&& (strcmp (keyword->string, "status") == 0))
g_string_append_printf
(order,
" ORDER BY"
" (CASE WHEN target = 0"
" THEN 'Container'"
" ELSE run_status_name (run_status)"
" || (SELECT CAST (temp / 100 AS text)"
" || CAST (temp / 10 AS text)"
" || CAST (temp %% 10 as text)"
" FROM (SELECT report_progress (id) AS temp"
" FROM reports"
" WHERE task = tasks.id"
" ORDER BY date DESC LIMIT 1)"
" AS temp_sub)"
" END)"
" DESC");
else if ((strcmp (type, "task") == 0)
&& (strcmp (keyword->string, "threat") == 0))
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY order_threat (%s) DESC",
column);
}
else if (strcmp (keyword->string, "severity") == 0
|| strcmp (keyword->string, "original_severity") == 0
|| strcmp (keyword->string, "cvss") == 0
|| strcmp (keyword->string, "cvss_base") == 0
|| strcmp (keyword->string, "max_cvss") == 0
|| strcmp (keyword->string, "fp_per_host") == 0
|| strcmp (keyword->string, "log_per_host") == 0
|| strcmp (keyword->string, "low_per_host") == 0
|| strcmp (keyword->string, "medium_per_host") == 0
|| strcmp (keyword->string, "high_per_host") == 0)
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
g_string_append_printf (order,
" ORDER BY CASE CAST (%s AS text)"
" WHEN '' THEN '-Infinity'::real"
" ELSE coalesce(%s::real,"
" '-Infinity'::real)"
" END DESC",
column,
column);
}
else if (strcmp (keyword->string, "roles") == 0)
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY"
" CASE WHEN %s %s 'Admin.*'"
" THEN '0' || %s"
" ELSE '1' || %s END DESC",
column,
sql_regexp_op (),
column,
column);
}
else if ((strcmp (keyword->string, "created") == 0)
|| (strcmp (keyword->string, "modified") == 0)
|| (strcmp (keyword->string, "published") == 0)
|| (strcmp (keyword->string, "qod") == 0)
|| (strcmp (keyword->string, "cves") == 0)
|| (strcmp (keyword->string, "high") == 0)
|| (strcmp (keyword->string, "medium") == 0)
|| (strcmp (keyword->string, "low") == 0)
|| (strcmp (keyword->string, "log") == 0)
|| (strcmp (keyword->string, "false_positive") == 0)
|| (strcmp (keyword->string, "hosts") == 0)
|| (strcmp (keyword->string, "result_hosts") == 0)
|| (strcmp (keyword->string, "results") == 0)
|| (strcmp (keyword->string, "latest_severity") == 0)
|| (strcmp (keyword->string, "highest_severity") == 0)
|| (strcmp (keyword->string, "average_severity") == 0))
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY %s DESC",
column);
}
else if ((strcmp (keyword->string, "ips") == 0)
|| (strcmp (keyword->string, "total") == 0)
|| (strcmp (keyword->string, "tcp") == 0)
|| (strcmp (keyword->string, "udp") == 0))
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY CAST (%s AS INTEGER) DESC",
column);
}
else if (strcmp (keyword->string, "ip") == 0
|| strcmp (keyword->string, "host") == 0)
{
gchar *column;
column = columns_select_column (select_columns,
where_columns,
keyword->string);
assert (column);
g_string_append_printf (order,
" ORDER BY order_inet (%s) DESC",
column);
}
else if ((strcmp (type, "note")
&& strcmp (type, "override"))
|| (strcmp (keyword->string, "nvt")
&& strcmp (keyword->string, "name")))
{
gchar *column;
keyword_type_t column_type;
column = columns_select_column_with_type (select_columns,
where_columns,
keyword->string,
&column_type);
assert (column);
if (column_type == KEYWORD_TYPE_INTEGER)
g_string_append_printf (order,
" ORDER BY"
" cast (%s AS bigint) DESC",
column);
else if (column_type == KEYWORD_TYPE_DOUBLE)
g_string_append_printf (order,
" ORDER BY"
" cast (%s AS real) DESC",
column);
else
g_string_append_printf (order, " ORDER BY lower (%s) DESC",
column);
}
else
/* Special case for notes text sorting. */
g_string_append_printf (order,
" ORDER BY nvt DESC,"
" lower (%ss%s.text) DESC",
type,
trash ? "_trash" : "");
first_order = 0;
}
else
/* To help the client split_filter restricts the filter to one
* sorting term, preventing this from happening. */
g_string_append_printf (order, ", %s DESC",
keyword->string);
point++;
continue;
}
else if (keyword->column
&& (strcasecmp (keyword->column, "first") == 0))
{
if (first_return)
{
/* Subtract 1 to switch from 1 to 0 indexing. */
*first_return = atoi (keyword->string) - 1;
if (*first_return < 0)
*first_return = 0;
}
point++;
continue;
}
else if (keyword->column
&& (strcasecmp (keyword->column, "rows") == 0))
{
if (max_return)
*max_return = atoi (keyword->string);
point++;
continue;
}
else if (keyword->column
&& (strcasecmp (keyword->column, "permission") == 0))
{
if (permissions)
array_add (*permissions, g_strdup (keyword->string));
point++;
continue;
}
/* Add tag criteria to clause: tag name with optional value */
else if (keyword->column
&& (strcasecmp (keyword->column, "tag") == 0))
{
quoted_keyword = NULL;
filter_clause_append_tag (clause, keyword, type,
first_keyword, last_was_and, last_was_not);
first_keyword = 0;
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
/* Add criteria for tag_id to clause */
else if (keyword->column
&& (strcasecmp (keyword->column, "tag_id") == 0))
{
quoted_keyword = NULL;
filter_clause_append_tag_id (clause, keyword, type, first_keyword,
last_was_and, last_was_not);
first_keyword = 0;
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
/* Add SQL to the clause for each column name. */
quoted_keyword = NULL;
if (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL)
{
if (vector_find_filter (filter_columns, keyword->column) == 0)
{
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
if (keyword->column
&& (strlen (keyword->column) > 3)
&& (strcmp (keyword->column + strlen (keyword->column) - 3, "_id")
== 0)
&& strcasecmp (keyword->column, "nvt_id")
/* Tickets have a custom result_id column. */
&& strcasecmp (keyword->column, "result_id"))
{
gchar *type_term;
type_term = g_strndup (keyword->column,
strlen (keyword->column) - 3);
if (valid_type (type_term) == 0)
{
g_free (type_term);
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
quoted_keyword = sql_quote (keyword->string);
if (strcmp (quoted_keyword, ""))
g_string_append_printf (clause,
"%s(((SELECT id FROM %ss"
" WHERE %ss.uuid = '%s')"
" = %ss.%s"
" OR %ss.%s IS NULL"
" OR %ss.%s = 0)",
get_join (first_keyword,
last_was_and,
last_was_not),
type_term,
type_term,
quoted_keyword,
type,
type_term,
type,
type_term,
type,
type_term);
else
g_string_append_printf (clause,
"%s((%ss.%s IS NULL"
" OR %ss.%s = 0)",
get_join (first_keyword,
last_was_and,
last_was_not),
type,
type_term,
type,
type_term);
g_free (type_term);
}
else if (keyword->column && strcmp (keyword->column, "owner"))
{
gchar *column;
keyword_type_t column_type;
quoted_keyword = sql_quote (keyword->string);
column = columns_select_column_with_type (select_columns,
where_columns,
keyword->column,
&column_type);
assert (column);
if (keyword->type == KEYWORD_TYPE_INTEGER
&& (column_type == KEYWORD_TYPE_INTEGER
|| column_type == KEYWORD_TYPE_DOUBLE))
g_string_append_printf (clause,
"%s(CAST (%s AS NUMERIC) = %i",
get_join (first_keyword, last_was_and,
last_was_not),
column,
keyword->integer_value);
else if (keyword->type == KEYWORD_TYPE_DOUBLE
&& (column_type == KEYWORD_TYPE_DOUBLE
|| column_type == KEYWORD_TYPE_INTEGER))
g_string_append_printf (clause,
"%s(CAST (%s AS REAL)"
" = CAST (%f AS REAL)",
get_join (first_keyword, last_was_and,
last_was_not),
column,
keyword->double_value);
else if (strcmp (quoted_keyword, ""))
g_string_append_printf (clause,
"%s(CAST (%s AS TEXT) = '%s'",
get_join (first_keyword, last_was_and,
last_was_not),
column,
quoted_keyword);
else
g_string_append_printf (clause,
"%s((%s IS NULL OR CAST (%s AS TEXT) = '%s')",
get_join (first_keyword, last_was_and,
last_was_not),
column,
column,
quoted_keyword);
}
else
{
/* Skip term. Owner filtering is done via where_owned. */
skip = 1;
if (owner_filter && (*owner_filter == NULL))
*owner_filter = g_strdup (keyword->string);
}
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_APPROX)
{
gchar *column;
if (vector_find_filter (filter_columns, keyword->column) == 0)
{
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
quoted_keyword = sql_quote (keyword->string);
column = columns_select_column (select_columns,
where_columns,
keyword->column);
assert (column);
g_string_append_printf (clause,
"%s(CAST (%s AS TEXT) %s '%%%%%s%%%%'",
get_join (first_keyword, last_was_and,
last_was_not),
column,
sql_ilike_op (),
quoted_keyword);
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_ABOVE)
{
gchar *column;
keyword_type_t column_type;
if (vector_find_filter (filter_columns, keyword->column) == 0)
{
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
quoted_keyword = sql_quote (keyword->string);
column = columns_select_column_with_type (select_columns,
where_columns,
keyword->column,
&column_type);
assert (column);
if (keyword->type == KEYWORD_TYPE_INTEGER
&& (column_type == KEYWORD_TYPE_INTEGER
|| column_type == KEYWORD_TYPE_DOUBLE))
g_string_append_printf (clause,
"%s(CAST (%s AS NUMERIC) > %i",
get_join (first_keyword, last_was_and,
last_was_not),
column,
keyword->integer_value);
else if (keyword->type == KEYWORD_TYPE_DOUBLE
&& (column_type == KEYWORD_TYPE_DOUBLE
|| column_type == KEYWORD_TYPE_INTEGER))
g_string_append_printf (clause,
"%s(CAST (%s AS REAL)"
" > CAST (%f AS REAL)",
get_join (first_keyword, last_was_and,
last_was_not),
column,
keyword->double_value);
else
g_string_append_printf (clause,
"%s(CAST (%s AS TEXT) > '%s'",
get_join (first_keyword, last_was_and,
last_was_not),
column,
quoted_keyword);
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_BELOW)
{
gchar *column;
keyword_type_t column_type;
if (vector_find_filter (filter_columns, keyword->column) == 0)
{
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
quoted_keyword = sql_quote (keyword->string);
column = columns_select_column_with_type (select_columns,
where_columns,
keyword->column,
&column_type);
assert (column);
if (keyword->type == KEYWORD_TYPE_INTEGER
&& (column_type == KEYWORD_TYPE_INTEGER
|| column_type == KEYWORD_TYPE_DOUBLE))
g_string_append_printf (clause,
"%s(CAST (%s AS NUMERIC) < %i",
get_join (first_keyword, last_was_and,
last_was_not),
column,
keyword->integer_value);
else if (keyword->type == KEYWORD_TYPE_DOUBLE
&& (column_type == KEYWORD_TYPE_DOUBLE
|| column_type == KEYWORD_TYPE_INTEGER))
g_string_append_printf (clause,
"%s(CAST (%s AS REAL)"
" < CAST (%f AS REAL)",
get_join (first_keyword, last_was_and,
last_was_not),
column,
keyword->double_value);
else
g_string_append_printf (clause,
"%s(CAST (%s AS TEXT) < '%s'",
get_join (first_keyword, last_was_and,
last_was_not),
column,
quoted_keyword);
}
else if (keyword->relation == KEYWORD_RELATION_COLUMN_REGEXP)
{
gchar *column;
if (vector_find_filter (filter_columns, keyword->column) == 0)
{
last_was_and = 0;
last_was_not = 0;
point++;
continue;
}
quoted_keyword = sql_quote (keyword->string);
column = columns_select_column (select_columns,
where_columns,
keyword->column);
assert (column);
g_string_append_printf (clause,
"%s(CAST (%s AS TEXT) %s '%s'",
get_join (first_keyword, last_was_and,
last_was_not),
column,
sql_regexp_op (),
quoted_keyword);
}
else if (keyword->equal)
{
const char *filter_column;
/* Keyword like "=example". */
g_string_append_printf (clause,
"%s(",
(first_keyword
? ""
: (last_was_and ? " AND " : " OR ")));
quoted_keyword = sql_quote (keyword->string);
if (last_was_not)
for (index = 0;
(filter_column = filter_columns[index]) != NULL;
index++)
{
gchar *select_column;
keyword_type_t column_type;
select_column = columns_select_column_with_type (select_columns,
where_columns,
filter_column,
&column_type);
assert (select_column);
if (keyword->type == KEYWORD_TYPE_INTEGER
&& (column_type == KEYWORD_TYPE_INTEGER
|| column_type == KEYWORD_TYPE_DOUBLE))
g_string_append_printf (clause,
"%s"
"(%s IS NULL"
" OR CAST (%s AS NUMERIC)"
" != %i)",
(index ? " AND " : ""),
select_column,
select_column,
keyword->integer_value);
else if (keyword->type == KEYWORD_TYPE_DOUBLE
&& (column_type == KEYWORD_TYPE_DOUBLE
|| column_type == KEYWORD_TYPE_INTEGER))
g_string_append_printf (clause,
"%s"
"(%s IS NULL"
" OR CAST (%s AS REAL)"
" != CAST (%f AS REAL))",
(index ? " AND " : ""),
select_column,
select_column,
keyword->double_value);
else
g_string_append_printf (clause,
"%s"
"(%s IS NULL"
" OR CAST (%s AS TEXT)"
" != '%s')",
(index ? " AND " : ""),
select_column,
select_column,
quoted_keyword);
}
else
for (index = 0;
(filter_column = filter_columns[index]) != NULL;
index++)
{
gchar *select_column;
keyword_type_t column_type;
select_column = columns_select_column_with_type (select_columns,
where_columns,
filter_column,
&column_type);
assert (select_column);
if (keyword->type == KEYWORD_TYPE_INTEGER
&& (column_type == KEYWORD_TYPE_INTEGER
|| column_type == KEYWORD_TYPE_DOUBLE))
g_string_append_printf (clause,
"%sCAST (%s AS NUMERIC)"
" = %i",
(index ? " OR " : ""),
select_column,
keyword->integer_value);
else if (keyword->type == KEYWORD_TYPE_DOUBLE
&& (column_type == KEYWORD_TYPE_DOUBLE
|| column_type == KEYWORD_TYPE_INTEGER))
g_string_append_printf (clause,
"%sCAST (%s AS REAL)"
" = CAST (%f AS REAL)",
(index ? " OR " : ""),
select_column,
keyword->double_value);
else
g_string_append_printf (clause,
"%sCAST (%s AS TEXT)"
" = '%s'",
(index ? " OR " : ""),
select_column,
quoted_keyword);
}
}
else
{
const char *filter_column;
g_string_append_printf (clause,
"%s(",
(first_keyword
? ""
: (last_was_and ? " AND " : " OR ")));
quoted_keyword = sql_quote (keyword->string);
if (last_was_not)
for (index = 0;
(filter_column = filter_columns[index]) != NULL;
index++)
{
gchar *select_column;
keyword_type_t column_type;
int column_type_matches = 0;
select_column = columns_select_column_with_type (select_columns,
where_columns,
filter_column,
&column_type);
if (column_type != KEYWORD_TYPE_INTEGER
&& column_type != KEYWORD_TYPE_DOUBLE)
column_type_matches = 1;
if (keyword_applies_to_column (keyword, filter_column)
&& select_column && column_type_matches)
{
if (last_was_re)
g_string_append_printf (clause,
"%s"
"(%s IS NULL"
" OR NOT (CAST (%s AS TEXT)"
" %s '%s'))",
(index ? " AND " : ""),
select_column,
select_column,
sql_regexp_op (),
quoted_keyword);
else
g_string_append_printf (clause,
"%s"
"(%s IS NULL"
" OR CAST (%s AS TEXT)"
" NOT %s '%%%s%%')",
(index ? " AND " : ""),
select_column,
select_column,
sql_ilike_op (),
quoted_keyword);
}
else
g_string_append_printf (clause,
"%s t ()",
(index ? " AND " : ""));
}
else
for (index = 0;
(filter_column = filter_columns[index]) != NULL;
index++)
{
gchar *select_column;
keyword_type_t column_type;
int column_type_matches = 0;
select_column = columns_select_column_with_type (select_columns,
where_columns,
filter_column,
&column_type);
if (column_type != KEYWORD_TYPE_INTEGER
&& column_type != KEYWORD_TYPE_DOUBLE)
column_type_matches = 1;
if (keyword_applies_to_column (keyword, filter_column)
&& select_column && column_type_matches)
g_string_append_printf (clause,
"%sCAST (%s AS TEXT)"
" %s '%s%s%s'",
(index ? " OR " : ""),
select_column,
last_was_re
? sql_regexp_op ()
: sql_ilike_op (),
last_was_re ? "" : "%%",
quoted_keyword,
last_was_re ? "" : "%%");
else
g_string_append_printf (clause,
"%snot t ()",
(index ? " OR " : ""));
}
}
if (skip == 0)
{
g_string_append (clause, ")");
first_keyword = 0;
last_was_and = 0;
last_was_not = 0;
last_was_re = 0;
}
g_free (quoted_keyword);
point++;
}
filter_free (split);
if (order_return)
*order_return = g_string_free (order, FALSE);
else
g_string_free (order, TRUE);
if (max_return)
{
if (*max_return == -2)
setting_value_int (SETTING_UUID_ROWS_PER_PAGE, max_return);
else if (*max_return < 1)
*max_return = -1;
*max_return = manage_max_rows (*max_return);
}
if (strlen (clause->str))
return g_string_free (clause, FALSE);
g_string_free (clause, TRUE);
return NULL;
}
/* Resources. */
/**
* @brief Check whether a resource type name is valid.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
int
valid_type (const char* type)
{
return (strcasecmp (type, "alert") == 0)
|| (strcasecmp (type, "asset") == 0)
|| (strcasecmp (type, "config") == 0)
|| (strcasecmp (type, "credential") == 0)
|| (strcasecmp (type, "filter") == 0)
|| (strcasecmp (type, "group") == 0)
|| (strcasecmp (type, "host") == 0)
|| (strcasecmp (type, "info") == 0)
|| (strcasecmp (type, "note") == 0)
|| (strcasecmp (type, "os") == 0)
|| (strcasecmp (type, "override") == 0)
|| (strcasecmp (type, "permission") == 0)
|| (strcasecmp (type, "port_list") == 0)
|| (strcasecmp (type, "report") == 0)
|| (strcasecmp (type, "report_format") == 0)
|| (strcasecmp (type, "result") == 0)
|| (strcasecmp (type, "role") == 0)
|| (strcasecmp (type, "scanner") == 0)
|| (strcasecmp (type, "schedule") == 0)
|| (strcasecmp (type, "tag") == 0)
|| (strcasecmp (type, "target") == 0)
|| (strcasecmp (type, "task") == 0)
|| (strcasecmp (type, "ticket") == 0)
|| (strcasecmp (type, "tls_certificate") == 0)
|| (strcasecmp (type, "user") == 0)
|| (strcasecmp (type, "vuln") == 0);
}
/**
* @brief Return DB name of type.
*
* @param[in] type Database or pretty name.
*
* @return Database name of type if possible, else NULL.
*/
static const char *
type_db_name (const char* type)
{
if (type == NULL)
return NULL;
if (valid_type (type))
return type;
if (strcasecmp (type, "Alert") == 0)
return "alert";
if (strcasecmp (type, "Asset") == 0)
return "asset";
if (strcasecmp (type, "Config") == 0)
return "config";
if (strcasecmp (type, "Credential") == 0)
return "credential";
if (strcasecmp (type, "Filter") == 0)
return "filter";
if (strcasecmp (type, "Note") == 0)
return "note";
if (strcasecmp (type, "Override") == 0)
return "override";
if (strcasecmp (type, "Permission") == 0)
return "permission";
if (strcasecmp (type, "Port List") == 0)
return "port_list";
if (strcasecmp (type, "Report") == 0)
return "report";
if (strcasecmp (type, "Report Format") == 0)
return "report_format";
if (strcasecmp (type, "Result") == 0)
return "result";
if (strcasecmp (type, "Role") == 0)
return "role";
if (strcasecmp (type, "Scanner") == 0)
return "scanner";
if (strcasecmp (type, "Schedule") == 0)
return "schedule";
if (strcasecmp (type, "Tag") == 0)
return "tag";
if (strcasecmp (type, "Target") == 0)
return "target";
if (strcasecmp (type, "Task") == 0)
return "task";
if (strcasecmp (type, "Ticket") == 0)
return "ticket";
if (strcasecmp (type, "TLS Certificate") == 0)
return "tls_certificate";
if (strcasecmp (type, "SecInfo") == 0)
return "info";
return NULL;
}
/**
* @brief Check whether a resource type is an asset subtype.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_is_asset_subtype (const char *type)
{
return (strcasecmp (type, "host")
&& strcasecmp (type, "os"))
== 0;
}
/**
* @brief Check whether a resource type is an info subtype.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_is_info_subtype (const char *type)
{
return (strcasecmp (type, "nvt")
&& strcasecmp (type, "cve")
&& strcasecmp (type, "cpe")
&& strcasecmp (type, "ovaldef")
&& strcasecmp (type, "cert_bund_adv")
&& strcasecmp (type, "dfn_cert_adv"))
== 0;
}
/**
* @brief Check whether a type has a name and comment.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_named (const char *type)
{
return strcasecmp (type, "note")
&& strcasecmp (type, "override");
}
/**
* @brief Check whether a type must have globally unique names.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_globally_unique (const char *type)
{
if (strcasecmp (type, "user") == 0)
return 1;
else
return 0;
}
/**
* @brief Check whether a type has a comment.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_has_comment (const char *type)
{
return strcasecmp (type, "report_format");
}
/**
* @brief Check whether a resource type uses the trashcan.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_has_trash (const char *type)
{
return strcasecmp (type, "report")
&& strcasecmp (type, "result")
&& strcasecmp (type, "info")
&& type_is_info_subtype (type) == 0
&& strcasecmp (type, "vuln")
&& strcasecmp (type, "user")
&& strcasecmp (type, "tls_certificate");
}
/**
* @brief Check whether a resource type has an owner.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_owned (const char* type)
{
return strcasecmp (type, "info")
&& type_is_info_subtype (type) == 0
&& strcasecmp (type, "vuln");
}
/**
* @brief Check whether the trash is in the real table.
*
* @param[in] type Type of resource.
*
* @return 1 yes, 0 no.
*/
static int
type_trash_in_table (const char *type)
{
return strcasecmp (type, "task") == 0;
}
/* TODO Only used by find_scanner, find_permission and check_permission_args. */
/**
* @brief Find a resource given a UUID.
*
* This only looks for resources owned (or effectively owned) by the current user.
* So no shared resources and no globals.
*
* @param[in] type Type of resource.
* @param[in] uuid UUID of resource.
* @param[out] resource Resource return, 0 if successfully failed to find resource.
*
* @return FALSE on success (including if failed to find resource), TRUE on error.
*/
gboolean
find_resource (const char* type, const char* uuid, resource_t* resource)
{
gchar *quoted_uuid;
quoted_uuid = sql_quote (uuid);
if (acl_user_owns_uuid (type, quoted_uuid, 0) == 0)
{
g_free (quoted_uuid);
*resource = 0;
return FALSE;
}
// TODO should really check type
switch (sql_int64 (resource,
"SELECT id FROM %ss WHERE uuid = '%s'%s;",
type,
quoted_uuid,
strcmp (type, "task") ? "" : " AND hidden < 2"))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*resource = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_uuid);
return TRUE;
break;
}
g_free (quoted_uuid);
return FALSE;
}
/**
* @brief Find a resource given a UUID and a permission.
*
* @param[in] type Type of resource.
* @param[in] uuid UUID of resource.
* @param[out] resource Resource return, 0 if successfully failed to find
* resource.
* @param[in] permission Permission.
* @param[in] trash Whether resource is in trashcan.
*
* @return FALSE on success (including if failed to find resource), TRUE on
* error.
*/
gboolean
find_resource_with_permission (const char* type, const char* uuid,
resource_t* resource, const char *permission,
int trash)
{
gchar *quoted_uuid;
if (uuid == NULL)
return TRUE;
if ((type == NULL) || (valid_db_resource_type (type) == 0))
return TRUE;
quoted_uuid = sql_quote (uuid);
if (acl_user_has_access_uuid (type, quoted_uuid, permission, trash) == 0)
{
g_free (quoted_uuid);
*resource = 0;
return FALSE;
}
switch (sql_int64 (resource,
"SELECT id FROM %ss%s WHERE uuid = '%s'%s%s;",
type,
(trash && strcmp (type, "task") && strcmp (type, "report"))
? "_trash"
: "",
quoted_uuid,
strcmp (type, "task")
? ""
: (trash ? " AND hidden = 2" : " AND hidden < 2"),
strcmp (type, "report")
? ""
: (trash
? " AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 2"
: " AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 0")))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*resource = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_uuid);
return TRUE;
break;
}
g_free (quoted_uuid);
return FALSE;
}
/**
* @brief Find a resource given a name.
*
* @param[in] type Type of resource.
* @param[in] name A resource name.
* @param[out] resource Resource return, 0 if successfully failed to find
* resource.
*
* @return FALSE on success (including if failed to find resource), TRUE on
* error.
*/
static gboolean
find_resource_by_name (const char* type, const char* name, resource_t *resource)
{
gchar *quoted_name;
quoted_name = sql_quote (name);
// TODO should really check type
switch (sql_int64 (resource,
"SELECT id FROM %ss WHERE name = '%s'"
" ORDER BY id DESC;",
type,
quoted_name))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*resource = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_name);
return TRUE;
break;
}
g_free (quoted_name);
return FALSE;
}
/**
* @brief Find a resource given a UUID and a permission.
*
* @param[in] type Type of resource.
* @param[in] name Name of resource.
* @param[out] resource Resource return, 0 if successfully failed to find
* resource.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find resource), TRUE on
* error.
*/
static gboolean
find_resource_by_name_with_permission (const char *type, const char *name,
resource_t *resource,
const char *permission)
{
gchar *quoted_name;
assert (strcmp (type, "task"));
if (name == NULL)
return TRUE;
quoted_name = sql_quote (name);
// TODO should really check type
switch (sql_int64 (resource,
"SELECT id FROM %ss WHERE name = '%s'"
" ORDER BY id DESC;",
type,
quoted_name))
{
case 0:
{
gchar *uuid;
uuid = sql_string ("SELECT uuid FROM %ss WHERE id = %llu;",
type, *resource);
if (acl_user_has_access_uuid (type, uuid, permission, 0) == 0)
{
g_free (uuid);
g_free (quoted_name);
*resource = 0;
return FALSE;
}
g_free (uuid);
}
break;
case 1: /* Too few rows in result of query. */
*resource = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_name);
return TRUE;
break;
}
g_free (quoted_name);
return FALSE;
}
/**
* @brief Create a resource from an existing resource.
*
* @param[in] type Type of resource.
* @param[in] name Name of new resource. NULL to copy from existing.
* @param[in] comment Comment on new resource. NULL to copy from existing.
* @param[in] resource_id UUID of existing resource.
* @param[in] columns Extra columns in resource.
* @param[in] make_name_unique When name NULL, whether to make existing name
* unique.
* @param[out] new_resource Address for new resource, or NULL.
* @param[out] old_resource Address for existing resource, or NULL.
*
* @return 0 success, 1 resource exists already, 2 failed to find existing
* resource, 99 permission denied, -1 error.
*/
int
copy_resource_lock (const char *type, const char *name, const char *comment,
const char *resource_id, const char *columns,
int make_name_unique, resource_t* new_resource,
resource_t *old_resource)
{
gchar *quoted_name, *quoted_uuid, *uniquify, *command;
int named, globally_unique;
user_t owner;
resource_t resource;
resource_t new;
int ret = -1;
if (resource_id == NULL)
return -1;
command = g_strdup_printf ("create_%s", type);
if (acl_user_may (command) == 0)
{
g_free (command);
return 99;
}
g_free (command);
command = g_strdup_printf ("get_%ss", type);
if (find_resource_with_permission (type, resource_id, &resource, command, 0))
{
g_free (command);
return -1;
}
g_free (command);
if (resource == 0)
return 2;
if (find_user_by_name (current_credentials.username, &owner)
|| owner == 0)
{
return -1;
}
if (strcmp (type, "permission") == 0)
{
resource_t perm_resource;
perm_resource = permission_resource (resource);
if ((perm_resource == 0)
&& (acl_user_can_everything (current_credentials.uuid) == 0))
/* Only admins can copy permissions that apply to whole commands. */
return 99;
}
named = type_named (type);
globally_unique = type_globally_unique (type);
if (named && name && *name && resource_with_name_exists (name, type, 0))
return 1;
if ((strcmp (type, "tls_certificate") == 0)
&& user_has_tls_certificate (resource, owner))
return 1;
if (name && *name)
quoted_name = sql_quote (name);
else
quoted_name = NULL;
quoted_uuid = sql_quote (resource_id);
/* Copy the existing resource. */
if (globally_unique && make_name_unique)
uniquify = g_strdup_printf ("uniquify ('%s', name, NULL, '%cClone')",
type,
strcmp (type, "user") ? ' ' : '_');
else if (make_name_unique)
uniquify = g_strdup_printf ("uniquify ('%s', name, %llu, ' Clone')",
type,
owner);
else
uniquify = g_strdup ("name");
if (named && comment && strlen (comment))
{
gchar *quoted_comment;
quoted_comment = sql_nquote (comment, strlen (comment));
ret = sql_error ("INSERT INTO %ss"
" (uuid, owner, name, comment,"
" creation_time, modification_time%s%s)"
" SELECT make_uuid (),"
" (SELECT id FROM users"
" where users.uuid = '%s'),"
" %s%s%s, '%s', m_now (), m_now ()%s%s"
" FROM %ss WHERE uuid = '%s';",
type,
columns ? ", " : "",
columns ? columns : "",
current_credentials.uuid,
quoted_name ? "'" : "",
quoted_name ? quoted_name : uniquify,
quoted_name ? "'" : "",
quoted_comment,
columns ? ", " : "",
columns ? columns : "",
type,
quoted_uuid);
g_free (quoted_comment);
}
else if (named)
ret = sql_error ("INSERT INTO %ss"
" (uuid, owner, name%s,"
" creation_time, modification_time%s%s)"
" SELECT make_uuid (),"
" (SELECT id FROM users where users.uuid = '%s'),"
" %s%s%s%s, m_now (), m_now ()%s%s"
" FROM %ss WHERE uuid = '%s';",
type,
type_has_comment (type) ? ", comment" : "",
columns ? ", " : "",
columns ? columns : "",
current_credentials.uuid,
quoted_name ? "'" : "",
quoted_name ? quoted_name : uniquify,
quoted_name ? "'" : "",
type_has_comment (type) ? ", comment" : "",
columns ? ", " : "",
columns ? columns : "",
type,
quoted_uuid);
else
ret = sql_error ("INSERT INTO %ss"
" (uuid, owner, creation_time, modification_time%s%s)"
" SELECT make_uuid (),"
" (SELECT id FROM users where users.uuid = '%s'),"
" m_now (), m_now ()%s%s"
" FROM %ss WHERE uuid = '%s';",
type,
columns ? ", " : "",
columns ? columns : "",
current_credentials.uuid,
columns ? ", " : "",
columns ? columns : "",
type,
quoted_uuid);
if (ret == 3)
{
g_free (quoted_uuid);
g_free (quoted_name);
g_free (uniquify);
return 1;
}
else if (ret)
{
g_free (quoted_uuid);
g_free (quoted_name);
g_free (uniquify);
return -1;
}
new = sql_last_insert_id ();
/* Copy attached tags */
sql ("INSERT INTO tag_resources"
" (tag, resource_type, resource, resource_uuid, resource_location)"
" SELECT tag, resource_type, %llu,"
" (SELECT uuid FROM %ss WHERE id = %llu),"
" resource_location"
" FROM tag_resources"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TABLE) ";",
new,
type, new,
type, resource);
if (new_resource)
*new_resource = new;
if (old_resource)
*old_resource = resource;
g_free (quoted_uuid);
g_free (quoted_name);
g_free (uniquify);
if (sql_last_insert_id () == 0)
return -1;
return 0;
}
/**
* @brief Create a resource from an existing resource.
*
* @param[in] type Type of resource.
* @param[in] name Name of new resource. NULL to copy from existing.
* @param[in] comment Comment on new resource. NULL to copy from existing.
* @param[in] resource_id UUID of existing resource.
* @param[in] columns Extra columns in resource.
* @param[in] make_name_unique When name NULL, whether to make existing name
* unique.
* @param[out] new_resource New resource.
* @param[out] old_resource Address for existing resource, or NULL.
*
* @return 0 success, 1 resource exists already, 2 failed to find existing
* resource, 99 permission denied, -1 error.
*/
int
copy_resource (const char *type, const char *name, const char *comment,
const char *resource_id, const char *columns,
int make_name_unique, resource_t* new_resource,
resource_t *old_resource)
{
int ret;
assert (current_credentials.uuid);
sql_begin_immediate ();
ret = copy_resource_lock (type, name, comment, resource_id, columns,
make_name_unique, new_resource, old_resource);
if (ret)
sql_rollback ();
else
sql_commit ();
return ret;
}
/**
* @brief Get whether a resource exists.
*
* @param[in] type Type.
* @param[in] resource Resource.
* @param[in] location Location.
*
* @return 1 yes, 0 no, -1 error in type.
*/
int
resource_exists (const char *type, resource_t resource, int location)
{
if (valid_db_resource_type (type) == 0)
return -1;
if (location == LOCATION_TABLE)
return sql_int ("SELECT EXISTS (SELECT id FROM %ss WHERE id = %llu);",
type,
resource);
return sql_int ("SELECT EXISTS (SELECT id FROM %ss%s WHERE id = %llu);",
type,
strcmp (type, "task") ? "_trash" : "",
resource);
}
/**
* @brief Get the name of a resource.
*
* @param[in] type Type.
* @param[in] uuid UUID.
* @param[in] location Location.
* @param[out] name Return for freshly allocated name.
*
* @return 0 success, 1 error in type.
*/
int
resource_name (const char *type, const char *uuid, int location, char **name)
{
if (valid_db_resource_type (type) == 0)
return 1;
if (strcasecmp (type, "note") == 0)
*name = sql_string ("SELECT 'Note for: '"
" || (SELECT name"
" FROM nvts"
" WHERE nvts.uuid = notes%s.nvt)"
" FROM notes%s"
" WHERE uuid = '%s';",
location == LOCATION_TABLE ? "" : "_trash",
location == LOCATION_TABLE ? "" : "_trash",
uuid);
else if (strcasecmp (type, "override") == 0)
*name = sql_string ("SELECT 'Override for: '"
" || (SELECT name"
" FROM nvts"
" WHERE nvts.uuid = overrides%s.nvt)"
" FROM overrides%s"
" WHERE uuid = '%s';",
location == LOCATION_TABLE ? "" : "_trash",
location == LOCATION_TABLE ? "" : "_trash",
uuid);
else if (strcasecmp (type, "report") == 0)
*name = sql_string ("SELECT (SELECT name FROM tasks WHERE id = task)"
" || ' - '"
" || (SELECT"
" CASE (SELECT end_time FROM tasks"
" WHERE id = task)"
" WHEN 0 THEN 'N/A'"
" ELSE (SELECT iso_time (end_time)"
" FROM tasks WHERE id = task)"
" END)"
" FROM reports"
" WHERE uuid = '%s';",
uuid);
else if (strcasecmp (type, "result") == 0)
*name = sql_string ("SELECT (SELECT name FROM tasks WHERE id = task)"
" || ' - '"
" || (SELECT name FROM nvts WHERE oid = nvt)"
" || ' - '"
" || (SELECT"
" CASE (SELECT end_time FROM tasks"
" WHERE id = task)"
" WHEN 0 THEN 'N/A'"
" ELSE (SELECT iso_time (end_time)"
" FROM tasks WHERE id = task)"
" END)"
" FROM results"
" WHERE uuid = '%s';",
uuid);
else if (location == LOCATION_TABLE)
*name = sql_string ("SELECT name"
" FROM %ss"
" WHERE uuid = '%s';",
type,
uuid);
else if (type_has_trash (type))
*name = sql_string ("SELECT name"
" FROM %ss%s"
" WHERE uuid = '%s';",
type,
strcmp (type, "task") ? "_trash" : "",
uuid);
else
*name = NULL;
return 0;
}
/**
* @brief Get the name of a resource.
*
* @param[in] type Type.
* @param[in] uuid UUID.
* @param[out] name Return for freshly allocated name.
*
* @return 0 success, 1 error in type.
*/
int
manage_resource_name (const char *type, const char *uuid, char **name)
{
return resource_name (type, uuid, LOCATION_TABLE, name);
}
/**
* @brief Get the name of a trashcan resource.
*
* @param[in] type Type.
* @param[in] uuid UUID.
* @param[out] name Return for freshly allocated name.
*
* @return 0 success, 1 error in type.
*/
int
manage_trash_resource_name (const char *type, const char *uuid, char **name)
{
return resource_name (type, uuid, LOCATION_TRASH, name);
}
/**
* @brief Get the UUID of a resource.
*
* @param[in] type Type.
* @param[in] resource Resource.
*
* @return Freshly allocated UUID on success, else NULL.
*/
gchar *
resource_uuid (const gchar *type, resource_t resource)
{
assert (valid_db_resource_type (type));
return sql_string ("SELECT uuid FROM %ss WHERE id = %llu;",
type,
resource);
}
/**
* @brief Initialise a GET iterator, including observed resources.
*
* This version includes the extra_with arg.
*
* @param[in] iterator Iterator.
* @param[in] type Type of resource.
* @param[in] get GET data.
* @param[in] select_columns Columns for SQL.
* @param[in] trash_select_columns Columns for SQL trash case.
* @param[in] where_columns WHERE columns. These are columns that
* can be used for filtering and searching,
* but are not accessed (so column has no
* iterator access function).
* @param[in] trash_where_columns WHERE columns for trashcan.
* @param[in] filter_columns Columns for filter.
* @param[in] distinct Whether the query should be distinct. Skipped
* for trash and single resource.
* @param[in] extra_tables Extra tables to join in FROM clause.
* @param[in] extra_where Extra WHERE clauses. Skipped for single
* resource.
* @param[in] extra_where_single Extra WHERE clauses. Used for single
* resource.
* @param[in] owned Only get items owned by the current user.
* @param[in] ignore_id Whether to ignore id (e.g. for report results).
* @param[in] extra_order Extra ORDER clauses.
* @param[in] extra_with Extra WITH clauses.
* @param[in] acl_with_optional Whether default permission WITH clauses are
* optional.
* @param[in] assume_permitted Whether to skip permission checks.
*
* @return 0 success, 1 failed to find resource, 2 failed to find filter, -1
* error.
*/
static int
init_get_iterator2_with (iterator_t* iterator, const char *type,
const get_data_t *get, column_t *select_columns,
column_t *trash_select_columns,
column_t *where_columns,
column_t *trash_where_columns,
const char **filter_columns, int distinct,
const char *extra_tables,
const char *extra_where,
const char *extra_where_single, int owned,
int ignore_id,
const char *extra_order,
const char *extra_with,
int acl_with_optional,
int assume_permitted)
{
int first, max;
gchar *clause, *order, *filter, *owned_clause, *with_clause;
array_t *permissions;
resource_t resource = 0;
gchar *owner_filter;
gchar *columns;
assert (get);
if (select_columns == NULL)
{
assert (0);
return -1;
}
if (ignore_id)
{
resource = 0;
}
else if (get->id && (owned == 0 || (current_credentials.uuid == NULL)))
{
gchar *quoted_uuid = sql_quote (get->id);
switch (sql_int64 (&resource,
"SELECT id FROM %ss WHERE uuid = '%s';",
type, quoted_uuid))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
g_free (quoted_uuid);
return 1;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_uuid);
return -1;
break;
}
g_free (quoted_uuid);
}
else if (get->id && owned)
{
/* For now assume that the permission is "get_". Callers wishing
* to iterate over a single resource with other permissions can use
* uuid= in the filter (instead of passing get->id). */
const char* permission;
/* Special case: "get_assets" subtypes */
if (type_is_asset_subtype (type))
permission = "get_assets";
else
permission = NULL;
if (find_resource_with_permission (type, get->id, &resource, permission,
get->trash))
return -1;
if (resource == 0)
return 1;
}
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
if (get->filter_replacement)
/* Replace the filter term with one given by the caller. This is
* used by GET_REPORTS to use the default filter with any task (when
* given the special value of -3 in filt_id). */
filter = g_strdup (get->filter_replacement);
else
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
clause = filter_clause (type, filter ? filter : get->filter, filter_columns,
(get->trash && trash_select_columns)
? trash_select_columns
: select_columns,
(get->trash && trash_where_columns)
? trash_where_columns
: where_columns,
get->trash, &order, &first, &max, &permissions,
&owner_filter);
g_free (filter);
with_clause = NULL;
if (resource || assume_permitted)
/* Ownership test of single resources is done above by find function
* but acl_where_owned has to be called to generate WITH clause
* in case subqueries depend on it.
*/
owned_clause = acl_where_owned (type, get, 0, owner_filter, resource,
permissions, acl_with_optional,
&with_clause);
else
owned_clause = acl_where_owned (type, get, owned, owner_filter, resource,
permissions, acl_with_optional,
&with_clause);
if (extra_with)
{
if (with_clause)
{
gchar *old_with;
old_with = with_clause;
with_clause = g_strdup_printf ("%s, %s", old_with, extra_with);
g_free (old_with);
}
else
with_clause = g_strdup_printf ("WITH %s", extra_with);
}
g_free (owner_filter);
array_free (permissions);
if (get->trash && trash_select_columns)
columns = columns_build_select (trash_select_columns);
else
columns = columns_build_select (select_columns);
if (get->ignore_pagination
&& ((strcmp (type, "host") == 0)
|| (strcmp (type, "os") == 0)
|| (strcmp (type, "task") == 0)
|| (strcmp (type, "report") == 0)
|| (strcmp (type, "result") == 0)))
{
first = 0;
max = -1;
}
if (strlen (order) == 0)
{
g_free (order);
order = NULL;
}
if (resource && get->trash)
init_iterator (iterator,
"%sSELECT %s"
" FROM %ss%s %s"
" WHERE %ss.id = %llu"
" AND %s%s"
"%s%s;",
with_clause ? with_clause : "",
columns,
type,
type_trash_in_table (type) ? "" : "_trash",
extra_tables ? extra_tables : "",
type,
resource,
owned_clause,
extra_where_single ? extra_where_single : "",
order ? order : "",
order ? (extra_order ? extra_order : "") : "");
else if (get->trash)
init_iterator (iterator,
"%sSELECT %s"
" FROM %ss%s %s"
" WHERE"
"%s"
"%s"
"%s%s;",
with_clause ? with_clause : "",
columns,
type,
type_trash_in_table (type) ? "" : "_trash",
extra_tables ? extra_tables : "",
owned_clause,
extra_where ? extra_where : "",
order ? order : "",
order ? (extra_order ? extra_order : "") : "");
else if (resource)
init_iterator (iterator,
"%sSELECT %s"
" FROM %ss %s"
" WHERE %ss.id = %llu"
" AND %s%s"
"%s%s;",
with_clause ? with_clause : "",
columns,
type,
extra_tables ? extra_tables : "",
type,
resource,
owned_clause,
extra_where_single ? extra_where_single : "",
order ? order : "",
order ? (extra_order ? extra_order : "") : "");
else
init_iterator (iterator,
"%s%sSELECT %s"
" FROM %ss %s"
" WHERE"
" %s%s%s%s%s%s%s"
" LIMIT %s OFFSET %i%s;",
with_clause ? with_clause : "",
distinct ? "SELECT DISTINCT * FROM (" : "",
columns,
type,
extra_tables ? extra_tables : "",
owned_clause,
clause ? " AND (" : "",
clause ? clause : "",
clause ? ")" : "",
extra_where ? extra_where : "",
order ? order : "",
order ? (extra_order ? extra_order : "") : "",
sql_select_limit (max),
first,
distinct ? ") AS subquery_for_distinct" : "");
g_free (columns);
g_free (with_clause);
g_free (owned_clause);
g_free (order);
g_free (clause);
return 0;
}
/**
* @brief Initialise a GET iterator, including observed resources.
*
* @param[in] iterator Iterator.
* @param[in] type Type of resource.
* @param[in] get GET data.
* @param[in] select_columns Columns for SQL.
* @param[in] trash_select_columns Columns for SQL trash case.
* @param[in] where_columns WHERE columns. These are columns that
* can be used for filtering and searching,
* but are not accessed (so column has no
* iterator access function).
* @param[in] trash_where_columns WHERE columns for trashcan.
* @param[in] filter_columns Columns for filter.
* @param[in] distinct Whether the query should be distinct. Skipped
* for trash and single resource.
* @param[in] extra_tables Extra tables to join in FROM clause.
* @param[in] extra_where Extra WHERE clauses. Skipped for single
* resource.
* @param[in] extra_where_single Extra WHERE clauses. Used for single
* resource.
* @param[in] owned Only get items owned by the current user.
* @param[in] ignore_id Whether to ignore id (e.g. for report results).
* @param[in] extra_order Extra ORDER clauses.
*
* @return 0 success, 1 failed to find resource, 2 failed to find filter, -1
* error.
*/
static int
init_get_iterator2 (iterator_t* iterator, const char *type,
const get_data_t *get, column_t *select_columns,
column_t *trash_select_columns,
column_t *where_columns,
column_t *trash_where_columns,
const char **filter_columns, int distinct,
const char *extra_tables,
const char *extra_where, const char *extra_where_single,
int owned, int ignore_id,
const char *extra_order)
{
return init_get_iterator2_with (iterator, type, get, select_columns,
trash_select_columns, where_columns,
trash_where_columns, filter_columns, distinct,
extra_tables, extra_where, extra_where_single,
owned, ignore_id, extra_order, NULL, 0, 0);
}
/**
* @brief Initialise a GET iterator, including observed resources.
*
* @param[in] iterator Iterator.
* @param[in] type Type of resource.
* @param[in] get GET data.
* @param[in] select_columns Columns for SQL.
* @param[in] trash_select_columns Columns for SQL trash case.
* @param[in] filter_columns Columns for filter.
* @param[in] distinct Whether the query should be distinct. Skipped
* for trash and single resource.
* @param[in] extra_tables Extra tables to join in FROM clause.
* @param[in] extra_where Extra WHERE clauses. Skipped for single
* resource.
* @param[in] owned Only get items owned by the current user.
*
* @return 0 success, 1 failed to find resource, 2 failed to find filter, -1
* error.
*/
int
init_get_iterator (iterator_t* iterator, const char *type,
const get_data_t *get, column_t *select_columns,
column_t *trash_select_columns,
const char **filter_columns, int distinct,
const char *extra_tables,
const char *extra_where, int owned)
{
return init_get_iterator2 (iterator, type, get, select_columns,
trash_select_columns, NULL, NULL, filter_columns,
distinct, extra_tables, extra_where, NULL, owned,
FALSE, NULL);
}
/**
* @brief Append expression for a column to an array.
*
* @param[in] columns Array.
* @param[in] column_name Name of column.
* @param[in] select_columns Definition of "SELECT" columns.
* @param[in] where_columns Definition of "WHERE" columns.
*/
static void
append_column (GArray *columns, const gchar *column_name,
column_t *select_columns, column_t *where_columns)
{
int i = 0;
while (select_columns[i].select != NULL)
{
gchar *select = NULL;
if (strcmp (select_columns[i].select, column_name) == 0
|| (select_columns[i].filter
&& strcmp (select_columns[i].filter, column_name) == 0))
{
select = g_strdup (select_columns[i].select);
g_array_append_val (columns, select);
break;
}
i++;
}
if (select_columns[i].select == NULL && where_columns)
{
i = 0;
while (where_columns[i].select != NULL)
{
gchar *select = NULL;
if (strcmp (where_columns[i].select, column_name) == 0
|| (where_columns[i].filter
&& strcmp (where_columns[i].filter, column_name) == 0))
{
select = g_strdup (where_columns[i].select);
g_array_append_val (columns, select);
break;
}
i++;
}
}
}
/**
* @brief Initialise a GET_AGGREGATES iterator, including observed resources.
*
* @param[in] iterator Iterator.
* @param[in] type Type of resource.
* @param[in] get GET data.
* @param[in] distinct Whether the query should be distinct. Skipped
* for trash and single resource.
* @param[in] data_columns Columns to calculate statistics for.
* @param[in] group_column Column to group data by.
* @param[in] subgroup_column Column to further group data by.
* @param[in] text_columns Columns to get text from.
* @param[in] sort_data GArray of sorting data.
* @param[in] first_group Row number to start iterating from.
* @param[in] max_groups Maximum number of rows.
* @param[in] extra_tables Join tables. Skipped for trash and single
* resource.
* @param[in] given_extra_where Extra WHERE clauses. Skipped for single
* resource.
*
* @return 0 success, 1 failed to find resource, 2 failed to find filter,
* 3 invalid stat_column, 4 invalid group_column, 5 invalid type,
* 6 trashcan not used by type, 7 invalid text column, 8 invalid
* subgroup_column, -1 error.
*/
int
init_aggregate_iterator (iterator_t* iterator, const char *type,
const get_data_t *get, int distinct,
GArray *data_columns,
const char *group_column, const char *subgroup_column,
GArray *text_columns, GArray *sort_data,
int first_group, int max_groups,
const char *extra_tables,
const char *given_extra_where)
{
column_t *select_columns, *where_columns;
const char **filter_columns;
GString *aggregate_select, *outer_col_select;
gchar *aggregate_group_by;
gchar *outer_group_by_column, *outer_subgroup_column;
gchar *select_group_column, *select_subgroup_column;
GArray *select_data_columns, *select_text_columns;
GString *order_clause;
gchar *inner_select;
int build_select_ret;
assert (get);
if (get->id)
g_warning ("%s: Called with an id parameter", __func__);
if ((manage_scap_loaded () == FALSE
&& (strcmp (type, "cve") == 0
|| strcmp (type, "cpe") == 0
|| strcmp (type, "ovaldef") == 0))
|| (manage_cert_loaded () == FALSE
&& (strcmp (type, "cert_bund_adv") == 0
|| strcmp (type, "dfn_cert_adv") == 0)))
{
// Init a dummy iterator if SCAP or CERT DB is required but unavailable.
init_iterator (iterator,
"SELECT NULL LIMIT %s",
sql_select_limit (0));
return 0;
}
select_columns = type_select_columns (type);
where_columns = type_where_columns (type);
filter_columns = type_filter_columns (type);
if (filter_columns == NULL)
return 5;
if (get->trash && type_has_trash (type) == 0)
return 6;
if (data_columns && data_columns->len > 0)
{
int i;
for (i = 0; i < data_columns->len; i++)
{
if (vector_find_filter (filter_columns,
g_array_index (data_columns, gchar*, i))
== 0)
{
return 3;
}
}
}
if (text_columns && text_columns->len > 0)
{
int i;
for (i = 0; i < text_columns->len; i++)
{
if (vector_find_filter (filter_columns,
g_array_index (text_columns, gchar*, i))
== 0)
{
return 7;
}
}
}
if (group_column && vector_find_filter (filter_columns, group_column) == 0)
{
return 4;
}
if (subgroup_column
&& vector_find_filter (filter_columns, subgroup_column) == 0)
{
return 8;
}
select_data_columns = g_array_new (TRUE, TRUE, sizeof (gchar*));
select_text_columns = g_array_new (TRUE, TRUE, sizeof (gchar*));
select_group_column = NULL;
select_subgroup_column = NULL;
if (group_column)
{
select_group_column
= g_strdup (columns_select_column (select_columns,
where_columns,
group_column));
if (subgroup_column)
{
select_subgroup_column
= g_strdup (columns_select_column (select_columns,
where_columns,
subgroup_column));
}
}
if (data_columns && data_columns->len > 0)
{
int column_index;
for (column_index = 0; column_index < data_columns->len; column_index++)
append_column (select_data_columns,
g_array_index (data_columns, gchar*, column_index),
select_columns,
where_columns);
}
if (text_columns && text_columns->len > 0)
{
int column_index;
for (column_index = 0; column_index < text_columns->len; column_index++)
append_column (select_text_columns,
g_array_index (text_columns, gchar*, column_index),
select_columns,
where_columns);
}
/* Round time fields to the next day to reduce amount of rows returned
* This returns "pseudo-UTC" dates which are used by the GSA charts because
* the JavaScript Date objects do not support setting the timezone.
*/
if (column_is_timestamp (group_column))
outer_group_by_column
= g_strdup_printf ("EXTRACT (EPOCH FROM"
" date_trunc ('day',"
" TIMESTAMP WITH TIME ZONE 'epoch'"
" + (%s) * INTERVAL '1 second'))"
" :: integer",
"aggregate_group_value");
else
outer_group_by_column = g_strdup ("aggregate_group_value");
if (column_is_timestamp (subgroup_column))
outer_subgroup_column
= g_strdup_printf ("EXTRACT (EPOCH FROM"
" date_trunc ('day',"
" TIMESTAMP WITH TIME ZONE 'epoch'"
" + (%s) * INTERVAL '1 second'))"
" :: integer",
"aggregate_subgroup_value");
else
outer_subgroup_column = g_strdup ("aggregate_subgroup_value");
if (sort_data)
{
order_clause = g_string_new ("ORDER BY");
int sort_index;
for (sort_index = 0; sort_index < sort_data->len; sort_index++) {
sort_data_t *sort_data_item;
const gchar *sort_field, *sort_stat;
int sort_order;
gchar *order_column;
sort_data_item = g_array_index (sort_data, sort_data_t*, sort_index);
sort_field = sort_data_item->field;
sort_stat = sort_data_item->stat;
sort_order = sort_data_item->order;
if (sort_stat && strcmp (sort_stat, "count") == 0)
order_column = g_strdup ("outer_count");
else if (sort_stat && strcmp (sort_stat, "value") == 0)
order_column = g_strdup ("outer_group_column");
else if (sort_field
&& group_column
&& strcmp (sort_field, "")
&& strcmp (sort_field, group_column)
&& (subgroup_column == NULL
|| strcmp (sort_field, subgroup_column)))
{
int index;
order_column = NULL;
for (index = 0;
data_columns && index < data_columns->len && order_column == NULL;
index++)
{
gchar *column = g_array_index (data_columns, gchar*, index);
if (strcmp (column, sort_field) == 0)
{
if (sort_stat == NULL || strcmp (sort_stat, "") == 0
|| ( strcmp (sort_stat, "min")
&& strcmp (sort_stat, "max")
&& strcmp (sort_stat, "mean")
&& strcmp (sort_stat, "sum")))
order_column = g_strdup_printf ("max (aggregate_max_%d)",
index);
else if (strcmp (sort_stat, "mean") == 0)
order_column = g_strdup_printf ("sum (aggregate_sum_%d)"
" / sum(aggregate_count)",
index);
else
order_column = g_strdup_printf ("%s (aggregate_%s_%d)",
sort_stat, sort_stat,
index);
}
}
for (index = 0;
text_columns && index < text_columns->len && order_column == NULL;
index++)
{
gchar *column = g_array_index (text_columns, gchar*, index);
if (strcmp (column, sort_field) == 0)
{
order_column = g_strdup_printf ("max (text_column_%d)",
index);
}
}
}
else if (sort_field && subgroup_column
&& strcmp (sort_field, subgroup_column) == 0)
order_column = g_strdup ("outer_subgroup_column");
else
order_column = g_strdup ("outer_group_column");
if (subgroup_column && sort_index == 0)
{
xml_string_append (order_clause,
" outer_group_column %s, %s %s",
sort_order ? "ASC" : "DESC",
order_column,
sort_order ? "ASC" : "DESC");
}
else
{
xml_string_append (order_clause,
"%s %s %s",
sort_index > 0 ? "," : "",
order_column,
sort_order ? "ASC" : "DESC");
}
g_free (order_column);
}
if (sort_data->len == 0)
g_string_append (order_clause, " outer_group_column ASC");
}
else
order_clause = g_string_new ("");
aggregate_select = g_string_new ("");
outer_col_select = g_string_new ("");
if (group_column && strcmp (group_column, ""))
{
if (subgroup_column && strcmp (subgroup_column, ""))
{
g_string_append_printf (aggregate_select,
" count(*) AS aggregate_count,"
" %s AS aggregate_group_value,"
" %s AS aggregate_subgroup_value",
select_group_column,
select_subgroup_column);
aggregate_group_by = g_strdup_printf ("%s, %s",
select_group_column,
select_subgroup_column);
}
else
{
g_string_append_printf (aggregate_select,
" count(*) AS aggregate_count,"
" %s AS aggregate_group_value,"
" CAST (NULL AS TEXT) AS aggregate_subgroup_value",
select_group_column);
aggregate_group_by = g_strdup (select_group_column);
}
}
else
{
g_string_append_printf (aggregate_select,
" count(*) AS aggregate_count,"
" CAST (NULL AS TEXT) AS aggregate_group_value,"
" CAST (NULL AS TEXT) AS aggregate_subgroup_value");
aggregate_group_by = NULL;
}
int col_index;
for (col_index = 0; col_index < select_data_columns->len; col_index ++)
{
gchar *select_data_column = g_array_index (select_data_columns, gchar*,
col_index);
// TODO: Test type of column (string, number, timestamp)
g_string_append_printf (aggregate_select,
","
" min(CAST (%s AS real)) AS aggregate_min_%d,"
" max(CAST (%s AS real)) AS aggregate_max_%d,"
" avg(CAST (%s AS real)) * count(*)"
" AS aggregate_avg_%d,"
" sum(CAST (%s AS real))"
" AS aggregate_sum_%d",
select_data_column,
col_index,
select_data_column,
col_index,
select_data_column,
col_index,
select_data_column,
col_index);
g_string_append_printf (outer_col_select,
", min(aggregate_min_%d),"
" max (aggregate_max_%d),"
" sum (aggregate_avg_%d) / sum(aggregate_count),"
" sum (aggregate_sum_%d)",
col_index, col_index, col_index, col_index);
}
for (col_index = 0; col_index < select_text_columns->len; col_index ++)
{
gchar *select_text_column = g_array_index (select_text_columns, gchar*,
col_index);
g_string_append_printf (aggregate_select,
", max (%s) as text_column_%d",
select_text_column,
col_index);
g_string_append_printf (outer_col_select,
", max (text_column_%d)",
col_index);
}
inner_select = NULL;
build_select_ret = type_build_select (type, aggregate_select->str, get,
distinct, 0, extra_tables,
given_extra_where, aggregate_group_by,
&inner_select);
if (build_select_ret == 0)
{
init_iterator (iterator,
"SELECT sum(aggregate_count) AS outer_count,"
" %s AS outer_group_column,"
" %s AS outer_subgroup_column"
" %s"
" FROM (%s)"
" AS agg_sub"
" GROUP BY outer_group_column, outer_subgroup_column"
" %s"
" LIMIT %s OFFSET %d;",
outer_group_by_column,
outer_subgroup_column,
outer_col_select->str,
inner_select,
order_clause->str,
sql_select_limit (max_groups),
first_group);
}
g_string_free (order_clause, TRUE);
g_free (aggregate_group_by);
g_string_free (aggregate_select, TRUE);
g_string_free (outer_col_select, TRUE);
g_free (outer_group_by_column);
g_free (outer_subgroup_column);
g_free (select_group_column);
g_free (select_subgroup_column);
g_free (inner_select);
switch (build_select_ret)
{
case 0:
break;
case 1:
return 2;
default:
return -1;
}
return 0;
}
/**
* @brief Offset for aggregate iterator.
*/
#define AGGREGATE_ITERATOR_OFFSET 3
/**
* @brief Number of stats, for aggregate iterator.
*/
#define AGGREGATE_ITERATOR_N_STATS 4
/**
* @brief Get the count from an aggregate iterator.
*
* @param[in] iterator Iterator.
*
* @return The count of resources in the current group.
*/
int
aggregate_iterator_count (iterator_t* iterator)
{
return iterator_int (iterator, 0);
}
/**
* @brief Get the minimum from an aggregate iterator.
*
* @param[in] iterator Iterator.
* @param[in] data_column_index Index of the data column to get min of.
*
* @return The minimum value in the current group.
*/
double
aggregate_iterator_min (iterator_t* iterator, int data_column_index)
{
return iterator_double (iterator,
AGGREGATE_ITERATOR_OFFSET
+ data_column_index * AGGREGATE_ITERATOR_N_STATS);
}
/**
* @brief Get the maximum from an aggregate iterator.
*
* @param[in] iterator Iterator.
* @param[in] data_column_index Index of the data column to get max of.
*
* @return The maximum value in the current group.
*/
double
aggregate_iterator_max (iterator_t* iterator, int data_column_index)
{
return iterator_double (iterator,
AGGREGATE_ITERATOR_OFFSET + 1
+ data_column_index * AGGREGATE_ITERATOR_N_STATS);
}
/**
* @brief Get the mean from an aggregate iterator.
*
* @param[in] iterator Iterator.
* @param[in] data_column_index Index of the data column to get mean of.
*
* @return The mean value in the current group.
*/
double
aggregate_iterator_mean (iterator_t* iterator, int data_column_index)
{
return iterator_double (iterator,
AGGREGATE_ITERATOR_OFFSET + 2
+ data_column_index * AGGREGATE_ITERATOR_N_STATS);
}
/**
* @brief Get the sum from a statistics iterator.
*
* @param[in] iterator Iterator.
* @param[in] data_column_index Index of the data column to get sum of.
*
* @return The sum of values in the current group.
*/
double
aggregate_iterator_sum (iterator_t* iterator, int data_column_index)
{
return iterator_double (iterator,
AGGREGATE_ITERATOR_OFFSET + 3
+ data_column_index * AGGREGATE_ITERATOR_N_STATS);
}
/**
* @brief Get the value of a text column from an aggregate iterator.
*
* @param[in] iterator Iterator.
* @param[in] text_column_index Index of the text column to get.
* @param[in] data_columns Number of data columns.
*
* @return The value, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
const char*
aggregate_iterator_text (iterator_t* iterator, int text_column_index,
int data_columns)
{
const char *ret;
if (iterator->done) return NULL;
ret = (const char*) iterator_string (iterator,
AGGREGATE_ITERATOR_OFFSET
+ (data_columns
* AGGREGATE_ITERATOR_N_STATS)
+ text_column_index);
return ret;
}
/**
* @brief Get the value of the group column from a statistics iterator.
*
* @param[in] iterator Iterator.
*
* @return The value, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
const char*
aggregate_iterator_value (iterator_t* iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = (const char*) iterator_string (iterator, 1);
return ret;
}
/**
* @brief Get the value of the subgroup column from an aggregate iterator.
*
* @param[in] iterator Iterator.
*
* @return The value, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
const char*
aggregate_iterator_subgroup_value (iterator_t* iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = (const char*) iterator_string (iterator, 2);
return ret;
}
/**
* @brief Count number of a particular resource.
*
* @param[in] type Type of resource.
* @param[in] get GET params.
* @param[in] select_columns SELECT columns.
* @param[in] trash_select_columns SELECT columns for trashcan.
* @param[in] where_columns WHERE columns.
* @param[in] trash_where_columns WHERE columns for trashcan.
* @param[in] filter_columns Extra columns.
* @param[in] distinct Whether the query should be distinct. Skipped
* for trash and single resource.
* @param[in] extra_tables Join tables. Skipped for trash and single
* resource.
* @param[in] extra_where Extra WHERE clauses. Skipped for trash and
* single resource.
* @param[in] extra_with Extra WITH clauses.
* @param[in] owned Only count items owned by current user.
*
* @return Total number of resources in filtered set.
*/
static int
count2 (const char *type, const get_data_t *get, column_t *select_columns,
column_t *trash_select_columns, column_t *where_columns,
column_t *trash_where_columns, const char **filter_columns,
int distinct, const char *extra_tables, const char *extra_where,
const char *extra_with, int owned)
{
int ret;
gchar *clause, *owned_clause, *owner_filter, *columns, *filter, *with;
array_t *permissions;
assert (get);
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return -1;
}
else
filter = NULL;
g_debug ("%s", __func__);
clause = filter_clause (type, filter ? filter : get->filter, filter_columns,
get->trash && trash_select_columns
? trash_select_columns
: select_columns,
get->trash && trash_where_columns
? trash_where_columns
: where_columns,
get->trash, NULL, NULL, NULL, &permissions,
&owner_filter);
g_free (filter);
owned_clause = acl_where_owned (type, get, owned, owner_filter, 0,
permissions, 0, &with);
if (extra_with)
{
if (with)
{
gchar *old_with;
old_with = with;
with = g_strdup_printf ("%s, %s", old_with, extra_with);
g_free (old_with);
}
else
with = g_strdup_printf ("WITH %s", extra_with);
}
g_free (owner_filter);
array_free (permissions);
if (get->trash && trash_select_columns)
columns = columns_build_select (trash_select_columns);
else
columns = columns_build_select (select_columns);
if ((distinct == 0)
&& (extra_tables == NULL)
&& (clause == NULL)
&& (extra_where == NULL)
&& (strcmp (owned_clause, " t ()") == 0))
ret = sql_int ("%sSELECT count (*) FROM %ss%s;",
with ? with : "", type,
get->trash && strcmp (type, "task") ? "_trash" : "");
else
ret = sql_int ("%sSELECT count (%scount_id)"
" FROM (SELECT %ss%s.id AS count_id"
" FROM %ss%s%s"
" WHERE %s"
" %s%s%s%s) AS subquery;",
with ? with : "",
distinct ? "DISTINCT " : "",
type,
get->trash && strcmp (type, "task") ? "_trash" : "",
type,
get->trash && strcmp (type, "task") ? "_trash" : "",
extra_tables ? extra_tables : "",
owned_clause,
clause ? " AND (" : "",
clause ? clause : "",
clause ? ") " : "",
extra_where ? extra_where : "");
g_free (with);
g_free (columns);
g_free (owned_clause);
g_free (clause);
g_debug ("%s: done", __func__);
return ret;
}
/**
* @brief Count number of a particular resource.
*
* @param[in] type Type of resource.
* @param[in] get GET params.
* @param[in] select_columns SELECT columns.
* @param[in] trash_select_columns SELECT columns for trashcan.
* @param[in] filter_columns Extra columns.
* @param[in] distinct Whether the query should be distinct. Skipped
* for trash and single resource.
* @param[in] extra_tables Join tables. Skipped for trash and single
* resource.
* @param[in] extra_where Extra WHERE clauses. Skipped for trash and
* single resource.
* @param[in] owned Only count items owned by current user.
*
* @return Total number of resources in filtered set.
*/
int
count (const char *type, const get_data_t *get, column_t *select_columns,
column_t *trash_select_columns, const char **filter_columns,
int distinct, const char *extra_tables, const char *extra_where,
int owned)
{
return count2 (type, get, select_columns, trash_select_columns, NULL, NULL,
filter_columns, distinct, extra_tables, extra_where, NULL,
owned);
}
/**
* @brief Count number of info of a given subtype with a given name.
*
* @param[in] type GET_INFO subtype.
* @param[out] name Name of the info item.
*
* @return Total number of get_info items of given type, -1 on error.
*/
int
info_name_count (const char *type, const char *name)
{
gchar *quoted_name;
int count;
assert(type);
assert(name);
quoted_name = sql_quote (name);
count = sql_int ("SELECT COUNT(id)"
" FROM %ss"
" WHERE name = '%s';",
type,
quoted_name);
g_free (quoted_name);
return count;
}
/**
* @brief Return the database version supported by this manager.
*
* @return Database version supported by this manager.
*/
int
manage_db_supported_version ()
{
return GVMD_DATABASE_VERSION;
}
/**
* @brief Return the database version of the actual database.
*
* @return Database version read from database, -2 if database is empty,
* -1 on error.
*/
int
manage_db_version ()
{
int number;
char *version;
if (manage_db_empty ())
return -2;
version = sql_string ("SELECT value FROM %s.meta"
" WHERE name = 'database_version' LIMIT 1;",
sql_schema ());
if (version)
{
number = atoi (version);
free (version);
return number;
}
return -1;
}
/**
* @brief Return the database version supported by this manager.
*
* @return Database version supported by this manager.
*/
int
manage_scap_db_supported_version ()
{
return GVMD_SCAP_DATABASE_VERSION;
}
/**
* @brief Return the database version of the actual database.
*
* @return Database version read from database if possible, else -1.
*/
int
manage_scap_db_version ()
{
if (manage_scap_loaded () == 0)
return -1;
int number;
char *version = sql_string ("SELECT value FROM scap.meta"
" WHERE name = 'database_version' LIMIT 1;");
if (version)
{
number = atoi (version);
free (version);
return number;
}
return -1;
}
/**
* @brief Return the database version supported by this manager.
*
* @return Database version supported by this manager.
*/
int
manage_cert_db_supported_version ()
{
return GVMD_CERT_DATABASE_VERSION;
}
/**
* @brief Return the database version of the actual database.
*
* @return Database version read from database if possible, else -1.
*/
int
manage_cert_db_version ()
{
if (manage_cert_loaded () == 0)
return -1;
int number;
char *version = sql_string ("SELECT value FROM cert.meta"
" WHERE name = 'database_version' LIMIT 1;");
if (version)
{
number = atoi (version);
free (version);
return number;
}
return -1;
}
/**
* @brief Set the database version of the actual database.
*
* Caller must organise transaction.
*
* @param version New version number.
*/
void
set_db_version (int version)
{
sql ("DELETE FROM %s.meta WHERE name = 'database_version';",
sql_schema ());
sql ("INSERT INTO %s.meta (name, value)"
" VALUES ('database_version', '%i');",
sql_schema (),
version);
}
/**
* @brief Encrypt, re-encrypt or decrypt all credentials
*
* All plaintext credentials in the credentials table are
* encrypted, all already encrypted credentials are encrypted again
* using the latest key.
*
* @param[in] decrypt_flag If true decrypt all credentials.
*
* @return 0 success, -1 error.
*/
static int
encrypt_all_credentials (gboolean decrypt_flag)
{
iterator_t iterator;
unsigned long ntotal, nencrypted, nreencrypted, ndecrypted;
init_iterator (&iterator,
"SELECT id,"
" (SELECT value FROM credentials_data"
" WHERE credential = credentials.id"
" AND type = 'secret'),"
" (SELECT value FROM credentials_data"
" WHERE credential = credentials.id"
" AND type = 'password'),"
" (SELECT value FROM credentials_data"
" WHERE credential = credentials.id"
" AND type = 'private_key')"
" FROM credentials");
iterator.crypt_ctx = lsc_crypt_new ();
sql_begin_immediate ();
ntotal = nencrypted = nreencrypted = ndecrypted = 0;
while (next (&iterator))
{
long long int rowid;
const char *secret, *password, *privkey;
ntotal++;
if (!(ntotal % 10))
g_message (" %lu credentials so far processed", ntotal);
rowid = iterator_int64 (&iterator, 0);
secret = iterator_string (&iterator, 1);
password = iterator_string (&iterator, 2);
privkey = iterator_string (&iterator, 3);
/* If there is no secret, password or private key, skip the row. */
if (!secret && !password && !privkey)
continue;
if (secret)
{
lsc_crypt_flush (iterator.crypt_ctx);
password = lsc_crypt_get_password (iterator.crypt_ctx, secret);
privkey = lsc_crypt_get_private_key (iterator.crypt_ctx, secret);
/* If there is no password or private key, skip the row. */
if (!password && !privkey)
continue;
nreencrypted++;
}
else
{
if (decrypt_flag)
continue; /* Skip non-encrypted rows. */
nencrypted++;
}
if (decrypt_flag)
{
set_credential_data (rowid, "password", password);
set_credential_data (rowid, "private_key", privkey);
set_credential_data (rowid, "secret", NULL);
sql ("UPDATE credentials SET"
" modification_time = m_now ()"
" WHERE id = %llu;", rowid);
ndecrypted++;
}
else
{
char *encblob;
if (password && privkey)
encblob = lsc_crypt_encrypt (iterator.crypt_ctx,
"password", password,
"private_key", privkey, NULL);
else if (password)
encblob = lsc_crypt_encrypt (iterator.crypt_ctx,
"password", password, NULL);
else
encblob = lsc_crypt_encrypt (iterator.crypt_ctx,
"private_key", privkey, NULL);
if (!encblob)
{
sql_rollback ();
cleanup_iterator (&iterator);
return -1;
}
set_credential_data (rowid, "password", NULL);
set_credential_data (rowid, "private_key", NULL);
set_credential_data (rowid, "secret", encblob);
sql ("UPDATE credentials SET"
" modification_time = m_now ()"
" WHERE id = %llu;", rowid);
g_free (encblob);
}
}
sql_commit ();
if (decrypt_flag)
g_message ("%lu out of %lu credentials decrypted",
ndecrypted, ntotal);
else
g_message ("%lu out of %lu credentials encrypted and %lu re-encrypted",
nencrypted, ntotal, nreencrypted);
cleanup_iterator (&iterator);
return 0;
}
/**
* @brief Encrypt or re-encrypt all credentials
*
* All plaintext credentials in the credentials table are
* encrypted, all already encrypted credentials are encrypted again
* using the latest key.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
*
* @return 0 success, -1 error,
* -2 database is wrong version, -3 database needs to be initialised
* from server.
*/
int
manage_encrypt_all_credentials (GSList *log_config,
const db_conn_info_t *database)
{
int ret;
g_info (" (Re-)encrypting all credentials.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
ret = encrypt_all_credentials (FALSE);
if (ret)
printf ("Encryption failed.\n");
else
printf ("Encryption succeeded.\n");
manage_option_cleanup ();
return ret;
}
/**
* @brief Decrypt all credentials
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
*
* @return 0 success, -1 error,
* -2 database is wrong version, -3 database needs to be initialised
* from server.
*/
int
manage_decrypt_all_credentials (GSList *log_config,
const db_conn_info_t *database)
{
int ret;
g_info (" Decrypting all credentials.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
ret = encrypt_all_credentials (TRUE);
if (ret)
printf ("Decryption failed.\n");
else
printf ("Decryption succeeded.\n");
manage_option_cleanup ();
return ret;
}
/* Collation. */
/**
* @brief Compare two number strings for collate_ip.
*
* @param[in] one_arg First string.
* @param[in] two_arg Second string.
*
* @return -1, 0 or 1 if first is less than, equal to or greater than second.
*/
static int
collate_ip_compare (const char *one_arg, const char *two_arg)
{
int one = atoi (one_arg);
int two = atoi (two_arg);
return one == two ? 0 : (one < two ? -1 : 1);
}
/**
* @brief Collate two IP addresses.
*
* For example, 127.0.0.2 is less than 127.0.0.3 and 127.0.0.10.
*
* Only works correctly for IPv4 addresses.
*
* @param[in] data Dummy for callback.
* @param[in] one_len Length of first IP (a string).
* @param[in] arg_one First string.
* @param[in] two_len Length of second IP (a string).
* @param[in] arg_two Second string.
*
* @return -1, 0 or 1 if first is less than, equal to or greater than second.
*/
static int
collate_ip (void* data,
int one_len, const void* arg_one,
int two_len, const void* arg_two)
{
int ret, one_dot, two_dot;
char one_a[4], one_b[4], one_c[4], one_d[4];
char two_a[4], two_b[4], two_c[4], two_d[4];
const char* one = (const char*) arg_one;
const char* two = (const char*) arg_two;
if ((sscanf (one, "%3[0-9].%3[0-9].%3[0-9].%n%3[0-9]",
one_a, one_b, one_c, &one_dot, one_d)
== 4)
&& (sscanf (two, "%3[0-9].%3[0-9].%3[0-9].%n%3[0-9]",
two_a, two_b, two_c, &two_dot, two_d)
== 4))
{
ret = collate_ip_compare (one_a, two_a);
if (ret) return ret < 0 ? -1 : 1;
ret = collate_ip_compare (one_b, two_b);
if (ret) return ret < 0 ? -1 : 1;
ret = collate_ip_compare (one_c, two_c);
if (ret) return ret < 0 ? -1 : 1;
/* Ensure that the last number is limited to digits in the arg. */
one_d[one_len - one_dot] = '\0';
two_d[two_len - two_dot] = '\0';
ret = collate_ip_compare (one_d, two_d);
if (ret) return ret < 0 ? -1 : 1;
return 0;
}
ret = strncmp (one, two, MIN (one_len, two_len));
return ret == 0 ? 0 : (ret < 0 ? -1 : 1);
}
/* Task subject iterators. */
/**
* @brief Initialise a task user iterator.
*
* @param[in] iterator Iterator.
* @param[in] task Task.
*/
static void
init_task_user_iterator (iterator_t *iterator, task_t task)
{
init_iterator (iterator,
"SELECT DISTINCT 1, resource, subject,"
" (SELECT name FROM users"
" WHERE users.id = permissions.subject)"
" FROM permissions"
/* Any permission implies 'get_tasks'. */
" WHERE resource_type = 'task'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
" AND subject_type = 'user'"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
task);
}
static
DEF_ACCESS (task_user_iterator_name, 3);
/**
* @brief Initialise a task group iterator.
*
* @param[in] iterator Iterator.
* @param[in] task Task.
*/
void
init_task_group_iterator (iterator_t *iterator, task_t task)
{
init_iterator (iterator,
"SELECT DISTINCT 1, resource, subject,"
" (SELECT name FROM groups"
" WHERE groups.id = permissions.subject),"
" (SELECT uuid FROM groups"
" WHERE groups.id = permissions.subject)"
" FROM permissions"
/* Any permission implies 'get_tasks'. */
" WHERE resource_type = 'task'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
" AND subject_type = 'group'"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
task);
}
DEF_ACCESS (task_group_iterator_name, 3);
DEF_ACCESS (task_group_iterator_uuid, 4);
/**
* @brief Initialise a task role iterator.
*
* @param[in] iterator Iterator.
* @param[in] task Task.
*/
void
init_task_role_iterator (iterator_t *iterator, task_t task)
{
init_iterator (iterator,
"SELECT DISTINCT 1, resource, subject,"
" (SELECT name FROM roles"
" WHERE roles.id = permissions.subject),"
" (SELECT uuid FROM roles"
" WHERE roles.id = permissions.subject)"
" FROM permissions"
/* Any permission implies 'get'. */
" WHERE resource_type = 'task'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
" AND subject_type = 'role'",
task);
}
DEF_ACCESS (task_role_iterator_name, 3);
DEF_ACCESS (task_role_iterator_uuid, 4);
/* Events and Alerts. */
/**
* @brief Check if any SecInfo alerts are due.
*/
void
check_alerts ()
{
if (manage_scap_loaded ())
{
int max_time;
max_time
= sql_int ("SELECT %s"
" ((SELECT max (modification_time) FROM scap.cves),"
" (SELECT max (modification_time) FROM scap.cpes),"
" (SELECT max (modification_time) FROM scap.ovaldefs),"
" (SELECT max (creation_time) FROM scap.cves),"
" (SELECT max (creation_time) FROM scap.cpes),"
" (SELECT max (creation_time) FROM scap.ovaldefs));",
sql_greatest ());
if (sql_int ("SELECT NOT EXISTS (SELECT * FROM meta"
" WHERE name = 'scap_check_time')"))
sql ("INSERT INTO meta (name, value)"
" VALUES ('scap_check_time', %i);",
max_time);
else if (sql_int ("SELECT value = '0' FROM meta"
" WHERE name = 'scap_check_time';"))
sql ("UPDATE meta SET value = %i"
" WHERE name = 'scap_check_time';",
max_time);
else
{
check_for_new_scap ();
check_for_updated_scap ();
sql ("UPDATE meta SET value = %i"
" WHERE name = 'scap_check_time';",
max_time);
}
}
if (manage_cert_loaded ())
{
int max_time;
max_time
= sql_int ("SELECT"
" %s"
" ((SELECT max (modification_time) FROM cert.cert_bund_advs),"
" (SELECT max (modification_time) FROM cert.dfn_cert_advs),"
" (SELECT max (creation_time) FROM cert.cert_bund_advs),"
" (SELECT max (creation_time) FROM cert.dfn_cert_advs));",
sql_greatest ());
if (sql_int ("SELECT NOT EXISTS (SELECT * FROM meta"
" WHERE name = 'cert_check_time')"))
sql ("INSERT INTO meta (name, value)"
" VALUES ('cert_check_time', %i);",
max_time);
else if (sql_int ("SELECT value = '0' FROM meta"
" WHERE name = 'cert_check_time';"))
sql ("UPDATE meta SET value = %i"
" WHERE name = 'cert_check_time';",
max_time);
else
{
check_for_new_cert ();
check_for_updated_cert ();
sql ("UPDATE meta SET value = %i"
" WHERE name = 'cert_check_time';",
max_time);
}
}
}
/**
* @brief Check if any SecInfo alerts are due.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
*
* @return 0 success, -1 error,
* -2 database is wrong version, -3 database needs to be initialised
* from server.
*/
int
manage_check_alerts (GSList *log_config, const db_conn_info_t *database)
{
int ret;
g_info (" Checking alerts.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
/* Setup a dummy user, so that create_user will work. */
current_credentials.uuid = "";
check_alerts ();
current_credentials.uuid = NULL;
manage_option_cleanup ();
return ret;
}
/**
* @brief Find a alert for a specific permission, given a UUID.
*
* @param[in] uuid UUID of alert.
* @param[out] alert Alert return, 0 if successfully failed to find alert.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find alert), TRUE on error.
*/
gboolean
find_alert_with_permission (const char* uuid, alert_t* alert,
const char *permission)
{
return find_resource_with_permission ("alert", uuid, alert, permission, 0);
}
/**
* @brief Validate an email address.
*
* @param[in] address Email address.
*
* @return 0 success, 1 failure.
*/
static int
validate_email (const char* address)
{
gchar **split, *point;
assert (address);
split = g_strsplit (address, "@", 0);
if (split[0] == NULL || split[1] == NULL || split[2])
{
g_strfreev (split);
return 1;
}
/* Local part. */
point = split[0];
while (*point)
if (isalnum (*point)
|| strchr ("!#$%&'*+-/=?^_`{|}~", *point)
|| ((*point == '.')
&& (point > split[0])
&& point[1]
&& (point[1] != '.')
&& (point[-1] != '.')))
point++;
else
{
g_strfreev (split);
return 1;
}
/* Domain. */
point = split[1];
while (*point)
if (isalnum (*point)
|| strchr ("-_", *point) /* RFC actually forbids _. */
|| ((*point == '.')
&& (point > split[1])
&& point[1]
&& (point[1] != '.')
&& (point[-1] != '.')))
point++;
else
{
g_strfreev (split);
return 1;
}
g_strfreev (split);
return 0;
}
/**
* @brief Validate an email address list.
*
* @param[in] list Comma separated list of email addresses.
*
* @return 0 success, 1 failure.
*/
static int
validate_email_list (const char *list)
{
gchar **split, **point;
assert (list);
split = g_strsplit (list, ",", 0);
if (split[0] == NULL)
{
g_strfreev (split);
return 1;
}
point = split;
while (*point)
{
const char *address;
address = *point;
while (*address && (*address == ' ')) address++;
if (validate_email (address))
{
g_strfreev (split);
return 1;
}
point++;
}
g_strfreev (split);
return 0;
}
/**
* @brief Validate condition data for an alert.
*
* @param[in] name Name.
* @param[in] data Data to validate.
* @param[in] condition The condition.
*
* @return 0 on success, 1 unexpected data name, 2 syntax error in data,
* 3 failed to find filter for condition, -1 internal error.
*/
static int
validate_alert_condition_data (gchar *name, gchar* data,
alert_condition_t condition)
{
if (condition == ALERT_CONDITION_ALWAYS)
return 1;
if (condition == ALERT_CONDITION_SEVERITY_AT_LEAST)
{
if (strcmp (name, "severity"))
return 1;
if (g_regex_match_simple ("^(-1(\\.0)?|[0-9](\\.[0-9])?|10(\\.0))$",
data ? data : "",
0,
0)
== 0)
return 2;
}
else if (condition == ALERT_CONDITION_SEVERITY_CHANGED)
{
if (strcmp (name, "direction"))
return 1;
if (g_regex_match_simple ("^(increased|decreased|changed)$",
data ? data : "",
0,
0)
== 0)
return 2;
}
else if (condition == ALERT_CONDITION_FILTER_COUNT_AT_LEAST)
{
if (strcmp (name, "filter_id") == 0)
{
filter_t filter;
if (data == NULL)
return 3;
filter = 0;
if (find_filter_with_permission (data, &filter, "get_filters"))
return -1;
if (filter == 0)
return 3;
return 0;
}
if (strcmp (name, "count"))
return 1;
}
else if (condition == ALERT_CONDITION_FILTER_COUNT_CHANGED)
{
if (strcmp (name, "filter_id") == 0)
{
filter_t filter;
if (data == NULL)
return 3;
filter = 0;
if (find_filter_with_permission (data, &filter, "get_filters"))
return -1;
if (filter == 0)
return 3;
return 0;
}
if (strcmp (name, "direction")
&& strcmp (name, "count"))
return 1;
if (strcmp (name, "direction") == 0
&& g_regex_match_simple ("^(increased|decreased|changed)$",
data ? data : "",
0,
0)
== 0)
return 2;
}
return 0;
}
/**
* @brief Validate event data for an alert.
*
* @param[in] name Name.
* @param[in] data Data to validate.
* @param[in] event The event.
*
* @return 0 on success, 1 unexpected data name, 2 syntax error in data.
*/
static int
validate_alert_event_data (gchar *name, gchar* data, event_t event)
{
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
if (strcmp (name, "secinfo_type"))
return 1;
if (data == NULL)
return 2;
if (strcasecmp (data, "nvt")
&& strcasecmp (data, "cve")
&& strcasecmp (data, "cpe")
&& strcasecmp (data, "cert_bund_adv")
&& strcasecmp (data, "dfn_cert_adv")
&& strcasecmp (data, "ovaldef"))
return 2;
}
return 0;
}
/**
* @brief Validate method data for the email method.
*
* @param[in] method Method that data corresponds to.
* @param[in] name Name of data.
* @param[in] data The data.
* @param[in] for_modify Whether to return error codes for modify_alert.
*
* @return 0 valid, 2 or 6: validation of email address failed,
* 7 or 9 subject too long, 8 or 10 message too long,
* 60 recipient credential not found, 61 invalid recipient credential
* type, -1 error. When for_modify is 0, the first code is returned,
* otherwise the second one.
*/
int
validate_email_data (alert_method_t method, const gchar *name, gchar **data,
int for_modify)
{
if (method == ALERT_METHOD_EMAIL
&& strcmp (name, "to_address") == 0
&& validate_email_list (*data))
return for_modify ? 6 : 2;
if (method == ALERT_METHOD_EMAIL
&& strcmp (name, "from_address") == 0
&& validate_email (*data))
return for_modify ? 6 : 2;
if (method == ALERT_METHOD_EMAIL
&& strcmp (name, "subject") == 0
&& strlen (*data) > 80)
return for_modify ? 9 : 7;
if (method == ALERT_METHOD_EMAIL
&& strcmp (name, "message") == 0
&& strlen (*data) > max_email_message_length)
return for_modify ? 10 : 8;
if (method == ALERT_METHOD_EMAIL
&& strcmp (name, "recipient_credential") == 0
&& *data && strcmp (*data, ""))
{
credential_t credential;
char *type;
if (find_credential_with_permission (*data, &credential, NULL))
return -1;
else if (credential == 0)
return 60;
type = credential_type (credential);
if (strcmp (type, "pgp")
&& strcmp (type, "smime"))
{
free (type);
return 61;
}
free (type);
}
return 0;
}
/**
* @brief Validate method data for the SCP method.
*
* @param[in] method Method that data corresponds to.
* @param[in] name Name of data.
* @param[in] data The data.
*
* @return 0 valid, 15 error in SCP host, 17 failed to find report format for
* SCP method, 18 error in SCP credential, 19 error in SCP path,
* -1 error.
*/
static int
validate_scp_data (alert_method_t method, const gchar *name, gchar **data)
{
if (method == ALERT_METHOD_SCP
&& strcmp (name, "scp_credential") == 0)
{
credential_t credential;
if (find_credential_with_permission (*data, &credential,
"get_credentials"))
return -1;
else if (credential == 0)
return 18;
else
{
gchar *username;
username = credential_value (credential, "username");
if (username == NULL || strlen (username) == 0)
{
g_free (username);
return 18;
}
if (strchr (username, ':'))
{
g_free (username);
return 18;
}
g_free (username);
}
}
if (method == ALERT_METHOD_SCP
&& strcmp (name, "scp_path") == 0)
{
if (strlen (*data) == 0)
return 19;
}
if (method == ALERT_METHOD_SCP
&& strcmp (name, "scp_host") == 0)
{
int type;
gchar *stripped;
stripped = g_strstrip (g_strdup (*data));
type = gvm_get_host_type (stripped);
g_free (stripped);
if ((type != HOST_TYPE_IPV4)
&& (type != HOST_TYPE_IPV6)
&& (type != HOST_TYPE_NAME))
return 15;
}
if (method == ALERT_METHOD_SCP
&& strcmp (name, "scp_report_format") == 0)
{
report_format_t report_format;
report_format = 0;
if (find_report_format_with_permission (*data,
&report_format,
"get_report_formats"))
return -1;
if (report_format == 0)
return 17;
}
return 0;
}
/**
* @brief Validate method data for the Send method.
*
* @param[in] method Method that data corresponds to.
* @param[in] name Name of data.
* @param[in] data The data.
*
* @return 0 valid, 12 error in Send host, 13 error in Send port, 14 failed
* to find report format for Send method, -1 error.
*/
static int
validate_send_data (alert_method_t method, const gchar *name, gchar **data)
{
if (method == ALERT_METHOD_SEND
&& strcmp (name, "send_host") == 0)
{
int type;
gchar *stripped;
stripped = g_strstrip (g_strdup (*data));
type = gvm_get_host_type (stripped);
g_free (stripped);
if ((type != HOST_TYPE_IPV4)
&& (type != HOST_TYPE_IPV6)
&& (type != HOST_TYPE_NAME))
return 12;
}
if (method == ALERT_METHOD_SEND
&& strcmp (name, "send_port") == 0)
{
int port;
gchar *stripped, *end;
stripped = g_strstrip (g_strdup (*data));
port = strtol (stripped, &end, 10);
if (*end != '\0')
{
g_free (stripped);
return 13;
}
g_free (stripped);
g_free (*data);
*data = g_strdup_printf ("%i", port);
}
if (method == ALERT_METHOD_SEND
&& strcmp (name, "send_report_format") == 0)
{
report_format_t report_format;
report_format = 0;
if (find_report_format_with_permission (*data,
&report_format,
"get_report_formats"))
return -1;
if (report_format == 0)
return 14;
}
return 0;
}
/**
* @brief Validate method data for the Send method.
*
* @param[in] method Method that data corresponds to.
* @param[in] name Name of data.
* @param[in] data The data.
*
* @return 0 valid, 40 invalid credential, 41 invalid SMB share path,
* 42 invalid SMB file path, 43 SMB file path contains dot, -1 error.
*/
static int
validate_smb_data (alert_method_t method, const gchar *name, gchar **data)
{
if (method == ALERT_METHOD_SMB)
{
if (strcmp (name, "smb_credential") == 0)
{
credential_t credential;
if (find_credential_with_permission (*data, &credential,
"get_credentials"))
return -1;
else if (credential == 0)
return 40;
else
{
gchar *username;
username = credential_value (credential, "username");
if (username == NULL || strlen (username) == 0)
{
g_free (username);
return 40;
}
if (strchr (username, '@') || strchr (username, ':'))
{
g_free (username);
return 40;
}
g_free (username);
}
}
if (strcmp (name, "smb_share_path") == 0)
{
/* Check if share path has the correct format
* "\\\" */
if (g_regex_match_simple ("^(?>\\\\\\\\|\\/\\/)[^:?<>|]+"
"(?>\\\\|\\/)[^:?<>|]+$", *data, 0, 0)
== FALSE)
{
return 41;
}
}
if (strcmp (name, "smb_file_path") == 0)
{
/* Check if file path contains invalid characters:
* ":", "?", "<", ">", "|" */
if (g_regex_match_simple ("^[^:?<>|]+$", *data, 0, 0)
== FALSE)
{
return 42;
}
/* Check if a file or directory name ends with a dot,
* e.g. "../a", "abc/../xyz" or "abc/..". */
else if (g_regex_match_simple ("^(?:.*\\.)(?:[\\/\\\\].*)*$",
*data, 0, 0))
{
return 43;
}
}
}
return 0;
}
/**
* @brief Validate method data for the TippingPoint method.
*
* @param[in] method Method that data corresponds to.
* @param[in] name Name of data.
* @param[in] data The data.
*
* @return 0 valid, 50 invalid credential, 51 invalid hostname,
* 52 invalid certificate, 53 invalid TLS workaround setting.
*/
static int
validate_tippingpoint_data (alert_method_t method, const gchar *name,
gchar **data)
{
if (method == ALERT_METHOD_TIPPINGPOINT)
{
if (strcmp (name, "tp_sms_credential") == 0)
{
credential_t credential;
if (find_credential_with_permission (*data, &credential,
"get_credentials"))
return -1;
else if (credential == 0)
return 50;
else
{
if (strcmp (credential_type (credential), "up"))
return 50;
}
}
if (strcmp (name, "tp_sms_hostname") == 0)
{
if (g_regex_match_simple ("^[0-9A-Za-z][0-9A-Za-z.-]*$",
*data, 0, 0)
== FALSE)
{
return 51;
}
}
if (strcmp (name, "tp_sms_tls_certificate") == 0)
{
// TODO: Check certificate, return 52 on failure
}
if (strcmp (name, "tp_sms_tls_workaround") == 0)
{
if (g_regex_match_simple ("^0|1$", *data, 0, 0)
== FALSE)
{
return 53;
}
}
}
return 0;
}
/**
* @brief Validate method data for the vFire alert method.
*
* @param[in] method Method that data corresponds to.
* @param[in] name Name of data.
* @param[in] data The data.
*
* @return 0 valid, 70 credential not found, 71 invalid credential type
*/
static int
validate_vfire_data (alert_method_t method, const gchar *name,
gchar **data)
{
if (method == ALERT_METHOD_VFIRE)
{
if (strcmp (name, "vfire_credential") == 0)
{
credential_t credential;
if (find_credential_with_permission (*data, &credential,
"get_credentials"))
return -1;
else if (credential == 0)
return 70;
else
{
char *cred_type = credential_type (credential);
if (strcmp (cred_type, "up"))
{
free (cred_type);
return 71;
}
free (cred_type);
}
}
}
return 0;
}
/**
* @brief Validate method data for the Sourcefire method.
*
* @param[in] method Method that data corresponds to.
* @param[in] name Name of data.
* @param[in] data The data.
*
* @return 0 valid, 80 credential not found, 81 invalid credential type
*/
static int
validate_sourcefire_data (alert_method_t method, const gchar *name,
gchar **data)
{
if (method == ALERT_METHOD_SOURCEFIRE)
{
if (strcmp (name, "pkcs12_credential") == 0)
{
credential_t credential;
if (find_credential_with_permission (*data, &credential,
"get_credentials"))
return -1;
else if (credential == 0)
return 80;
else
{
char *sourcefire_credential_type;
sourcefire_credential_type = credential_type (credential);
if (strcmp (sourcefire_credential_type, "up")
&& strcmp (sourcefire_credential_type, "pw"))
{
free (sourcefire_credential_type);
return 81;
}
free (sourcefire_credential_type);
}
}
}
return 0;
}
/**
* @brief Check alert params.
*
* @param[in] event Type of event.
* @param[in] condition Event condition.
* @param[in] method Escalation method.
*
* @return 0 success, 20 method does not match event, 21 condition does not
* match event.
*/
static int
check_alert_params (event_t event, alert_condition_t condition,
alert_method_t method)
{
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
if (method == ALERT_METHOD_HTTP_GET
|| method == ALERT_METHOD_SOURCEFIRE
|| method == ALERT_METHOD_VERINICE)
return 20;
if (condition == ALERT_CONDITION_SEVERITY_AT_LEAST
|| condition == ALERT_CONDITION_SEVERITY_CHANGED
|| condition == ALERT_CONDITION_FILTER_COUNT_CHANGED)
return 21;
}
return 0;
}
/**
* @brief Create an alert.
*
* @param[in] name Name of alert.
* @param[in] comment Comment on alert.
* @param[in] filter_id Filter.
* @param[in] active Whether the alert is active.
* @param[in] event Type of event.
* @param[in] event_data Type-specific event data.
* @param[in] condition Event condition.
* @param[in] condition_data Condition-specific data.
* @param[in] method Escalation method.
* @param[in] method_data Data for escalation method.
* @param[out] alert Created alert on success.
*
* @return 0 success, 1 escalation exists already, 2 validation of email failed,
* 3 failed to find filter, 4 type must be "result" if specified,
* 5 unexpected condition data name, 6 syntax error in condition data,
* 7 email subject too long, 8 email message too long, 9 failed to find
* filter for condition, 12 error in Send host, 13 error in Send port,
* 14 failed to find report format for Send method, 15 error in
* SCP host, 17 failed to find report format for SCP method, 18 error
* in SCP credential, 19 error in SCP path, 20 method does not match
* event, 21 condition does not match event, 31 unexpected event data
* name, 32 syntax error in event data, 40 invalid SMB credential
* , 41 invalid SMB share path, 42 invalid SMB file path,
* 43 SMB file path contains dot,
* 50 invalid TippingPoint credential, 51 invalid TippingPoint hostname,
* 52 invalid TippingPoint certificate, 53 invalid TippingPoint TLS
* workaround setting, 60 recipient credential not found, 61 invalid
* recipient credential type, 70 vFire credential not found,
* 71 invalid vFire credential type,
* 99 permission denied, -1 error.
*/
int
create_alert (const char* name, const char* comment, const char* filter_id,
const char* active, event_t event, GPtrArray* event_data,
alert_condition_t condition, GPtrArray* condition_data,
alert_method_t method, GPtrArray* method_data,
alert_t *alert)
{
int index, ret;
gchar *item, *quoted_comment;
gchar *quoted_name;
filter_t filter;
assert (current_credentials.uuid);
sql_begin_immediate ();
if (acl_user_may ("create_alert") == 0)
{
sql_rollback ();
return 99;
}
ret = check_alert_params (event, condition, method);
if (ret)
{
sql_rollback ();
return ret;
}
filter = 0;
if (event != EVENT_NEW_SECINFO && event != EVENT_UPDATED_SECINFO && filter_id
&& strcmp (filter_id, "0"))
{
char *type;
if (find_filter_with_permission (filter_id, &filter, "get_filters"))
{
sql_rollback ();
return -1;
}
if (filter == 0)
{
sql_rollback ();
return 3;
}
/* Filter type must be result if specified. */
type = sql_string ("SELECT type FROM filters WHERE id = %llu;",
filter);
if (type && strcasecmp (type, "result"))
{
free (type);
sql_rollback ();
return 4;
}
free (type);
}
if (resource_with_name_exists (name, "alert", 0))
{
sql_rollback ();
return 1;
}
quoted_name = sql_quote (name);
quoted_comment = sql_quote (comment ?: "");
sql ("INSERT INTO alerts (uuid, owner, name, comment, event, condition,"
" method, filter, active, creation_time, modification_time)"
" VALUES (make_uuid (),"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', '%s', %i, %i, %i, %llu, %i, m_now (), m_now ());",
current_credentials.uuid,
quoted_name,
quoted_comment,
event,
condition,
method,
filter,
active ? strcmp (active, "0") : 1);
g_free (quoted_comment);
g_free (quoted_name);
*alert = sql_last_insert_id ();
index = 0;
while ((item = (gchar*) g_ptr_array_index (condition_data, index++)))
{
int validation_result;
gchar *data_name = sql_quote (item);
gchar *data = sql_quote (item + strlen (item) + 1);
validation_result = validate_alert_condition_data (data_name,
data,
condition);
if (validation_result)
{
g_free (data_name);
g_free (data);
sql_rollback ();
switch (validation_result)
{
case 1:
return 5;
case 2:
return 6;
case 3:
return 9;
default:
return -1;
}
}
sql ("INSERT INTO alert_condition_data (alert, name, data)"
" VALUES (%llu, '%s', '%s');",
*alert,
data_name,
data);
g_free (data_name);
g_free (data);
}
index = 0;
while ((item = (gchar*) g_ptr_array_index (event_data, index++)))
{
int validation_result;
gchar *data_name = sql_quote (item);
gchar *data = sql_quote (item + strlen (item) + 1);
validation_result = validate_alert_event_data (data_name, data, event);
if (validation_result)
{
g_free (data_name);
g_free (data);
sql_rollback ();
switch (validation_result)
{
case 1:
return 31;
case 2:
return 32;
default:
return -1;
}
}
sql ("INSERT INTO alert_event_data (alert, name, data)"
" VALUES (%llu, '%s', '%s');",
*alert,
data_name,
data);
g_free (data_name);
g_free (data);
}
index = 0;
while ((item = (gchar*) g_ptr_array_index (method_data, index++)))
{
gchar *data_name, *data;
data_name = sql_quote (item);
data = sql_quote (item + strlen (item) + 1);
ret = validate_email_data (method, data_name, &data, 0);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_scp_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_send_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_smb_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_sourcefire_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_tippingpoint_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_vfire_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
sql ("INSERT INTO alert_method_data (alert, name, data)"
" VALUES (%llu, '%s', '%s');",
*alert,
data_name,
data);
g_free (data_name);
g_free (data);
}
sql_commit ();
return 0;
}
/**
* @brief Create an alert from an existing alert.
*
* @param[in] name Name of new alert. NULL to copy from existing.
* @param[in] comment Comment on new alert. NULL to copy from
* existing.
* @param[in] alert_id UUID of existing alert.
* @param[out] new_alert New alert.
*
* @return 0 success, 1 alert exists already, 2 failed to find existing
* alert, 99 permission denied, -1 error.
*/
int
copy_alert (const char* name, const char* comment, const char* alert_id,
alert_t* new_alert)
{
int ret;
alert_t new, old;
assert (current_credentials.uuid);
if (alert_id == NULL)
return -1;
sql_begin_immediate ();
ret = copy_resource_lock ("alert", name, comment, alert_id,
"event, condition, method, filter, active",
1, &new, &old);
if (ret)
{
sql_rollback ();
return ret;
}
/* Copy the alert condition data */
sql ("INSERT INTO alert_condition_data (alert, name, data)"
" SELECT %llu, name, data FROM alert_condition_data"
" WHERE alert = %llu;",
new,
old);
/* Copy the alert event data */
sql ("INSERT INTO alert_event_data (alert, name, data)"
" SELECT %llu, name, data FROM alert_event_data"
" WHERE alert = %llu;",
new,
old);
/* Copy the alert method data */
sql ("INSERT INTO alert_method_data (alert, name, data)"
" SELECT %llu, name, data FROM alert_method_data"
" WHERE alert = %llu;",
new,
old);
sql_commit ();
if (new_alert) *new_alert = new;
return 0;
}
/**
* @brief Modify an alert.
*
* @param[in] alert_id UUID of alert.
* @param[in] name Name of alert.
* @param[in] comment Comment on alert.
* @param[in] filter_id Filter.
* @param[in] active Whether the alert is active. NULL to leave it
* at the current value.
* @param[in] event Type of event.
* @param[in] event_data Type-specific event data.
* @param[in] condition Event condition.
* @param[in] condition_data Condition-specific data.
* @param[in] method Escalation method.
* @param[in] method_data Data for escalation method.
*
* @return 0 success, 1 failed to find alert, 2 alert with new name exists,
* 3 alert_id required, 4 failed to find filter, 5 filter type must be
* result if specified, 6 Provided email address not valid,
* 7 unexpected condition data name, 8 syntax error in condition data,
* 9 email subject too long, 10 email message too long, 11 failed to
* find filter for condition, 12 error in Send host, 13 error in Send
* port, 14 failed to find report format for Send method, 15 error in
* SCP host, 17 failed to find report format for SCP method, 18 error
* in SCP credential, 19 error in SCP path, 20 method does not match
* event, 21 condition does not match event, 31 unexpected event data
* name, 32 syntax error in event data, 40 invalid SMB credential
* , 41 invalid SMB share path, 42 invalid SMB file path,
* 43 SMB file path contains dot,
* 50 invalid TippingPoint credential, 51 invalid TippingPoint hostname,
* 52 invalid TippingPoint certificate, 53 invalid TippingPoint TLS
* workaround setting, 60 recipient credential not found, 61 invalid
* recipient credential type, 70 vFire credential not found,
* 71 invalid vFire credential type,
* 99 permission denied, -1 internal error.
*/
int
modify_alert (const char *alert_id, const char *name, const char *comment,
const char *filter_id, const char *active, event_t event,
GPtrArray *event_data, alert_condition_t condition,
GPtrArray *condition_data, alert_method_t method,
GPtrArray *method_data)
{
int index, ret;
gchar *quoted_name, *quoted_comment, *item;
alert_t alert;
filter_t filter;
if (alert_id == NULL)
return 3;
sql_begin_immediate ();
assert (current_credentials.uuid);
if (acl_user_may ("modify_alert") == 0)
{
sql_rollback ();
return 99;
}
ret = check_alert_params (event, condition, method);
if (ret)
{
sql_rollback ();
return ret;
}
alert = 0;
if (find_alert_with_permission (alert_id, &alert, "modify_alert"))
{
sql_rollback ();
return -1;
}
if (alert == 0)
{
sql_rollback ();
return 1;
}
/* Check whether an alert with the same name exists already. */
if (resource_with_name_exists (name, "alert", alert))
{
sql_rollback ();
return 2;
}
/* Check filter. */
filter = 0;
if (event != EVENT_NEW_SECINFO && event != EVENT_UPDATED_SECINFO && filter_id
&& strcmp (filter_id, "0"))
{
char *type;
if (find_filter_with_permission (filter_id, &filter, "get_filters"))
{
sql_rollback ();
return -1;
}
if (filter == 0)
{
sql_rollback ();
return 4;
}
/* Filter type must be report if specified. */
type = sql_string ("SELECT type FROM filters WHERE id = %llu;",
filter);
if (type && strcasecmp (type, "result"))
{
free (type);
sql_rollback ();
return 5;
}
free (type);
}
quoted_name = sql_quote (name ?: "");
quoted_comment = sql_quote (comment ? comment : "");
sql ("UPDATE alerts SET"
" name = '%s',"
" comment = '%s',"
" filter = %llu,"
" active = %s,"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_name,
quoted_comment,
filter,
active
? (strcmp (active, "0") ? "1" : "0")
: "active",
alert);
g_free (quoted_comment);
g_free (quoted_name);
/* Modify alert event */
if (event != EVENT_ERROR)
{
sql ("UPDATE alerts set event = %i WHERE id = %llu", event, alert);
sql ("DELETE FROM alert_event_data WHERE alert = %llu", alert);
index = 0;
while ((item = (gchar*) g_ptr_array_index (event_data, index++)))
{
int validation_result;
gchar *data_name = sql_quote (item);
gchar *data = sql_quote (item + strlen (item) + 1);
validation_result = validate_alert_event_data (data_name,
data,
event);
if (validation_result)
{
g_free (data_name);
g_free (data);
sql_rollback ();
switch (validation_result)
{
case 1:
return 31;
case 2:
return 32;
default:
return -1;
}
}
sql ("INSERT INTO alert_event_data (alert, name, data)"
" VALUES (%llu, '%s', '%s');",
alert,
data_name,
data);
g_free (data_name);
g_free (data);
}
}
/* Modify alert condition */
if (condition != ALERT_CONDITION_ERROR)
{
sql ("UPDATE alerts set condition = %i WHERE id = %llu",
condition,
alert);
sql ("DELETE FROM alert_condition_data WHERE alert = %llu", alert);
index = 0;
while ((item = (gchar*) g_ptr_array_index (condition_data, index++)))
{
int validation_result;
gchar *data_name = sql_quote (item);
gchar *data = sql_quote (item + strlen (item) + 1);
validation_result = validate_alert_condition_data (data_name, data,
condition);
if (validation_result)
{
g_free (data_name);
g_free (data);
sql_rollback ();
switch (validation_result)
{
case 1:
return 7;
case 2:
return 8;
case 3:
return 11;
default:
return -1;
}
}
sql ("INSERT INTO alert_condition_data (alert, name, data)"
" VALUES (%llu, '%s', '%s');",
alert,
data_name,
data);
g_free (data_name);
g_free (data);
}
}
/* Modify alert method */
if (method != ALERT_METHOD_ERROR)
{
sql ("UPDATE alerts set method = %i WHERE id = %llu", method, alert);
sql ("DELETE FROM alert_method_data WHERE alert = %llu", alert);
index = 0;
while ((item = (gchar*) g_ptr_array_index (method_data, index++)))
{
gchar *data_name, *data;
data_name = sql_quote (item);
data = sql_quote (item + strlen (item) + 1);
ret = validate_email_data (method, data_name, &data, 1);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_scp_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_send_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_smb_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_sourcefire_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_tippingpoint_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
ret = validate_vfire_data (method, data_name, &data);
if (ret)
{
g_free (data_name);
g_free (data);
sql_rollback ();
return ret;
}
sql ("INSERT INTO alert_method_data (alert, name, data)"
" VALUES (%llu, '%s', '%s');",
alert,
data_name,
data);
g_free (data_name);
g_free (data);
}
}
sql_commit ();
return 0;
}
/**
* @brief Delete an alert.
*
* @param[in] alert_id UUID of alert.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 fail because a task refers to the alert, 2 failed
* to find target, 99 permission denied, -1 error.
*/
int
delete_alert (const char *alert_id, int ultimate)
{
alert_t alert = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_alert") == 0)
{
sql_rollback ();
return 99;
}
if (find_alert_with_permission (alert_id, &alert, "delete_alert"))
{
sql_rollback ();
return -1;
}
if (alert == 0)
{
if (find_trash ("alert", alert_id, &alert))
{
sql_rollback ();
return -1;
}
if (alert == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
/* Check if it's in use by a task in the trashcan. */
if (sql_int ("SELECT count(*) FROM task_alerts"
" WHERE alert = %llu"
" AND alert_location = " G_STRINGIFY (LOCATION_TRASH) ";",
alert))
{
sql_rollback ();
return 1;
}
permissions_set_orphans ("alert", alert, LOCATION_TRASH);
tags_remove_resource ("alert", alert, LOCATION_TRASH);
sql ("DELETE FROM alert_condition_data_trash WHERE alert = %llu;",
alert);
sql ("DELETE FROM alert_event_data_trash WHERE alert = %llu;",
alert);
sql ("DELETE FROM alert_method_data_trash WHERE alert = %llu;",
alert);
sql ("DELETE FROM alerts_trash WHERE id = %llu;", alert);
sql_commit ();
return 0;
}
if (ultimate == 0)
{
alert_t trash_alert;
if (sql_int ("SELECT count(*) FROM task_alerts"
" WHERE alert = %llu"
" AND alert_location = " G_STRINGIFY (LOCATION_TABLE)
" AND (SELECT hidden < 2 FROM tasks"
" WHERE id = task_alerts.task);",
alert))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO alerts_trash"
" (uuid, owner, name, comment, event, condition, method, filter,"
" filter_location, active, creation_time, modification_time)"
" SELECT uuid, owner, name, comment, event, condition, method,"
" filter, " G_STRINGIFY (LOCATION_TABLE) ", active,"
" creation_time, m_now ()"
" FROM alerts WHERE id = %llu;",
alert);
trash_alert = sql_last_insert_id ();
sql ("INSERT INTO alert_condition_data_trash"
" (alert, name, data)"
" SELECT %llu, name, data"
" FROM alert_condition_data WHERE alert = %llu;",
trash_alert,
alert);
sql ("INSERT INTO alert_event_data_trash"
" (alert, name, data)"
" SELECT %llu, name, data"
" FROM alert_event_data WHERE alert = %llu;",
trash_alert,
alert);
sql ("INSERT INTO alert_method_data_trash"
" (alert, name, data)"
" SELECT %llu, name, data"
" FROM alert_method_data WHERE alert = %llu;",
trash_alert,
alert);
/* Update the location of the alert in any trashcan tasks. */
sql ("UPDATE task_alerts"
" SET alert = %llu,"
" alert_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE alert = %llu"
" AND alert_location = " G_STRINGIFY (LOCATION_TABLE) ";",
trash_alert,
alert);
permissions_set_locations ("alert", alert, trash_alert,
LOCATION_TRASH);
tags_set_locations ("alert", alert, trash_alert,
LOCATION_TRASH);
}
else if (sql_int ("SELECT count(*) FROM task_alerts"
" WHERE alert = %llu"
" AND alert_location = " G_STRINGIFY (LOCATION_TABLE) ";",
alert))
{
sql_rollback ();
return 1;
}
else
{
permissions_set_orphans ("alert", alert, LOCATION_TABLE);
tags_remove_resource ("alert", alert, LOCATION_TABLE);
}
sql ("DELETE FROM alert_condition_data WHERE alert = %llu;",
alert);
sql ("DELETE FROM alert_event_data WHERE alert = %llu;", alert);
sql ("DELETE FROM alert_method_data WHERE alert = %llu;", alert);
sql ("DELETE FROM alerts WHERE id = %llu;", alert);
sql_commit ();
return 0;
}
/**
* @brief Return the UUID of an alert.
*
* @param[in] alert Alert.
*
* @return UUID of alert.
*/
char *
alert_uuid (alert_t alert)
{
return sql_string ("SELECT uuid FROM alerts WHERE id = %llu;",
alert);
}
/**
* @brief Return the name of an alert.
*
* @param[in] alert Alert.
*
* @return Name of alert.
*/
static char *
alert_name (alert_t alert)
{
return sql_string ("SELECT name FROM alerts WHERE id = %llu;", alert);
}
/**
* @brief Return the owner of an alert.
*
* @param[in] alert Alert.
*
* @return Owner.
*/
static user_t
alert_owner (alert_t alert)
{
return sql_int64_0 ("SELECT owner FROM alerts WHERE id = %llu;",
alert);
}
/**
* @brief Return the UUID of the owner of an alert.
*
* @param[in] alert Alert.
*
* @return UUID of owner.
*/
static char *
alert_owner_uuid (alert_t alert)
{
return sql_string ("SELECT uuid FROM users"
" WHERE id = (SELECT owner FROM alerts WHERE id = %llu);",
alert);
}
/**
* @brief Return the UUID of the filter of an alert.
*
* @param[in] alert Alert.
*
* @return UUID if there's a filter, else NULL.
*/
static char *
alert_filter_id (alert_t alert)
{
return sql_string ("SELECT"
" (CASE WHEN (SELECT filter IS NULL OR filter = 0"
" FROM alerts WHERE id = %llu)"
" THEN NULL"
" ELSE (SELECT uuid FROM filters"
" WHERE id = (SELECT filter FROM alerts"
" WHERE id = %llu))"
" END);",
alert,
alert);
}
/**
* @brief Return the condition associated with an alert.
*
* @param[in] alert Alert.
*
* @return Condition.
*/
static alert_condition_t
alert_condition (alert_t alert)
{
return sql_int ("SELECT condition FROM alerts WHERE id = %llu;",
alert);
}
/**
* @brief Return the method associated with an alert.
*
* @param[in] alert Alert.
*
* @return Method.
*/
static alert_method_t
alert_method (alert_t alert)
{
return sql_int ("SELECT method FROM alerts WHERE id = %llu;",
alert);
}
/**
* @brief Return the event associated with an alert.
*
* @param[in] alert Alert.
*
* @return Event.
*/
static event_t
alert_event (alert_t alert)
{
return sql_int ("SELECT event FROM alerts WHERE id = %llu;",
alert);
}
/**
* @brief Filter columns for alert iterator.
*/
#define ALERT_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "event", "condition", "method", \
"filter", NULL }
/**
* @brief Alert iterator columns.
*/
#define ALERT_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (alerts), \
{ "event", NULL, KEYWORD_TYPE_INTEGER }, \
{ "condition", NULL, KEYWORD_TYPE_INTEGER }, \
{ "method", NULL, KEYWORD_TYPE_INTEGER }, \
{ "filter", NULL, KEYWORD_TYPE_INTEGER }, \
{ G_STRINGIFY (LOCATION_TABLE), NULL, KEYWORD_TYPE_INTEGER }, \
{ "active", NULL, KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Alert iterator columns for trash case.
*/
#define ALERT_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (alerts_trash), \
{ "event", NULL, KEYWORD_TYPE_INTEGER }, \
{ "condition", NULL, KEYWORD_TYPE_INTEGER }, \
{ "method", NULL, KEYWORD_TYPE_INTEGER }, \
{ "filter", NULL, KEYWORD_TYPE_STRING }, \
{ "filter_location", NULL, KEYWORD_TYPE_INTEGER}, \
{ "active", NULL, KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count the number of alerts.
*
* @param[in] get GET params.
*
* @return Total number of alerts filtered set.
*/
int
alert_count (const get_data_t *get)
{
static const char *filter_columns[] = ALERT_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = ALERT_ITERATOR_COLUMNS;
static column_t trash_columns[] = ALERT_ITERATOR_TRASH_COLUMNS;
return count ("alert", get, columns, trash_columns, filter_columns, 0, 0, 0,
TRUE);
}
/**
* @brief Return whether a alert is in use by a task.
*
* @param[in] alert Alert.
*
* @return 1 if in use, else 0.
*/
int
alert_in_use (alert_t alert)
{
return !!sql_int ("SELECT count (*) FROM task_alerts WHERE alert = %llu;",
alert);
}
/**
* @brief Return whether a trashcan alert is in use by a task.
*
* @param[in] alert Alert.
*
* @return 1 if in use, else 0.
*/
int
trash_alert_in_use (alert_t alert)
{
return !!sql_int ("SELECT count(*) FROM task_alerts"
" WHERE alert = %llu"
" AND alert_location = " G_STRINGIFY (LOCATION_TRASH),
alert);
}
/**
* @brief Return whether a alert is writable.
*
* @param[in] alert Alert.
*
* @return 1 if writable, else 0.
*/
int
alert_writable (alert_t alert)
{
return 1;
}
/**
* @brief Return whether a trashcan alert is writable.
*
* @param[in] alert Alert.
*
* @return 1 if writable, else 0.
*/
int
trash_alert_writable (alert_t alert)
{
return 1;
}
/**
* @brief Initialise an alert iterator, including observed alerts.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find alert, 2 failed to find filter (filt_id),
* -1 error.
*/
int
init_alert_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = ALERT_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = ALERT_ITERATOR_COLUMNS;
static column_t trash_columns[] = ALERT_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"alert",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Return the event from an alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Event of the alert or NULL if iteration is complete.
*/
int
alert_iterator_event (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT);
return ret;
}
/**
* @brief Return the condition from an alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Condition of the alert or NULL if iteration is complete.
*/
int
alert_iterator_condition (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
return ret;
}
/**
* @brief Return the method from an alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Method of the alert or NULL if iteration is complete.
*/
int
alert_iterator_method (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
return ret;
}
/**
* @brief Return the filter from an alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Filter of the alert or NULL if iteration is complete.
*/
static filter_t
alert_iterator_filter (iterator_t* iterator)
{
if (iterator->done) return -1;
return (filter_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
}
/**
* @brief Return the filter UUID from an alert iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID of filter of the alert or NULL if iteration is complete.
*/
char *
alert_iterator_filter_uuid (iterator_t* iterator)
{
filter_t filter;
if (iterator->done) return NULL;
filter = alert_iterator_filter (iterator);
if (filter)
{
if (iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
== LOCATION_TABLE)
return filter_uuid (filter);
return trash_filter_uuid (filter);
}
return NULL;
}
/**
* @brief Return the filter name from an alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Name of filter of the alert or NULL if iteration is complete.
*/
char *
alert_iterator_filter_name (iterator_t* iterator)
{
filter_t filter;
if (iterator->done) return NULL;
filter = alert_iterator_filter (iterator);
if (filter)
{
if (iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
== LOCATION_TABLE)
return filter_name (filter);
return trash_filter_name (filter);
}
return NULL;
}
/**
* @brief Return the location of an alert iterator filter.
*
* @param[in] iterator Iterator.
*
* @return 0 in table, 1 in trash.
*/
int
alert_iterator_filter_trash (iterator_t* iterator)
{
if (iterator->done) return 0;
if (alert_iterator_filter (iterator)
&& (iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
== LOCATION_TRASH))
return 1;
return 0;
}
/**
* @brief Return the filter readable state from an alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Whether filter is readable.
*/
int
alert_iterator_filter_readable (iterator_t* iterator)
{
filter_t filter;
if (iterator->done) return 0;
filter = alert_iterator_filter (iterator);
if (filter)
{
char *uuid;
uuid = alert_iterator_filter_uuid (iterator);
if (uuid)
{
int readable;
readable = acl_user_has_access_uuid
("filter", uuid, "get_filters",
iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
== LOCATION_TRASH);
free (uuid);
return readable;
}
}
return 0;
}
/**
* @brief Return the active state from an alert.
*
* @param[in] iterator Iterator.
*
* @return Method of the alert or NULL if iteration is complete.
*/
int
alert_iterator_active (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
return ret;
}
/**
* @brief Initialise an alert data iterator.
*
* @param[in] iterator Iterator.
* @param[in] alert Alert.
* @param[in] trash Whether to iterate over trashcan alert data.
* @param[in] table Type of data: "condition", "event" or "method",
* corresponds to substring of the table to select
* from.
*/
void
init_alert_data_iterator (iterator_t *iterator, alert_t alert,
int trash, const char *table)
{
init_iterator (iterator,
"SELECT name, data FROM alert_%s_data%s"
" WHERE alert = %llu;",
table,
trash ? "_trash" : "",
alert);
}
/**
* @brief Return the name from an alert data iterator.
*
* @param[in] iterator Iterator.
*
* @return Name of the alert data or NULL if iteration is complete.
*/
const char*
alert_data_iterator_name (iterator_t* iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = iterator_string (iterator, 0);
return ret;
}
/**
* @brief Return the data from an alert data iterator.
*
* @param[in] iterator Iterator.
*
*
* @return Data of the alert data or NULL if iteration is complete.
*/
const char*
alert_data_iterator_data (iterator_t* iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = iterator_string (iterator, 1);
return ret;
}
/**
* @brief Return data associated with an alert.
*
* @param[in] alert Alert.
* @param[in] type Type of data: "condition", "event" or "method".
* @param[in] name Name of the data.
*
* @return Freshly allocated data if it exists, else NULL.
*/
char *
alert_data (alert_t alert, const char *type, const char *name)
{
gchar *quoted_name;
char *data;
assert (strcmp (type, "condition") == 0
|| strcmp (type, "event") == 0
|| strcmp (type, "method") == 0);
quoted_name = sql_quote (name);
data = sql_string ("SELECT data FROM alert_%s_data"
" WHERE alert = %llu AND name = '%s';",
type,
alert,
quoted_name);
g_free (quoted_name);
return data;
}
/**
* @brief Check whether an alert applies to a task.
*
* @param[in] alert Alert.
* @param[in] task Task.
*
* @return 1 if applies, else 0.
*/
static int
alert_applies_to_task (alert_t alert, task_t task)
{
return sql_int ("SELECT EXISTS (SELECT * FROM task_alerts"
" WHERE task = %llu"
" AND alert = %llu);",
task,
alert);
}
/**
* @brief Initialise a task alert iterator.
*
* @param[in] iterator Iterator.
* @param[in] task Task.
*/
void
init_task_alert_iterator (iterator_t* iterator, task_t task)
{
gchar *owned_clause, *with_clause;
get_data_t get;
array_t *permissions;
assert (task);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_alerts"));
owned_clause = acl_where_owned ("alert", &get, 0, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT alerts.id, alerts.uuid, alerts.name"
" FROM alerts, task_alerts"
" WHERE task_alerts.task = %llu"
" AND task_alerts.alert = alerts.id"
" AND %s;",
with_clause ? with_clause : "",
task,
owned_clause);
g_free (with_clause);
g_free (owned_clause);
}
/**
* @brief Get the UUID from a task alert iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (task_alert_iterator_uuid, 1);
/**
* @brief Get the name from a task alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (task_alert_iterator_name, 2);
/**
* @brief Initialise an event alert iterator.
*
* @param[in] iterator Iterator.
* @param[in] event Event.
*/
static void
init_event_alert_iterator (iterator_t* iterator, event_t event)
{
gchar *owned_clause, *with_clause;
get_data_t get;
array_t *permissions;
assert (event);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_alerts"));
owned_clause = acl_where_owned ("alert", &get, 0, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT alerts.id, alerts.active"
" FROM alerts"
" WHERE event = %i"
" AND %s;",
with_clause ? with_clause : "",
event,
owned_clause);
g_free (with_clause);
g_free (owned_clause);
}
/**
* @brief Get the alert from a event alert iterator.
*
* @param[in] iterator Iterator.
*
* @return alert.
*/
static alert_t
event_alert_iterator_alert (iterator_t* iterator)
{
if (iterator->done) return 0;
return (task_t) iterator_int64 (iterator, 0);
}
/**
* @brief Get the active state from an event alert iterator.
*
* @param[in] iterator Iterator.
*
* @return Active state.
*/
static int
event_alert_iterator_active (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, 1);
return ret;
}
/**
* @brief Write the content of a plain text email to a stream.
*
* @param[in] content_file Stream to write the email content to.
* @param[in] to_address Address to send to.
* @param[in] from_address Address to send to.
* @param[in] subject Subject of email.
* @param[in] body Body of email.
* @param[in] attachment Attachment in line broken base64, or NULL.
* @param[in] attachment_type Attachment MIME type, or NULL.
* @param[in] attachment_name Base file name of the attachment, or NULL.
* @param[in] attachment_extension Attachment file extension, or NULL.
*
* @return 0 success, -1 error.
*/
static int
email_write_content (FILE *content_file,
const char *to_address, const char *from_address,
const char *subject, const char *body,
const gchar *attachment, const char *attachment_type,
const char *attachment_name,
const char *attachment_extension)
{
if (fprintf (content_file,
"To: %s\n"
"From: %s\n"
"Subject: %s\n"
"%s%s%s"
"\n"
"%s"
"%s\n",
to_address,
from_address ? from_address
: "automated@openvas.org",
subject,
(attachment
? "MIME-Version: 1.0\n"
"Content-Type: multipart/mixed;"
" boundary=\""
: "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"),
/* @todo Future callers may give email containing this string. */
(attachment ? "=-=-=-=-=" : ""),
(attachment ? "\"\n" : ""),
(attachment ? "--=-=-=-=-=\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Content-Disposition: inline\n"
"\n"
: ""),
body)
< 0)
{
g_warning ("%s: output error", __func__);
return -1;
}
if (attachment)
{
int len;
if (fprintf (content_file,
"--=-=-=-=-=\n"
"Content-Type: %s\n"
"Content-Disposition: attachment;"
" filename=\"%s.%s\"\n"
"Content-Transfer-Encoding: base64\n"
"Content-Description: Report\n\n",
attachment_type,
attachment_name,
attachment_extension)
< 0)
{
g_warning ("%s: output error", __func__);
return -1;
}
len = strlen (attachment);
while (len)
if (len > 72)
{
if (fprintf (content_file,
"%.*s\n",
72,
attachment)
< 0)
{
g_warning ("%s: output error", __func__);
return -1;
}
attachment += 72;
len -= 72;
}
else
{
if (fprintf (content_file,
"%s\n",
attachment)
< 0)
{
g_warning ("%s: output error", __func__);
return -1;
}
break;
}
if (fprintf (content_file,
"--=-=-=-=-=--\n")
< 0)
{
g_warning ("%s: output error", __func__);
return -1;
}
}
while (fflush (content_file))
if (errno == EINTR)
continue;
else
{
g_warning ("%s", strerror (errno));
return -1;
}
return 0;
}
/**
* @brief Create a PGP encrypted email from a plain text one.
*
* @param[in] plain_file Stream to read the plain text email from.
* @param[in] encrypted_file Stream to write the encrypted email to.
* @param[in] public_key Recipient public key to use for encryption.
* @param[in] to_address Email address to send to.
* @param[in] from_address Email address to use as sender.
* @param[in] subject Subject of email.
*
* @return 0 success, -1 error.
*/
static int
email_encrypt_gpg (FILE *plain_file, FILE *encrypted_file,
const char *public_key,
const char *to_address, const char *from_address,
const char *subject)
{
// Headers and metadata parts
if (fprintf (encrypted_file,
"To: %s\n"
"From: %s\n"
"Subject: %s\n"
"MIME-Version: 1.0\n"
"Content-Type: multipart/encrypted;\n"
" protocol=\"application/pgp-encrypted\";\n"
" boundary=\"=-=-=-=-=\"\n"
"\n"
"--=-=-=-=-=\n"
"Content-Type: application/pgp-encrypted\n"
"Content-Description: PGP/MIME version identification\n"
"\n"
"Version: 1\n"
"\n"
"--=-=-=-=-=\n"
"Content-Type: application/octet-stream\n"
"Content-Description: OpenPGP encrypted message\n"
"Content-Disposition: inline; filename=\"encrypted.asc\"\n"
"\n",
to_address,
from_address ? from_address
: "automated@openvas.org",
subject) < 0)
{
g_warning ("%s: output error at headers", __func__);
return -1;
}
// Encrypted message
if (gvm_pgp_pubkey_encrypt_stream (plain_file, encrypted_file, to_address,
public_key, -1))
{
return -1;
}
// End of message
if (fprintf (encrypted_file,
"\n"
"--=-=-=-=-=--\n") < 0)
{
g_warning ("%s: output error at end of message", __func__);
return -1;
}
while (fflush (encrypted_file))
if (errno == EINTR)
continue;
else
{
g_warning ("%s", strerror (errno));
return -1;
}
return 0;
}
/**
* @brief Create an S/MIME encrypted email from a plain text one.
*
* @param[in] plain_file Stream to read the plain text email from.
* @param[in] encrypted_file Stream to write the encrypted email to.
* @param[in] certificate Recipient certificate chain for encryption.
* @param[in] to_address Email address to send to.
* @param[in] from_address Email address to use as sender.
* @param[in] subject Subject of email.
*
* @return 0 success, -1 error.
*/
static int
email_encrypt_smime (FILE *plain_file, FILE *encrypted_file,
const char *certificate,
const char *to_address, const char *from_address,
const char *subject)
{
// Headers and metadata parts
if (fprintf (encrypted_file,
"To: %s\n"
"From: %s\n"
"Subject: %s\n"
"Content-Type: application/x-pkcs7-mime;"
" smime-type=enveloped-data; name=\"smime.p7m\"\n"
"Content-Disposition: attachment; filename=\"smime.p7m\"\n"
"Content-Transfer-Encoding: base64\n"
"\n",
to_address,
from_address ? from_address
: "automated@openvas.org",
subject) < 0)
{
g_warning ("%s: output error at headers", __func__);
return -1;
}
// Encrypted message
if (gvm_smime_encrypt_stream (plain_file, encrypted_file, to_address,
certificate, -1))
{
g_warning ("%s: encryption failed", __func__);
return -1;
}
// End of message
if (fprintf (encrypted_file,
"\n") < 0)
{
g_warning ("%s: output error at end of message", __func__);
return -1;
}
while (fflush (encrypted_file))
if (errno == EINTR)
continue;
else
{
g_warning ("%s", strerror (errno));
return -1;
}
return 0;
}
/**
* @brief Send an email.
*
* @param[in] to_address Address to send to.
* @param[in] from_address Address to send to.
* @param[in] subject Subject of email.
* @param[in] body Body of email.
* @param[in] attachment Attachment in line broken base64, or NULL.
* @param[in] attachment_type Attachment MIME type, or NULL.
* @param[in] attachment_name Base file name of the attachment, or NULL.
* @param[in] attachment_extension Attachment file extension, or NULL.
* @param[in] recipient_credential Optional credential to use for encryption.
*
* @return 0 success, -1 error.
*/
static int
email (const char *to_address, const char *from_address, const char *subject,
const char *body, const gchar *attachment, const char *attachment_type,
const char *attachment_name, const char *attachment_extension,
credential_t recipient_credential)
{
int ret, content_fd, args_fd;
gchar *command;
GError *error = NULL;
char content_file_name[] = "/tmp/gvmd-content-XXXXXX";
char args_file_name[] = "/tmp/gvmd-args-XXXXXX";
gchar *sendmail_args;
FILE *content_file;
content_fd = mkstemp (content_file_name);
if (content_fd == -1)
{
g_warning ("%s: mkstemp: %s", __func__, strerror (errno));
return -1;
}
g_debug (" EMAIL to %s from %s subject: %s, body: %s",
to_address, from_address, subject, body);
content_file = fdopen (content_fd, "w");
if (content_file == NULL)
{
g_warning ("%s: Could not open content file: %s",
__func__, strerror (errno));
close (content_fd);
return -1;
}
if (recipient_credential)
{
iterator_t iterator;
init_credential_iterator_one (&iterator, recipient_credential);
if (next (&iterator))
{
const char *type = credential_iterator_type (&iterator);
const char *public_key = credential_iterator_public_key (&iterator);
const char *certificate
= credential_iterator_certificate (&iterator);
char plain_file_name[] = "/tmp/gvmd-plain-XXXXXX";
int plain_fd;
FILE *plain_file;
// Create plain text message
plain_fd = mkstemp (plain_file_name);
if (plain_fd == -1)
{
g_warning ("%s: mkstemp for plain text file: %s",
__func__, strerror (errno));
fclose (content_file);
unlink (content_file_name);
cleanup_iterator (&iterator);
return -1;
}
plain_file = fdopen (plain_fd, "w+");
if (plain_file == NULL)
{
g_warning ("%s: Could not open plain text file: %s",
__func__, strerror (errno));
fclose (content_file);
unlink (content_file_name);
close (plain_fd);
unlink (plain_file_name);
cleanup_iterator (&iterator);
return -1;
}
if (email_write_content (plain_file,
to_address, from_address,
subject, body, attachment,
attachment_type, attachment_name,
attachment_extension))
{
fclose (content_file);
unlink (content_file_name);
fclose (plain_file);
unlink (plain_file_name);
cleanup_iterator (&iterator);
return -1;
}
rewind (plain_file);
// Create encrypted email
if (strcmp (type, "pgp") == 0)
{
ret = email_encrypt_gpg (plain_file, content_file,
public_key,
to_address, from_address, subject);
fclose (plain_file);
unlink (plain_file_name);
if (ret)
{
g_warning ("%s: PGP encryption failed", __func__);
fclose (content_file);
unlink (content_file_name);
cleanup_iterator (&iterator);
return -1;
}
}
else if (strcmp (type, "smime") == 0)
{
ret = email_encrypt_smime (plain_file, content_file,
certificate,
to_address, from_address, subject);
fclose (plain_file);
unlink (plain_file_name);
if (ret)
{
g_warning ("%s: S/MIME encryption failed", __func__);
fclose (content_file);
unlink (content_file_name);
cleanup_iterator (&iterator);
return -1;
}
}
else
{
g_warning ("%s: Invalid recipient credential type",
__func__);
fclose (content_file);
unlink (content_file_name);
fclose (plain_file);
unlink (plain_file_name);
cleanup_iterator (&iterator);
return -1;
}
}
cleanup_iterator (&iterator);
}
else
{
if (email_write_content (content_file,
to_address, from_address,
subject, body, attachment, attachment_type,
attachment_name, attachment_extension))
{
fclose (content_file);
return -1;
}
}
args_fd = mkstemp (args_file_name);
if (args_fd == -1)
{
g_warning ("%s: mkstemp: %s", __func__, strerror (errno));
fclose (content_file);
return -1;
}
sendmail_args = g_strdup_printf ("%s %s",
from_address,
to_address);
g_file_set_contents (args_file_name,
sendmail_args,
strlen (sendmail_args),
&error);
g_free (sendmail_args);
if (error)
{
g_warning ("%s", error->message);
g_error_free (error);
fclose (content_file);
close (args_fd);
return -1;
}
command = g_strdup_printf ("read FROM TO < %s;"
" /usr/sbin/sendmail -f \"$FROM\" \"$TO\" < %s"
" > /dev/null 2>&1",
args_file_name,
content_file_name);
g_debug (" command: %s", command);
ret = system (command);
if ((ret == -1) || WEXITSTATUS (ret))
{
g_warning ("%s: system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
command);
g_free (command);
fclose (content_file);
close (args_fd);
unlink (content_file_name);
unlink (args_file_name);
return -1;
}
g_free (command);
fclose (content_file);
close (args_fd);
unlink (content_file_name);
unlink (args_file_name);
return 0;
}
/**
* @brief GET an HTTP resource.
*
* @param[in] url URL.
*
* @return 0 success, -1 error.
*/
static int
http_get (const char *url)
{
int ret;
gchar *standard_out = NULL;
gchar *standard_err = NULL;
gint exit_status;
gchar **cmd;
g_debug (" HTTP_GET %s", url);
cmd = (gchar **) g_malloc (5 * sizeof (gchar *));
cmd[0] = g_strdup ("/usr/bin/wget");
cmd[1] = g_strdup ("-O");
cmd[2] = g_strdup ("-");
cmd[3] = g_strdup (url);
cmd[4] = NULL;
g_debug ("%s: Spawning in /tmp/: %s %s %s %s",
__func__, cmd[0], cmd[1], cmd[2], cmd[3]);
if ((g_spawn_sync ("/tmp/",
cmd,
NULL, /* Environment. */
G_SPAWN_SEARCH_PATH,
NULL, /* Setup function. */
NULL,
&standard_out,
&standard_err,
&exit_status,
NULL)
== FALSE)
|| (WIFEXITED (exit_status) == 0)
|| WEXITSTATUS (exit_status))
{
g_debug ("%s: wget failed: %d (WIF %i, WEX %i)",
__func__,
exit_status,
WIFEXITED (exit_status),
WEXITSTATUS (exit_status));
g_debug ("%s: stdout: %s", __func__, standard_out);
g_debug ("%s: stderr: %s", __func__, standard_err);
ret = -1;
}
else
{
if (strlen (standard_out) > 80)
standard_out[80] = '\0';
g_debug (" HTTP_GET %s: %s", url, standard_out);
ret = 0;
}
g_free (cmd[0]);
g_free (cmd[1]);
g_free (cmd[2]);
g_free (cmd[3]);
g_free (cmd[4]);
g_free (cmd);
g_free (standard_out);
g_free (standard_err);
return ret;
}
/**
* @brief Initialize common files and variables for an alert script.
*
* The temporary file / dir parameters will be modified by mkdtemp / mkstemp
* to contain the actual path.
* The extra data is meant for data that should not be logged like passwords.
*
* @param[in] report_filename Filename for the report or NULL for default.
* @param[in] report Report that should be sent.
* @param[in] report_size Size of the report.
* @param[in] extra_content Optional extra data, e.g. credentials
* @param[in] extra_size Optional extra data length
* @param[in,out] report_dir Template for temporary report directory
* @param[out] report_path Pointer to store path to report file at
* @param[out] error_path Pointer to temporary file path for error messages
* @param[out] extra_path Pointer to temporary extra data file path
*
* @return 0 success, -1 error.
*/
static int
alert_script_init (const char *report_filename, const char* report,
size_t report_size,
const char *extra_content, size_t extra_size,
char *report_dir,
gchar **report_path, gchar **error_path, gchar **extra_path)
{
GError *error;
/* Create temp directory */
if (mkdtemp (report_dir) == NULL)
{
g_warning ("%s: mkdtemp failed", __func__);
return -1;
}
/* Create report file */
*report_path = g_strdup_printf ("%s/%s",
report_dir,
report_filename ? report_filename
: "report");
error = NULL;
g_file_set_contents (*report_path, report, report_size, &error);
if (error)
{
g_warning ("%s: could not write report: %s",
__func__, error->message);
g_error_free (error);
g_free (*report_path);
gvm_file_remove_recurse (report_dir);
return -1;
}
/* Create error file */
*error_path = g_strdup_printf ("%s/error_XXXXXX", report_dir);
if (mkstemp (*error_path) == -1)
{
g_warning ("%s: mkstemp for error output failed", __func__);
gvm_file_remove_recurse (report_dir);
g_free (*report_path);
g_free (*error_path);
return -1;
}
/* Create extra data file */
if (extra_content)
{
*extra_path = g_strdup_printf ("%s/extra_XXXXXX", report_dir);
if (mkstemp (*extra_path) == -1)
{
g_warning ("%s: mkstemp for extra data failed", __func__);
gvm_file_remove_recurse (report_dir);
g_free (*report_path);
g_free (*error_path);
g_free (*extra_path);
return -1;
}
error = NULL;
g_file_set_contents (*extra_path, extra_content, extra_size, &error);
if (error)
{
g_warning ("%s: could not write extra data: %s",
__func__, error->message);
g_error_free (error);
gvm_file_remove_recurse (report_dir);
g_free (*report_path);
g_free (*error_path);
g_free (*extra_path);
return -1;
}
}
else
*extra_path = NULL;
return 0;
}
/**
* @brief Execute the alert script.
*
* @param[in] alert_id UUID of the alert.
* @param[in] command_args Args for the "alert" script.
* @param[in] report_path Path to temporary file containing the report
* @param[in] report_dir Temporary directory for the report
* @param[in] error_path Path to the script error message file
* @param[in] extra_path Path to the extra data file
* @param[out] message Custom error message generated by the script
*
* @return 0 success, -1 error, -5 alert script failed.
*/
static int
alert_script_exec (const char *alert_id, const char *command_args,
const char *report_path, const char *report_dir,
const char *error_path, const char *extra_path,
gchar **message)
{
gchar *script, *script_dir;
/* Setup script file name. */
script_dir = g_build_filename (GVMD_DATA_DIR,
"global_alert_methods",
alert_id,
NULL);
script = g_build_filename (script_dir, "alert", NULL);
if (!gvm_file_is_readable (script))
{
g_warning ("%s: Failed to find alert script: %s",
__func__,
script);
g_free (script);
g_free (script_dir);
return -1;
}
/* Run the script */
{
gchar *command;
char *previous_dir;
int ret;
/* Change into the script directory. */
previous_dir = getcwd (NULL, 0);
if (previous_dir == NULL)
{
g_warning ("%s: Failed to getcwd: %s",
__func__,
strerror (errno));
g_free (previous_dir);
g_free (script);
g_free (script_dir);
return -1;
}
if (chdir (script_dir))
{
g_warning ("%s: Failed to chdir: %s",
__func__,
strerror (errno));
g_free (previous_dir);
g_free (script);
g_free (script_dir);
return -1;
}
g_free (script_dir);
/* Call the script. */
if (extra_path)
command = g_strdup_printf ("%s %s %s %s"
" > /dev/null 2> %s",
script,
command_args,
extra_path,
report_path,
error_path);
else
command = g_strdup_printf ("%s %s %s"
" > /dev/null 2> %s",
script,
command_args,
report_path,
error_path);
g_free (script);
g_debug (" command: %s", command);
if (geteuid () == 0)
{
pid_t pid;
struct passwd *nobody;
/* Run the command with lower privileges in a fork. */
nobody = getpwnam ("nobody");
if ((nobody == NULL)
|| chown (report_dir, nobody->pw_uid, nobody->pw_gid)
|| chown (report_path, nobody->pw_uid, nobody->pw_gid)
|| chown (error_path, nobody->pw_uid, nobody->pw_gid)
|| (extra_path && chown (extra_path, nobody->pw_uid,
nobody->pw_gid)))
{
g_warning ("%s: Failed to set permissions for user nobody: %s",
__func__,
strerror (errno));
g_free (previous_dir);
g_free (command);
return -1;
}
pid = fork ();
switch (pid)
{
case 0:
{
/* Child. Drop privileges, run command, exit. */
cleanup_manage_process (FALSE);
proctitle_set ("gvmd: Running alert script");
if (setgroups (0,NULL))
{
g_warning ("%s (child): setgroups: %s",
__func__, strerror (errno));
exit (EXIT_FAILURE);
}
if (setgid (nobody->pw_gid))
{
g_warning ("%s (child): setgid: %s",
__func__,
strerror (errno));
exit (EXIT_FAILURE);
}
if (setuid (nobody->pw_uid))
{
g_warning ("%s (child): setuid: %s",
__func__,
strerror (errno));
exit (EXIT_FAILURE);
}
ret = system (command);
/*
* Check shell command exit status, assuming 0 means success.
*/
if (ret == -1)
{
g_warning ("%s (child):"
" system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
command);
exit (EXIT_FAILURE);
}
else if (ret != 0)
{
GError *error;
if (g_file_get_contents (error_path, message,
NULL, &error) == FALSE)
{
g_warning ("%s: failed to test error message: %s",
__func__, error->message);
g_error_free (error);
if (message)
g_free (*message);
exit (EXIT_FAILURE);
}
if (message == NULL)
exit (EXIT_FAILURE);
else if (*message == NULL || strcmp (*message, "") == 0)
{
g_free (*message);
*message
= g_strdup_printf ("Exited with code %d.",
WEXITSTATUS (ret));
if (g_file_set_contents (error_path, *message,
strlen (*message),
&error) == FALSE)
{
g_warning ("%s: failed to write error message:"
" %s",
__func__, error->message);
g_error_free (error);
g_free (*message);
exit (EXIT_FAILURE);
}
}
g_free (*message);
exit (2);
}
exit (EXIT_SUCCESS);
}
case -1:
/* Parent when error. */
g_warning ("%s: Failed to fork: %s",
__func__,
strerror (errno));
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
break;
default:
{
int status;
/* Parent on success. Wait for child, and check result. */
while (waitpid (pid, &status, 0) < 0)
{
if (errno == ECHILD)
{
g_warning ("%s: Failed to get child exit status",
__func__);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
if (errno == EINTR)
continue;
g_warning ("%s: wait: %s",
__func__,
strerror (errno));
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
if (WIFEXITED (status))
switch (WEXITSTATUS (status))
{
case EXIT_SUCCESS:
break;
case 2: // script failed
if (message)
{
GError *error = NULL;
if (g_file_get_contents (error_path, message,
NULL, &error) == FALSE)
{
g_warning ("%s: failed to get error message: %s",
__func__, error->message);
g_error_free (error);
}
if (strcmp (*message, "") == 0)
{
g_free (*message);
*message = NULL;
}
}
if (chdir (previous_dir))
g_warning ("%s: chdir failed",
__func__);
return -5;
case EXIT_FAILURE:
default:
g_warning ("%s: child failed, %s",
__func__,
command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
else
{
g_warning ("%s: child failed, %s",
__func__,
command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
/* Child succeeded, continue to process result. */
break;
}
}
}
else
{
/* Just run the command as the current user. */
ret = system (command);
/* Ignore the shell command exit status, because we've not
* specified what it must be in the past. */
if (ret == -1)
{
g_warning ("%s: system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
}
else if (ret)
{
if (message)
{
GError *error = NULL;
if (g_file_get_contents (error_path, message, NULL, &error)
== FALSE)
{
g_warning ("%s: failed to get error message: %s",
__func__, error->message);
g_error_free (error);
}
if (strcmp (*message, "") == 0)
{
g_free (*message);
*message = NULL;
}
if (*message == NULL)
{
*message
= g_strdup_printf ("Exited with code %d.",
WEXITSTATUS (ret));
}
}
g_free (previous_dir);
g_free (command);
return -5;
}
}
g_free (command);
/* Change back to the previous directory. */
if (chdir (previous_dir))
{
g_warning ("%s: Failed to chdir back: %s",
__func__,
strerror (errno));
g_free (previous_dir);
return -1;
}
g_free (previous_dir);
}
return 0;
}
/**
* @brief Write data to a file for use by an alert script.
*
* @param[in] directory Base directory to create the file in
* @param[in] filename Filename without directory
* @param[in] content The file content
* @param[in] content_size Size of the file content
* @param[in] description Short file description for error messages
* @param[out] file_path Return location of combined file path
*
* @return 0 success, -1 error
*/
static int
alert_write_data_file (const char *directory, const char *filename,
const char *content, gsize content_size,
const char *description, gchar **file_path)
{
gchar *path;
GError *error;
if (file_path)
*file_path = NULL;
/* Setup extra data file */
path = g_build_filename (directory, filename, NULL);
error = NULL;
if (g_file_set_contents (path, content, content_size, &error) == FALSE)
{
g_warning ("%s: Failed to write %s to file: %s",
__func__,
description ? description : "extra data",
error->message);
g_free (path);
return -1;
}
if (geteuid () == 0)
{
struct passwd *nobody;
/* Set the owner for the extra data file like the other
* files handled by alert_script_exec, to be able to
* run the command with lower privileges in a fork. */
nobody = getpwnam ("nobody");
if ((nobody == NULL)
|| chown (path, nobody->pw_uid, nobody->pw_gid))
{
g_warning ("%s: Failed to set permissions for user nobody: %s",
__func__,
strerror (errno));
g_free (path);
return -1;
}
}
if (file_path)
*file_path = path;
return 0;
}
/**
* @brief Clean up common files and variables for running alert script.
*
* @param[in] report_dir The temporary directory.
* @param[in] report_path The temporary report file path to free.
* @param[in] error_path The temporary error file path to free.
* @param[in] extra_path The temporary extra data file path to free.
*
* @return 0 success, -1 error.
*/
static int
alert_script_cleanup (const char *report_dir,
gchar *report_path, gchar *error_path, gchar *extra_path)
{
gvm_file_remove_recurse (report_dir);
g_free (report_path);
g_free (error_path);
g_free (extra_path);
return 0;
}
/**
* @brief Run an alert's "alert" script with one file of extra data.
*
* @param[in] alert_id ID of alert.
* @param[in] command_args Args for the "alert" script.
* @param[in] report_filename Optional report file name, default: "report"
* @param[in] report Report that should be sent.
* @param[in] report_size Size of the report.
* @param[in] extra_content Optional extra data like passwords
* @param[in] extra_size Size of the report.
* @param[out] message Custom error message of the script.
*
* @return 0 success, -1 error, -5 alert script failed.
*/
static int
run_alert_script (const char *alert_id, const char *command_args,
const char *report_filename, const char *report,
size_t report_size,
const char *extra_content, size_t extra_size,
gchar **message)
{
char report_dir[] = "/tmp/gvmd_alert_XXXXXX";
gchar *report_path, *error_path, *extra_path;
int ret;
if (message)
*message = NULL;
if (report == NULL)
return -1;
g_debug ("report: %s", report);
/* Setup files. */
ret = alert_script_init (report_filename, report, report_size,
extra_content, extra_size,
report_dir,
&report_path, &error_path, &extra_path);
if (ret)
return ret;
/* Run the script */
ret = alert_script_exec (alert_id, command_args, report_path, report_dir,
error_path, extra_path, message);
if (ret)
{
alert_script_cleanup (report_dir, report_path, error_path, extra_path);
return ret;
}
/* Remove the directory. */
ret = alert_script_cleanup (report_dir, report_path, error_path, extra_path);
return ret;
}
/**
* @brief Send an SNMP TRAP to a host.
*
* @param[in] community Community.
* @param[in] agent Agent.
* @param[in] message Message.
* @param[out] script_message Custom error message of the script.
*
* @return 0 success, -1 error, -5 alert script failed.
*/
static int
snmp_to_host (const char *community, const char *agent, const char *message,
gchar **script_message)
{
gchar *clean_community, *clean_agent, *clean_message, *command_args;
int ret;
g_debug ("SNMP to host: %s", agent);
if (community == NULL || agent == NULL || message == NULL)
{
g_warning ("%s: parameter was NULL", __func__);
return -1;
}
clean_community = g_shell_quote (community);
clean_agent = g_shell_quote (agent);
clean_message = g_shell_quote (message);
command_args = g_strdup_printf ("%s %s %s", clean_community, clean_agent,
clean_message);
g_free (clean_community);
g_free (clean_agent);
g_free (clean_message);
ret = run_alert_script ("9d435134-15d3-11e6-bf5c-28d24461215b", command_args,
"report", "", 0, NULL, 0, script_message);
g_free (command_args);
return ret;
}
/**
* @brief Send a report to a host via TCP.
*
* @param[in] host Address of host.
* @param[in] port Port of host.
* @param[in] report Report that should be sent.
* @param[in] report_size Size of the report.
* @param[out] script_message Custom error message of the script.
*
* @return 0 success, -1 error, -5 alert script failed.
*/
static int
send_to_host (const char *host, const char *port,
const char *report, int report_size,
gchar **script_message)
{
gchar *clean_host, *clean_port, *command_args;
int ret;
g_debug ("send to host: %s:%s", host, port);
if (host == NULL)
return -1;
clean_host = g_shell_quote (host);
clean_port = g_shell_quote (port);
command_args = g_strdup_printf ("%s %s", clean_host, clean_port);
g_free (clean_host);
g_free (clean_port);
ret = run_alert_script ("4a398d42-87c0-11e5-a1c0-28d24461215b", command_args,
"report", report, report_size, NULL, 0,
script_message);
g_free (command_args);
return ret;
}
/**
* @brief Send a report to a host via TCP.
*
* @param[in] username Username.
* @param[in] password Password or passphrase of private key.
* @param[in] private_key Private key or NULL for password-only auth.
* @param[in] host Address of host.
* @param[in] path Destination filename with path.
* @param[in] known_hosts Content for known_hosts file.
* @param[in] report Report that should be sent.
* @param[in] report_size Size of the report.
* @param[out] script_message Custom error message of the alert script.
*
* @return 0 success, -1 error, -5 alert script failed.
*/
static int
scp_to_host (const char *username, const char *password,
const char *private_key,
const char *host, const char *path, const char *known_hosts,
const char *report, int report_size, gchar **script_message)
{
const char *alert_id = "2db07698-ec49-11e5-bcff-28d24461215b";
char report_dir[] = "/tmp/gvmd_alert_XXXXXX";
gchar *report_path, *error_path, *password_path, *private_key_path;
gchar *clean_username, *clean_host, *clean_path, *clean_private_key_path;
gchar *clean_known_hosts, *command_args;
int ret;
g_debug ("scp to host: %s@%s:%s", username, host, path);
if (password == NULL || username == NULL || host == NULL || path == NULL)
return -1;
if (known_hosts == NULL)
known_hosts = "";
/* Setup files, including password but not private key */
ret = alert_script_init ("report", report, report_size,
password, strlen (password),
report_dir,
&report_path, &error_path, &password_path);
if (ret)
return -1;
if (private_key)
{
/* Setup private key here because alert_script_init and alert_script_exec
* only handle one extra file. */
if (alert_write_data_file (report_dir, "private_key",
private_key, strlen (private_key),
"private key", &private_key_path))
{
alert_script_cleanup (report_dir, report_path, error_path,
password_path);
g_free (private_key_path);
return -1;
}
}
else
private_key_path = g_strdup ("");
/* Create arguments */
clean_username = g_shell_quote (username);
clean_host = g_shell_quote (host);
clean_path = g_shell_quote (path);
clean_known_hosts = g_shell_quote (known_hosts);
clean_private_key_path = g_shell_quote (private_key_path);
command_args = g_strdup_printf ("%s %s %s %s %s",
clean_username,
clean_host,
clean_path,
clean_known_hosts,
clean_private_key_path);
g_free (clean_username);
g_free (clean_host);
g_free (clean_path);
g_free (clean_known_hosts);
g_free (clean_private_key_path);
/* Run script */
ret = alert_script_exec (alert_id, command_args, report_path, report_dir,
error_path, password_path, script_message);
g_free (command_args);
if (ret)
{
alert_script_cleanup (report_dir, report_path, error_path,
password_path);
g_free (private_key_path);
return ret;
}
/* Remove the directory and free path strings. */
ret = alert_script_cleanup (report_dir, report_path, error_path,
password_path);
g_free (private_key_path);
return ret;
}
/**
* @brief Send a report to a host via SMB.
*
* @param[in] password Password.
* @param[in] username Username.
* @param[in] share_path Name/address of host and name of the share.
* @param[in] file_path Destination filename with path inside the share.
* @param[in] report Report that should be sent.
* @param[in] report_size Size of the report.
* @param[out] script_message Custom error message of the alert script.
*
* @return 0 success, -1 error, -5 alert script failed.
*/
static int
smb_send_to_host (const char *password, const char *username,
const char *share_path, const char *file_path,
const char *report, gsize report_size,
gchar **script_message)
{
gchar *clean_share_path, *clean_file_path;
gchar *authfile_content;
gchar *command_args;
int ret;
g_debug ("smb as %s to share: %s, path: %s", username, share_path, file_path);
if (password == NULL || username == NULL
|| share_path == NULL || file_path == NULL)
return -1;
clean_share_path = g_shell_quote (share_path);
clean_file_path = g_shell_quote (file_path);
authfile_content = g_strdup_printf ("username = %s\n"
"password = %s\n",
username, password);
command_args = g_strdup_printf ("%s %s",
clean_share_path, clean_file_path);
g_free (clean_share_path);
g_free (clean_file_path);
ret = run_alert_script ("c427a688-b653-40ab-a9d0-d6ba842a9d63", command_args,
"report", report, report_size,
authfile_content, strlen (authfile_content),
script_message);
g_free (authfile_content);
g_free (command_args);
return ret;
}
/**
* @brief Send a report to a Sourcefire Defense Center.
*
* @param[in] ip IP of center.
* @param[in] port Port of center.
* @param[in] pkcs12_64 PKCS12 content in base64.
* @param[in] pkcs12_password Password for encrypted PKCS12.
* @param[in] report Report in "Sourcefire" format.
*
* @return 0 success, -1 error.
*/
static int
send_to_sourcefire (const char *ip, const char *port, const char *pkcs12_64,
const char *pkcs12_password, const char *report)
{
gchar *script, *script_dir;
gchar *report_file, *pkcs12_file, *pkcs12, *clean_password;
gchar *clean_ip, *clean_port;
char report_dir[] = "/tmp/gvmd_escalate_XXXXXX";
GError *error;
gsize pkcs12_len;
if ((report == NULL) || (ip == NULL) || (port == NULL))
return -1;
g_debug ("send to sourcefire: %s:%s", ip, port);
g_debug ("report: %s", report);
/* Setup files. */
if (mkdtemp (report_dir) == NULL)
{
g_warning ("%s: mkdtemp failed", __func__);
return -1;
}
report_file = g_strdup_printf ("%s/report.csv", report_dir);
error = NULL;
g_file_set_contents (report_file, report, strlen (report), &error);
if (error)
{
g_warning ("%s", error->message);
g_error_free (error);
g_free (report_file);
return -1;
}
pkcs12_file = g_strdup_printf ("%s/pkcs12", report_dir);
if (strlen (pkcs12_64))
pkcs12 = (gchar*) g_base64_decode (pkcs12_64, &pkcs12_len);
else
{
pkcs12 = g_strdup ("");
pkcs12_len = 0;
}
error = NULL;
g_file_set_contents (pkcs12_file, pkcs12, pkcs12_len, &error);
if (error)
{
g_warning ("%s", error->message);
g_error_free (error);
g_free (report_file);
g_free (pkcs12_file);
return -1;
}
clean_password = g_shell_quote (pkcs12_password ? pkcs12_password : "");
/* Setup file names. */
script_dir = g_build_filename (GVMD_DATA_DIR,
"global_alert_methods",
"cd1f5a34-6bdc-11e0-9827-002264764cea",
NULL);
script = g_build_filename (script_dir, "alert", NULL);
if (!gvm_file_is_readable (script))
{
g_free (report_file);
g_free (pkcs12_file);
g_free (clean_password);
g_free (script);
g_free (script_dir);
return -1;
}
{
gchar *command;
char *previous_dir;
int ret;
/* Change into the script directory. */
previous_dir = getcwd (NULL, 0);
if (previous_dir == NULL)
{
g_warning ("%s: Failed to getcwd: %s",
__func__,
strerror (errno));
g_free (report_file);
g_free (pkcs12_file);
g_free (clean_password);
g_free (previous_dir);
g_free (script);
g_free (script_dir);
return -1;
}
if (chdir (script_dir))
{
g_warning ("%s: Failed to chdir: %s",
__func__,
strerror (errno));
g_free (report_file);
g_free (pkcs12_file);
g_free (clean_password);
g_free (previous_dir);
g_free (script);
g_free (script_dir);
return -1;
}
g_free (script_dir);
/* Call the script. */
clean_ip = g_shell_quote (ip);
clean_port = g_shell_quote (port);
command = g_strdup_printf ("%s %s %s %s %s %s > /dev/null"
" 2> /dev/null",
script,
clean_ip,
clean_port,
pkcs12_file,
report_file,
clean_password);
g_free (script);
g_free (clean_ip);
g_free (clean_port);
g_free (clean_password);
g_debug (" command: %s", command);
if (geteuid () == 0)
{
pid_t pid;
struct passwd *nobody;
/* Run the command with lower privileges in a fork. */
nobody = getpwnam ("nobody");
if ((nobody == NULL)
|| chown (report_dir, nobody->pw_uid, nobody->pw_gid)
|| chown (report_file, nobody->pw_uid, nobody->pw_gid)
|| chown (pkcs12_file, nobody->pw_uid, nobody->pw_gid))
{
g_warning ("%s: Failed to set permissions for user nobody: %s",
__func__,
strerror (errno));
g_free (report_file);
g_free (pkcs12_file);
g_free (previous_dir);
return -1;
}
g_free (report_file);
g_free (pkcs12_file);
pid = fork ();
switch (pid)
{
case 0:
{
/* Child. Drop privileges, run command, exit. */
cleanup_manage_process (FALSE);
proctitle_set ("gvmd: Sending to Sourcefire");
if (setgroups (0,NULL))
{
g_warning ("%s (child): setgroups: %s",
__func__, strerror (errno));
exit (EXIT_FAILURE);
}
if (setgid (nobody->pw_gid))
{
g_warning ("%s (child): setgid: %s",
__func__,
strerror (errno));
exit (EXIT_FAILURE);
}
if (setuid (nobody->pw_uid))
{
g_warning ("%s (child): setuid: %s",
__func__,
strerror (errno));
exit (EXIT_FAILURE);
}
ret = system (command);
/* Ignore the shell command exit status, because we've not
* specified what it must be in the past. */
if (ret == -1)
{
g_warning ("%s (child):"
" system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
command);
exit (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);
}
case -1:
/* Parent when error. */
g_warning ("%s: Failed to fork: %s",
__func__,
strerror (errno));
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
break;
default:
{
int status;
/* Parent on success. Wait for child, and check result. */
while (waitpid (pid, &status, 0) < 0)
{
if (errno == ECHILD)
{
g_warning ("%s: Failed to get child exit status",
__func__);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
if (errno == EINTR)
continue;
g_warning ("%s: wait: %s",
__func__,
strerror (errno));
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
}
if (WIFEXITED (status))
switch (WEXITSTATUS (status))
{
case EXIT_SUCCESS:
break;
case EXIT_FAILURE:
default:
g_warning ("%s: child failed, %s",
__func__,
command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
}
else
{
g_warning ("%s: child failed, %s",
__func__,
command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
}
/* Child succeeded, continue to process result. */
g_free (command);
break;
}
}
}
else
{
/* Just run the command as the current user. */
g_free (report_file);
g_free (pkcs12_file);
ret = system (command);
/* Ignore the shell command exit status, because we've not
* specified what it must be in the past. */
if (ret == -1)
{
g_warning ("%s: system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
}
g_free (command);
}
/* Change back to the previous directory. */
if (chdir (previous_dir))
{
g_warning ("%s: Failed to chdir back: %s",
__func__,
strerror (errno));
g_free (previous_dir);
return -1;
}
g_free (previous_dir);
/* Remove the directory. */
gvm_file_remove_recurse (report_dir);
return 0;
}
}
/**
* @brief Send a report to a verinice.PRO server.
*
* @param[in] url URL of the server.
* @param[in] username Username for server access.
* @param[in] password Password for server access.
* @param[in] archive Verinice archive that should be sent.
* @param[in] archive_size Size of the verinice archive
*
* @return 0 success, -1 error.
*/
static int
send_to_verinice (const char *url, const char *username, const char *password,
const char *archive, int archive_size)
{
gchar *script, *script_dir;
gchar *archive_file;
gchar *clean_url, *clean_username, *clean_password;
char archive_dir[] = "/tmp/gvmd_alert_XXXXXX";
GError *error;
if ((archive == NULL) || (url == NULL))
return -1;
g_debug ("send to verinice: %s", url);
g_debug ("archive: %s", archive);
/* Setup files. */
if (mkdtemp (archive_dir) == NULL)
{
g_warning ("%s: mkdtemp failed", __func__);
return -1;
}
archive_file = g_strdup_printf ("%s/archive.vna", archive_dir);
error = NULL;
g_file_set_contents (archive_file, archive, archive_size, &error);
if (error)
{
g_warning ("%s", error->message);
g_error_free (error);
g_free (archive_file);
return -1;
}
/* Setup file names. */
script_dir = g_build_filename (GVMD_DATA_DIR,
"global_alert_methods",
"f9d97653-f89b-41af-9ba1-0f6ee00e9c1a",
NULL);
script = g_build_filename (script_dir, "alert", NULL);
if (!gvm_file_is_readable (script))
{
g_warning ("%s: Failed to find alert script: %s",
__func__,
script);
g_free (archive_file);
g_free (script);
g_free (script_dir);
return -1;
}
{
gchar *command;
gchar *log_command; /* Command with password removed. */
char *previous_dir;
int ret;
/* Change into the script directory. */
previous_dir = getcwd (NULL, 0);
if (previous_dir == NULL)
{
g_warning ("%s: Failed to getcwd: %s",
__func__,
strerror (errno));
g_free (archive_file);
g_free (previous_dir);
g_free (script);
g_free (script_dir);
return -1;
}
if (chdir (script_dir))
{
g_warning ("%s: Failed to chdir: %s",
__func__,
strerror (errno));
g_free (archive_file);
g_free (previous_dir);
g_free (script);
g_free (script_dir);
return -1;
}
g_free (script_dir);
/* Call the script. */
clean_url = g_shell_quote (url);
clean_username = g_shell_quote (username);
clean_password = g_shell_quote (password);
command = g_strdup_printf ("%s %s %s %s %s > /dev/null"
" 2> /dev/null",
script,
clean_url,
clean_username,
clean_password,
archive_file);
log_command = g_strdup_printf ("%s %s %s ****** %s > /dev/null"
" 2> /dev/null",
script,
clean_url,
clean_username,
archive_file);
g_free (script);
g_free (clean_url);
g_free (clean_username);
g_free (clean_password);
g_debug (" command: %s", log_command);
if (geteuid () == 0)
{
pid_t pid;
struct passwd *nobody;
/* Run the command with lower privileges in a fork. */
nobody = getpwnam ("nobody");
if ((nobody == NULL)
|| chown (archive_dir, nobody->pw_uid, nobody->pw_gid)
|| chown (archive_file, nobody->pw_uid, nobody->pw_gid))
{
g_warning ("%s: Failed to set permissions for user nobody: %s",
__func__,
strerror (errno));
g_free (previous_dir);
g_free (archive_file);
g_free (command);
g_free (log_command);
return -1;
}
g_free (archive_file);
pid = fork ();
switch (pid)
{
case 0:
{
/* Child. Drop privileges, run command, exit. */
proctitle_set ("gvmd: Sending to Verinice");
cleanup_manage_process (FALSE);
if (setgroups (0,NULL))
{
g_warning ("%s (child): setgroups: %s",
__func__, strerror (errno));
exit (EXIT_FAILURE);
}
if (setgid (nobody->pw_gid))
{
g_warning ("%s (child): setgid: %s",
__func__,
strerror (errno));
exit (EXIT_FAILURE);
}
if (setuid (nobody->pw_uid))
{
g_warning ("%s (child): setuid: %s",
__func__,
strerror (errno));
exit (EXIT_FAILURE);
}
ret = system (command);
/* Ignore the shell command exit status, because we've not
* specified what it must be in the past. */
if (ret == -1)
{
g_warning ("%s (child):"
" system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
log_command);
exit (EXIT_FAILURE);
}
exit (EXIT_SUCCESS);
}
case -1:
/* Parent when error. */
g_warning ("%s: Failed to fork: %s",
__func__,
strerror (errno));
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
g_free (log_command);
return -1;
break;
default:
{
int status;
/* Parent on success. Wait for child, and check result. */
while (waitpid (pid, &status, 0) < 0)
{
if (errno == ECHILD)
{
g_warning ("%s: Failed to get child exit status",
__func__);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
if (errno == EINTR)
continue;
g_warning ("%s: wait: %s",
__func__,
strerror (errno));
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
if (WIFEXITED (status))
switch (WEXITSTATUS (status))
{
case EXIT_SUCCESS:
break;
case EXIT_FAILURE:
default:
g_warning ("%s: child failed, %s",
__func__,
log_command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
else
{
g_warning ("%s: child failed, %s",
__func__,
log_command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
return -1;
}
/* Child succeeded, continue to process result. */
break;
}
}
}
else
{
/* Just run the command as the current user. */
g_free (archive_file);
ret = system (command);
/* Ignore the shell command exit status, because we've not
* specified what it must be in the past. */
if (ret == -1)
{
g_warning ("%s: system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
log_command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
return -1;
}
}
g_free (command);
g_free (log_command);
/* Change back to the previous directory. */
if (chdir (previous_dir))
{
g_warning ("%s: Failed to chdir back: %s",
__func__,
strerror (errno));
g_free (previous_dir);
return -1;
}
g_free (previous_dir);
/* Remove the directory. */
gvm_file_remove_recurse (archive_dir);
return 0;
}
}
/**
* @brief Appends an XML fragment for vFire call input to a string buffer.
*
* @param[in] key The name of the key.
* @param[in] value The value to add.
* @param[in] buffer The string buffer to append to.
*
* @return Always FALSE.
*/
gboolean
buffer_vfire_call_input (gchar *key, gchar *value, GString *buffer)
{
xml_string_append (buffer,
"<%s>%s%s>",
key, value, key);
return FALSE;
}
/**
* @brief Checks a mandatory vFire parameter and adds it to the config XML.
*
* @param[in] param The parameter to check.
*/
#define APPEND_VFIRE_PARAM(param) \
if (param) \
xml_string_append (config_xml, \
"<" G_STRINGIFY(param) ">%s" G_STRINGIFY(param) ">", \
param); \
else \
{ \
if (message) \
*message = g_strdup ("Mandatory " G_STRINGIFY(param) " missing."); \
g_warning ("%s: Missing " G_STRINGIFY(param) ".", __func__); \
g_string_free (config_xml, TRUE); \
return -1; \
}
/**
* @brief Create a new call on an Alemba vFire server.
*
* @param[in] base_url Base url of the vFire server.
* @param[in] client_id The Alemba API Client ID to authenticate with.
* @param[in] session_type Alemba session type to use, e.g. "Analyst".
* @param[in] username Username.
* @param[in] password Password.
* @param[in] report_data Data for vFire call report attachments.
* @param[in] call_data Data for creating the vFire call.
* @param[in] description_template Template for the description text.
* @param[out] message Error message.
*
* @return 0 success, -1 error, -5 alert script failed.
*/
static int
send_to_vfire (const char *base_url, const char *client_id,
const char *session_type, const char *username,
const char *password, GPtrArray *report_data,
GTree *call_data, const char *description_template,
gchar **message)
{
const char *alert_id = "159f79a5-fce8-4ec5-aa49-7d17a77739a3";
GString *config_xml;
int index;
char config_xml_filename[] = "/tmp/gvmd_vfire_data_XXXXXX.xml";
int config_xml_fd;
FILE *config_xml_file;
gchar **cmd;
gchar *alert_script;
int ret, exit_status;
GError *err;
config_xml = g_string_new ("");
// Mandatory parameters
APPEND_VFIRE_PARAM (base_url)
APPEND_VFIRE_PARAM (client_id)
APPEND_VFIRE_PARAM (username)
APPEND_VFIRE_PARAM (password)
// Optional parameters
xml_string_append (config_xml,
"%s",
session_type ? session_type : "Analyst");
// Call input
g_string_append (config_xml, "");
g_tree_foreach (call_data, ((GTraverseFunc) buffer_vfire_call_input),
config_xml);
g_string_append (config_xml, "");
// Report data
g_string_append (config_xml, "");
for (index = 0; index < report_data->len; index++)
{
alert_report_data_t *report_item;
report_item = g_ptr_array_index (report_data, index);
xml_string_append (config_xml,
""
"%s"
"%s"
"%s"
"%s"
"",
report_item->local_filename,
report_item->remote_filename,
report_item->content_type,
report_item->report_format_name);
}
g_string_append (config_xml, "");
// End data XML and output to file
g_string_append (config_xml, "");
config_xml_fd = g_mkstemp (config_xml_filename);
if (config_xml_fd == -1)
{
g_warning ("%s: Could not create alert script config file: %s",
__func__, strerror (errno));
g_string_free (config_xml, TRUE);
return -1;
}
config_xml_file = fdopen (config_xml_fd, "w");
if (config_xml_file == NULL)
{
g_warning ("%s: Could not open alert script config file: %s",
__func__, strerror (errno));
g_string_free (config_xml, TRUE);
close (config_xml_fd);
return -1;
}
if (fprintf (config_xml_file, "%s", config_xml->str) <= 0)
{
g_warning ("%s: Could not write alert script config file: %s",
__func__, strerror (errno));
g_string_free (config_xml, TRUE);
fclose (config_xml_file);
return -1;
}
fflush (config_xml_file);
fclose (config_xml_file);
g_string_free (config_xml, TRUE);
// Run the script
alert_script = g_build_filename (GVMD_DATA_DIR,
"global_alert_methods",
alert_id,
"alert",
NULL);
// TODO: Drop privileges when running as root
cmd = (gchar **) g_malloc (3 * sizeof (gchar *));
cmd[0] = alert_script;
cmd[1] = config_xml_filename;
cmd[2] = NULL;
ret = 0;
if (g_spawn_sync (NULL,
cmd,
NULL,
G_SPAWN_STDOUT_TO_DEV_NULL,
NULL,
NULL,
NULL,
message,
&exit_status,
&err) == FALSE)
{
g_warning ("%s: Failed to run alert script: %s",
__func__, err->message);
ret = -1;
}
if (exit_status)
{
g_warning ("%s: Alert script exited with status %d",
__func__, exit_status);
g_message ("%s: stderr: %s",
__func__, message ? *message: "");
ret = -5;
}
// Cleanup
g_free (cmd);
g_free (alert_script);
g_unlink (config_xml_filename);
return ret;
}
/**
* @brief Convert an XML report and send it to a TippingPoint SMS.
*
* @param[in] report Report to send.
* @param[in] report_size Size of report.
* @param[in] username Username.
* @param[in] password Password.
* @param[in] hostname Hostname.
* @param[in] certificate Certificate.
* @param[in] cert_workaround Whether to use cert workaround.
* @param[out] message Custom error message of the script.
*
* @return 0 success, -1 error.
*/
static int
send_to_tippingpoint (const char *report, size_t report_size,
const char *username, const char *password,
const char *hostname, const char *certificate,
int cert_workaround, gchar **message)
{
const char *alert_id = "5b39c481-9137-4876-b734-263849dd96ce";
char report_dir[] = "/tmp/gvmd_alert_XXXXXX";
gchar *auth_config, *report_path, *error_path, *extra_path, *cert_path;
gchar *command_args, *hostname_clean, *convert_script;
GError *error = NULL;
int ret;
/* Setup auth file contents */
auth_config = g_strdup_printf ("machine %s\n"
"login %s\n"
"password %s\n",
cert_workaround ? "Tippingpoint" : hostname,
username, password);
/* Setup common files. */
ret = alert_script_init ("report", report, report_size,
auth_config, strlen (auth_config),
report_dir,
&report_path, &error_path, &extra_path);
g_free (auth_config);
if (ret)
return ret;
/* Setup certificate file */
cert_path = g_build_filename (report_dir, "cacert.pem", NULL);
if (g_file_set_contents (cert_path,
certificate, strlen (certificate),
&error) == FALSE)
{
g_warning ("%s: Failed to write TLS certificate to file: %s",
__func__, error->message);
alert_script_cleanup (report_dir, report_path, error_path, extra_path);
g_free (cert_path);
return -1;
}
if (geteuid () == 0)
{
struct passwd *nobody;
/* Run the command with lower privileges in a fork. */
nobody = getpwnam ("nobody");
if ((nobody == NULL)
|| chown (cert_path, nobody->pw_uid, nobody->pw_gid))
{
g_warning ("%s: Failed to set permissions for user nobody: %s",
__func__,
strerror (errno));
g_free (cert_path);
alert_script_cleanup (report_dir, report_path, error_path,
extra_path);
return -1;
}
}
/* Build args and run the script */
hostname_clean = g_shell_quote (hostname);
convert_script = g_build_filename (GVMD_DATA_DIR,
"global_alert_methods",
alert_id,
"report-convert.py",
NULL);
command_args = g_strdup_printf ("%s %s %d %s",
hostname_clean, cert_path, cert_workaround,
convert_script);
g_free (hostname_clean);
g_free (cert_path);
g_free (convert_script);
ret = alert_script_exec (alert_id, command_args, report_path, report_dir,
error_path, extra_path, message);
if (ret)
{
alert_script_cleanup (report_dir, report_path, error_path, extra_path);
return ret;
}
/* Remove the directory. */
ret = alert_script_cleanup (report_dir, report_path, error_path, extra_path);
return ret;
}
/**
* @brief Format string for simple notice alert email.
*/
#define SIMPLE_NOTICE_FORMAT \
"%s.\n" \
"\n" \
"After the event %s,\n" \
"the following condition was met: %s\n" \
"\n" \
"This email escalation is not configured to provide more details.\n" \
"Full details are stored on the scan engine.\n" \
"\n" \
"\n" \
"Note:\n" \
"This email was sent to you as a configured security scan escalation.\n" \
"Please contact your local system administrator if you think you\n" \
"should not have received it.\n"
/**
* @brief Format string for simple notice alert email.
*/
#define SECINFO_SIMPLE_NOTICE_FORMAT \
"%s.\n" \
"\n" \
"After the event %s,\n" \
"the following condition was met: %s\n" \
"\n" \
"This email escalation is not configured to provide more details.\n" \
"Full details are stored on the scan engine.\n" \
"\n" \
"\n" \
"Note:\n" \
"This email was sent to you as a configured security scan escalation.\n" \
"Please contact your local system administrator if you think you\n" \
"should not have received it.\n"
/**
* @brief Print an alert subject.
*
* @param[in] subject Format string for subject.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] alert Alert.
* @param[in] task Task.
* @param[in] total Total number of resources (for SecInfo alerts).
*
* @return Freshly allocated subject.
*/
static gchar *
alert_subject_print (const gchar *subject, event_t event,
const void *event_data,
alert_t alert, task_t task, int total)
{
int formatting;
const gchar *point, *end;
GString *new_subject;
assert (subject);
new_subject = g_string_new ("");
for (formatting = 0, point = subject, end = (subject + strlen (subject));
point < end;
point++)
if (formatting)
{
switch (*point)
{
case '$':
g_string_append_c (new_subject, '$');
break;
case 'd':
/* Date that the check was last performed. */
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
char time_string[100];
time_t date;
struct tm tm;
if (event_data && (strcasecmp (event_data, "nvt") == 0))
date = nvts_check_time ();
else if (type_is_scap (event_data))
date = scap_check_time ();
else
date = cert_check_time ();
if (localtime_r (&date, &tm) == NULL)
{
g_warning ("%s: localtime failed, aborting",
__func__);
abort ();
}
if (strftime (time_string, 98, "%F", &tm) == 0)
break;
g_string_append (new_subject, time_string);
}
break;
case 'e':
{
gchar *event_desc;
event_desc = event_description (event, event_data,
NULL);
g_string_append (new_subject, event_desc);
g_free (event_desc);
break;
}
case 'n':
{
if (task)
{
char *name = task_name (task);
g_string_append (new_subject, name);
free (name);
}
break;
}
case 'N':
{
/* Alert Name */
char *name = alert_name (alert);
g_string_append (new_subject, name);
free (name);
break;
}
case 'q':
if (event == EVENT_NEW_SECINFO)
g_string_append (new_subject, "New");
else if (event == EVENT_UPDATED_SECINFO)
g_string_append (new_subject, "Updated");
break;
case 's':
/* Type. */
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
g_string_append (new_subject, type_name (event_data));
break;
case 'S':
/* Type, plural. */
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
g_string_append (new_subject, type_name_plural (event_data));
break;
case 'T':
g_string_append_printf (new_subject, "%i", total);
break;
case 'u':
{
/* Current user or owner of the Alert */
if (current_credentials.username
&& strcmp (current_credentials.username, ""))
{
g_string_append (new_subject, current_credentials.username);
}
else
{
char *owner = alert_owner_uuid (alert);
gchar *name = user_name (owner);
g_string_append (new_subject, name);
free (owner);
g_free (name);
}
break;
}
case 'U':
{
/* Alert UUID */
char *uuid = alert_uuid (alert);
g_string_append (new_subject, uuid);
free (uuid);
break;
}
default:
g_string_append_c (new_subject, '$');
g_string_append_c (new_subject, *point);
break;
}
formatting = 0;
}
else if (*point == '$')
formatting = 1;
else
g_string_append_c (new_subject, *point);
return g_string_free (new_subject, FALSE);
}
/**
* @brief Print an alert message.
*
* @param[in] message Format string for message.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] task Task.
* @param[in] alert Alert.
* @param[in] condition Alert condition.
* @param[in] format_name Report format name.
* @param[in] filter Filter.
* @param[in] term Filter term.
* @param[in] zone Timezone.
* @param[in] host_summary Host summary.
* @param[in] content The report, for inlining.
* @param[in] content_length Length of content.
* @param[in] truncated Whether the report was truncated.
* @param[in] total Total number of resources (for SecInfo alerts).
* @param[in] max_length Max allowed length of content.
*
* @return Freshly allocated message.
*/
static gchar *
alert_message_print (const gchar *message, event_t event,
const void *event_data, task_t task,
alert_t alert, alert_condition_t condition,
gchar *format_name, filter_t filter,
const gchar *term, const gchar *zone,
const gchar *host_summary, const gchar *content,
gsize content_length, int truncated, int total,
int max_length)
{
int formatting;
const gchar *point, *end;
GString *new_message;
assert (message);
new_message = g_string_new ("");
for (formatting = 0, point = message, end = (message + strlen (message));
point < end;
point++)
if (formatting)
{
switch (*point)
{
case '$':
g_string_append_c (new_message, '$');
break;
case 'c':
{
gchar *condition_desc;
condition_desc = alert_condition_description
(condition, alert);
g_string_append (new_message, condition_desc);
g_free (condition_desc);
break;
}
case 'd':
/* Date that the check was last performed. */
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
char time_string[100];
time_t date;
struct tm tm;
if (event_data && (strcasecmp (event_data, "nvt") == 0))
date = nvts_check_time ();
else if (type_is_scap (event_data))
date = scap_check_time ();
else
date = cert_check_time ();
if (localtime_r (&date, &tm) == NULL)
{
g_warning ("%s: localtime failed, aborting",
__func__);
abort ();
}
if (strftime (time_string, 98, "%F", &tm) == 0)
break;
g_string_append (new_message, time_string);
}
break;
case 'e':
{
gchar *event_desc;
event_desc = event_description (event, event_data,
NULL);
g_string_append (new_message, event_desc);
g_free (event_desc);
break;
}
case 'H':
{
/* Host summary. */
g_string_append (new_message,
host_summary ? host_summary : "N/A");
break;
}
case 'i':
{
if (content)
{
g_string_append_printf (new_message,
"%.*s",
/* Cast for 64 bit. */
(int) MIN (content_length,
max_content_length),
content);
if (content_length > max_content_length)
g_string_append_printf (new_message,
"\n... (report truncated after"
" %i characters)\n",
max_content_length);
}
break;
}
case 'n':
if (task)
{
char *name = task_name (task);
g_string_append (new_message, name);
free (name);
}
break;
case 'N':
{
/* Alert Name */
char *name = alert_name (alert);
g_string_append (new_message, name);
free (name);
break;
}
case 'r':
{
/* Report format name. */
g_string_append (new_message,
format_name ? format_name : "N/A");
break;
}
case 'F':
{
/* Name of filter. */
if (filter)
{
char *name = filter_name (filter);
g_string_append (new_message, name);
free (name);
}
else
g_string_append (new_message, "N/A");
break;
}
case 'f':
{
/* Filter term. */
g_string_append (new_message, term ? term : "N/A");
break;
}
case 'q':
{
if (event == EVENT_NEW_SECINFO)
g_string_append (new_message, "New");
else if (event == EVENT_UPDATED_SECINFO)
g_string_append (new_message, "Updated");
break;
}
case 's':
/* Type. */
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
g_string_append (new_message, type_name (event_data));
break;
case 'S':
/* Type, plural. */
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
g_string_append (new_message, type_name_plural (event_data));
break;
case 't':
{
if (truncated)
g_string_append_printf (new_message,
"Note: This report exceeds the"
" maximum length of %i characters"
" and thus\n"
"was truncated.\n",
max_length);
break;
}
case 'T':
{
g_string_append_printf (new_message, "%i", total);
break;
}
case 'u':
{
/* Current user or owner of the Alert */
if (current_credentials.username
&& strcmp (current_credentials.username, ""))
{
g_string_append (new_message, current_credentials.username);
}
else
{
char *owner = alert_owner_uuid (alert);
gchar *name = user_name (owner);
g_string_append (new_message, name);
free (owner);
g_free (name);
}
break;
}
case 'U':
{
/* Alert UUID */
char *uuid = alert_uuid (alert);
g_string_append (new_message, uuid);
free (uuid);
break;
}
case 'z':
{
/* Timezone. */
g_string_append (new_message, zone ? zone : "N/A");
break;
}
case 'R':
default:
g_string_append_c (new_message, '$');
g_string_append_c (new_message, *point);
break;
}
formatting = 0;
}
else if (*point == '$')
formatting = 1;
else
g_string_append_c (new_message, *point);
return g_string_free (new_message, FALSE);
}
/**
* @brief Print an SCP alert file path.
*
* @param[in] message Format string for message.
* @param[in] task Task.
*
* @return Freshly allocated message.
*/
static gchar *
scp_alert_path_print (const gchar *message, task_t task)
{
int formatting;
const gchar *point, *end;
GString *new_message;
assert (message);
new_message = g_string_new ("");
for (formatting = 0, point = message, end = (message + strlen (message));
point < end;
point++)
if (formatting)
{
switch (*point)
{
case '$':
g_string_append_c (new_message, '$');
break;
case 'D':
case 'T':
{
char time_string[9];
time_t current_time;
struct tm tm;
const gchar *format_str;
if (*point == 'T')
format_str = "%H%M%S";
else
format_str = "%Y%m%d";
memset(&time_string, 0, 9);
current_time = time (NULL);
if (localtime_r (¤t_time, &tm) == NULL)
{
g_warning ("%s: localtime failed, aborting",
__func__);
abort ();
}
if (strftime (time_string, 9, format_str, &tm))
g_string_append (new_message, time_string);
break;
}
case 'n':
if (task)
{
char *name = task_name (task);
g_string_append (new_message, name);
free (name);
}
break;
}
formatting = 0;
}
else if (*point == '$')
formatting = 1;
else
g_string_append_c (new_message, *point);
return g_string_free (new_message, FALSE);
}
/**
* @brief Build and send email for a ticket alert.
*
* @param[in] alert Alert.
* @param[in] ticket Ticket.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] method Method from alert.
* @param[in] condition Condition from alert, which was met by event.
* @param[in] to_address To address.
* @param[in] from_address From address.
* @param[in] subject Subject.
*
* @return 0 success, -1 error.
*/
static int
email_ticket (alert_t alert, ticket_t ticket, event_t event,
const void* event_data, alert_method_t method,
alert_condition_t condition, const gchar *to_address,
const gchar *from_address, const gchar *subject)
{
gchar *full_subject, *body;
char *recipient_credential_id;
credential_t recipient_credential;
int ret;
/* Setup subject. */
full_subject = g_strdup_printf ("%s: %s (UUID: %s)",
subject,
ticket_nvt_name (ticket)
? ticket_nvt_name (ticket)
: "[Orphan]",
ticket_uuid (ticket));
/* Setup body. */
{
gchar *event_desc, *condition_desc;
event_desc = event_description (event, event_data, NULL);
condition_desc = alert_condition_description
(condition, alert);
body = g_strdup_printf (SIMPLE_NOTICE_FORMAT,
event_desc,
event_desc,
condition_desc);
free (event_desc);
free (condition_desc);
}
/* Get credential */
recipient_credential_id = alert_data (alert, "method",
"recipient_credential");
recipient_credential = 0;
if (recipient_credential_id)
{
find_credential_with_permission (recipient_credential_id,
&recipient_credential, NULL);
}
/* Send email. */
ret = email (to_address, from_address, full_subject,
body, NULL, NULL, NULL, NULL,
recipient_credential);
g_free (body);
g_free (full_subject);
free (recipient_credential_id);
return ret;
}
/**
* @brief Build and send email for SecInfo alert.
*
* @param[in] alert Alert.
* @param[in] task Task.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] method Method from alert.
* @param[in] condition Condition from alert, which was met by event.
* @param[in] to_address To address.
* @param[in] from_address From address.
*
* @return 0 success, -1 error, -2 failed to find report format, -3 failed to
* find filter.
*/
static int
email_secinfo (alert_t alert, task_t task, event_t event,
const void* event_data, alert_method_t method,
alert_condition_t condition, const gchar *to_address,
const gchar *from_address)
{
gchar *alert_subject, *message, *subject, *example, *list, *type, *base64;
gchar *term, *body;
char *notice, *recipient_credential_id, *condition_filter_id;
filter_t condition_filter;
credential_t recipient_credential;
int ret, count;
list = new_secinfo_list (event, event_data, alert, &count);
type = g_strdup (event_data);
if (type && (example = strstr (type, "_example")))
example[0] = '\0';
/* Setup subject. */
subject = g_strdup_printf
("[GVM] %s %s arrived",
event == EVENT_NEW_SECINFO ? "New" : "Updated",
type_name_plural (type ? type : "nvt"));
alert_subject = alert_data (alert, "method", "subject");
if (alert_subject && strlen (alert_subject))
{
g_free (subject);
subject = alert_subject_print (alert_subject, event,
type, alert, task, count);
}
g_free (alert_subject);
/* Setup body. */
notice = alert_data (alert, "method", "notice");
message = alert_data (alert, "method", "message");
if (message == NULL || strlen (message) == 0)
{
g_free (message);
if (notice && strcmp (notice, "0") == 0)
/* Message with inlined report. */
message = g_strdup (SECINFO_ALERT_MESSAGE_INCLUDE);
else if (notice && strcmp (notice, "2") == 0)
/* Message with attached report. */
message = g_strdup (SECINFO_ALERT_MESSAGE_ATTACH);
else
/* Simple notice message. */
message = NULL;
}
base64 = NULL;
if (list && notice && strcmp (notice, "2") == 0)
{
/* Add list as text attachment. */
if (max_attach_length <= 0
|| strlen (list) <= max_attach_length)
base64 = g_base64_encode ((guchar*) list,
strlen (list));
}
condition_filter = 0;
term = NULL;
condition_filter_id = alert_data (alert, "condition", "filter_id");
if (condition_filter_id)
{
gchar *quoted_filter_id;
quoted_filter_id = sql_quote (condition_filter_id);
sql_int64 (&condition_filter,
"SELECT id FROM filters WHERE uuid = '%s'",
quoted_filter_id);
term = filter_term (condition_filter_id);
g_free (quoted_filter_id);
}
free (condition_filter_id);
if (message && strlen (message))
body = alert_message_print (message, event, type,
task, alert, condition,
NULL, condition_filter, term, NULL, NULL,
list,
list ? strlen (list) : 0,
0, count, 0);
else
{
gchar *event_desc, *condition_desc;
event_desc = event_description (event, event_data, NULL);
condition_desc = alert_condition_description
(condition, alert);
body = g_strdup_printf (SECINFO_SIMPLE_NOTICE_FORMAT,
event_desc,
event_desc,
condition_desc);
free (event_desc);
free (condition_desc);
}
g_free (term);
g_free (message);
g_free (list);
/* Get credential */
recipient_credential_id = alert_data (alert, "method",
"recipient_credential");
recipient_credential = 0;
if (recipient_credential_id)
{
find_credential_with_permission (recipient_credential_id,
&recipient_credential, NULL);
}
/* Send email. */
ret = email (to_address, from_address, subject,
body, base64,
base64 ? "text/plain" : NULL,
base64 ? "secinfo-alert" : NULL,
base64 ? "txt" : NULL,
recipient_credential);
g_free (body);
g_free (type);
g_free (subject);
free (recipient_credential_id);
return ret;
}
/**
* @brief Get the delta report to be used for an alert.
*
* @param[in] alert Alert.
* @param[in] task Task.
* @param[in] report Report.
*
* @return Report to compare with if required, else 0.
*/
static report_t
get_delta_report (alert_t alert, task_t task, report_t report)
{
char *delta_type;
report_t delta_report;
delta_type = alert_data (alert,
"method",
"delta_type");
if (delta_type == NULL)
return 0;
delta_report = 0;
if (strcmp (delta_type, "Previous") == 0)
{
if (task_report_previous (task, report, &delta_report))
g_warning ("%s: failed to get previous report", __func__);
}
else if (strcmp (delta_type, "Report") == 0)
{
char *delta_report_id;
delta_report_id = alert_data (alert,
"method",
"delta_report_id");
if (delta_report_id
&& find_report_with_permission (delta_report_id,
&delta_report,
"get_reports"))
g_warning ("%s: error while finding report", __func__);
}
free (delta_type);
return delta_report;
}
/**
* @brief Generates report results get data for an alert.
*
* @param[in] alert The alert to try to get the filter data from.
* @param[in] base_get_data The get data for fallback and other data.
* @param[out] alert_filter_get Pointer to the newly allocated get_data.
* @param[out] filter_return Pointer to the filter.
*
* @return 0 success, -1 error, -3 filter not found.
*/
static int
generate_alert_filter_get (alert_t alert, const get_data_t *base_get_data,
get_data_t **alert_filter_get,
filter_t *filter_return)
{
char *filt_id;
filter_t filter;
if (alert_filter_get == NULL)
return -1;
filt_id = alert_filter_id (alert);
filter = 0;
if (filt_id)
{
if (find_filter_with_permission (filt_id, &filter,
"get_filters"))
{
free (filt_id);
return -1;
}
if (filter == 0)
{
free (filt_id);
return -3;
}
}
if (filter_return)
*filter_return = filter;
if (filter)
{
(*alert_filter_get) = g_malloc0 (sizeof (get_data_t));
(*alert_filter_get)->details = base_get_data->details;
(*alert_filter_get)->ignore_pagination = base_get_data->ignore_pagination;
(*alert_filter_get)->ignore_max_rows_per_page
= base_get_data->ignore_max_rows_per_page;
(*alert_filter_get)->filt_id = g_strdup (filt_id);
(*alert_filter_get)->filter = filter_term (filt_id);
}
else
(*alert_filter_get) = NULL;
/* Adjust filter for report composer.
*
* As a first step towards a full composer we have two fields stored
* on the alert for controlling visibility of notes and overrides.
*
* We simply use these fields to adjust the filter. In the future we'll
* remove the filter terms and extend the way we get the report. */
if (filter)
{
gchar *include_notes, *include_overrides;
include_notes = alert_data (alert, "method",
"composer_include_notes");
if (include_notes)
{
gchar *new_filter;
new_filter = g_strdup_printf ("notes=%i %s",
atoi (include_notes),
(*alert_filter_get)->filter);
g_free ((*alert_filter_get)->filter);
(*alert_filter_get)->filter = new_filter;
(*alert_filter_get)->filt_id = NULL;
g_free (include_notes);
}
include_overrides = alert_data (alert, "method",
"composer_include_overrides");
if (include_overrides)
{
gchar *new_filter;
new_filter = g_strdup_printf ("overrides=%i %s",
atoi (include_overrides),
(*alert_filter_get)->filter);
g_free ((*alert_filter_get)->filter);
(*alert_filter_get)->filter = new_filter;
(*alert_filter_get)->filt_id = NULL;
g_free (include_overrides);
}
}
return 0;
}
/**
* @brief Generate report content for alert
*
* @param[in] alert The alert the report is generated for.
* @param[in] report Report or NULL to get last report of task.
* @param[in] task Task the report belongs to.
* @param[in] get GET data for the report.
* @param[in] report_format_data_name Name of alert data with report format,
* or NULL if not configurable.
* @param[in] report_format_lookup Name of report format to lookup if
* lookup by name, or NULL if not required.
* Used if report_format_data_name is
* NULL or fails.
* @param[in] fallback_format_id UUID of fallback report format. Used
* if both report_format_data_name and
* report_format_lookup are NULL or fail.
* @param[in] notes_details Whether to include details of notes in report.
* @param[in] overrides_details Whether to include override details in report.
* @param[out] content Report content location.
* @param[out] content_length Length of report content.
* @param[out] extension File extension of report format.
* @param[out] content_type Content type of report format.
* @param[out] term Filter term.
* @param[out] report_zone Actual timezone used in report.
* @param[out] host_summary Summary of results per host.
* @param[out] used_report_format Report format used.
* @param[out] filter_return Filter used.
*
* @return 0 success, -1 error, -2 failed to find report format, -3 failed to
* find filter.
*/
static int
report_content_for_alert (alert_t alert, report_t report, task_t task,
const get_data_t *get,
const char *report_format_data_name,
const char *report_format_lookup,
const char *fallback_format_id,
int notes_details, int overrides_details,
gchar **content, gsize *content_length,
gchar **extension, gchar **content_type,
gchar **term, gchar **report_zone,
gchar **host_summary,
report_format_t *used_report_format,
filter_t *filter_return)
{
int ret;
report_format_t report_format;
get_data_t *alert_filter_get;
gchar *report_content;
filter_t filter;
assert (content);
// Get filter
ret = generate_alert_filter_get (alert, get, &alert_filter_get, &filter);
if (ret)
return ret;
// Get last report from task if no report is given
if (report == 0)
switch (sql_int64 (&report,
"SELECT max (id) FROM reports"
" WHERE task = %llu",
task))
{
case 0:
if (report)
break;
case 1: /* Too few rows in result of query. */
case -1:
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
return -1;
break;
default: /* Programming error. */
assert (0);
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
return -1;
}
// Get report format or use fallback.
report_format = 0;
if (report_format_data_name)
{
gchar *format_uuid;
format_uuid = alert_data (alert,
"method",
report_format_data_name);
if (format_uuid && strlen (format_uuid))
{
if (find_report_format_with_permission (format_uuid,
&report_format,
"get_report_formats")
|| (report_format == 0))
{
g_warning ("%s: Could not find report format '%s' for %s",
__func__, format_uuid,
alert_method_name (alert_method (alert)));
g_free (format_uuid);
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
return -2;
}
g_free (format_uuid);
}
}
if (report_format_lookup && (report_format == 0))
{
if (lookup_report_format (report_format_lookup, &report_format)
|| (report_format == 0))
{
g_warning ("%s: Could not find report format '%s' for %s",
__func__, report_format_lookup,
alert_method_name (alert_method (alert)));
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
return -2;
}
}
if (report_format == 0)
{
if (fallback_format_id == NULL)
{
g_warning ("%s: No fallback report format for %s",
__func__,
alert_method_name (alert_method (alert)));
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
return -1;
}
if (find_report_format_with_permission
(fallback_format_id,
&report_format,
"get_report_formats")
|| (report_format == 0))
{
g_warning ("%s: Could not find fallback RFP '%s' for %s",
__func__, fallback_format_id,
alert_method_name (alert_method (alert)));
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
return -2;
}
}
// Generate report content
report_content = manage_report (report,
get_delta_report (alert, task, report),
alert_filter_get ? alert_filter_get : get,
report_format,
notes_details,
overrides_details,
content_length,
extension,
content_type,
term,
report_zone,
host_summary);
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
if (report_content == NULL)
return -1;
*content = report_content;
*used_report_format = report_format;
return 0;
}
/**
* @brief Generates a filename or path for a report.
*
* If no custom_format is given, the setting "Report Export File Name"
* is used instead.
*
* @param[in] report The report to generate the filename for.
* @param[in] report_format The report format to use.
* @param[in] custom_format A custom format string to use for the filename.
* @param[in] add_extension Whether to add the filename extension or not.
*
* @return Newly allocated filename.
*/
static gchar *
generate_report_filename (report_t report, report_format_t report_format,
const char *custom_format, gboolean add_extension)
{
task_t task;
char *fname_format, *report_id, *creation_time, *modification_time;
char *report_task_name, *rf_name;
gchar *filename_base, *filename;
if (custom_format && strcmp (custom_format, ""))
fname_format = g_strdup (custom_format);
else
fname_format
= sql_string ("SELECT value FROM settings"
" WHERE name"
" = 'Report Export File Name'"
" AND " ACL_GLOBAL_OR_USER_OWNS ()
" ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
current_credentials.uuid);
report_id = report_uuid (report);
creation_time
= sql_string ("SELECT iso_time (start_time)"
" FROM reports"
" WHERE id = %llu",
report);
modification_time
= sql_string ("SELECT iso_time (end_time)"
" FROM reports"
" WHERE id = %llu",
report);
report_task (report, &task);
report_task_name = task_name (task);
rf_name = report_format ? report_format_name (report_format)
: g_strdup ("unknown");
filename_base
= gvm_export_file_name (fname_format,
current_credentials.username,
"report", report_id,
creation_time, modification_time,
report_task_name, rf_name);
if (add_extension && report_format)
{
gchar *extension;
extension = report_format_extension (report_format);
filename = g_strdup_printf ("%s.%s", filename_base, extension);
free (extension);
}
else
filename = g_strdup (filename_base);
free (fname_format);
free (report_id);
free (creation_time);
free (modification_time);
free (report_task_name);
free (rf_name);
g_free (filename_base);
return filename;
}
/**
* @brief Escalate an event.
*
* @param[in] alert Alert.
* @param[in] task Task.
* @param[in] report Report. 0 for most recent report.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] method Method from alert.
* @param[in] condition Condition from alert, which was met by event.
* @param[in] get GET data for report.
* @param[in] notes_details If notes, Whether to include details.
* @param[in] overrides_details If overrides, Whether to include details.
* @param[out] script_message Custom error message from the script.
*
* @return 0 success, -1 error, -2 failed to find report format, -3 failed to
* find filter, -4 failed to find credential, -5 alert script failed.
*/
static int
escalate_to_vfire (alert_t alert, task_t task, report_t report, event_t event,
const void* event_data, alert_method_t method,
alert_condition_t condition, const get_data_t *get,
int notes_details, int overrides_details,
gchar **script_message)
{
int ret;
char *credential_id;
get_data_t *alert_filter_get;
filter_t filter;
credential_t credential;
char *base_url, *session_type, *client_id, *username, *password;
char *report_formats_str;
gchar **report_formats, **point;
char reports_dir[] = "/tmp/gvmd_XXXXXX";
gboolean is_first_report = TRUE;
GString *format_names;
GPtrArray *reports;
char *report_zone;
gchar *host_summary;
iterator_t data_iterator;
GTree *call_input;
char *description_template;
int name_offset;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"Alemba vFire\" not support",
__func__);
return -1;
}
// Get report
if (report == 0)
switch (sql_int64 (&report,
"SELECT max (id) FROM reports"
" WHERE task = %llu",
task))
{
case 0:
if (report)
break;
case 1: /* Too few rows in result of query. */
case -1:
return -1;
break;
default: /* Programming error. */
assert (0);
return -1;
}
// Get report results filter and corresponding get data
alert_filter_get = NULL;
ret = generate_alert_filter_get (alert, get, &alert_filter_get, &filter);
if (ret)
return ret;
// Generate reports
if (mkdtemp (reports_dir) == NULL)
{
g_warning ("%s: mkdtemp failed", __func__);
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
return -1;
}
reports = g_ptr_array_new_full (0, (GDestroyNotify) alert_report_data_free);
report_formats_str = alert_data (alert, "method", "report_formats");
report_formats = g_strsplit_set (report_formats_str, ",", 0);
point = report_formats;
free (report_formats_str);
report_zone = NULL;
host_summary = NULL;
format_names = g_string_new ("");
while (*point)
{
gchar *report_format_id;
report_format_t report_format;
report_format_id = g_strstrip (*point);
find_report_format_with_permission (report_format_id,
&report_format,
"get_report_formats");
if (report_format)
{
alert_report_data_t *alert_report_item;
size_t content_length;
gchar *report_content;
GError *error = NULL;
alert_report_item = g_malloc0 (sizeof (alert_report_data_t));
report_content = manage_report (report,
get_delta_report
(alert, task, report),
alert_filter_get
? alert_filter_get
: get,
report_format,
notes_details,
overrides_details,
&content_length,
NULL /* extension */,
&(alert_report_item->content_type),
NULL /* term */,
is_first_report
? &report_zone
: NULL,
is_first_report
? &host_summary
: NULL);
if (report_content == NULL)
{
g_warning ("%s: Failed to generate report", __func__);
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
alert_report_data_free (alert_report_item);
g_strfreev (report_formats);
return -1;
}
alert_report_item->report_format_name
= report_format_name (report_format);
if (is_first_report == FALSE)
g_string_append (format_names, ", ");
g_string_append (format_names,
alert_report_item->report_format_name);
alert_report_item->remote_filename
= generate_report_filename (report, report_format, NULL, TRUE);
alert_report_item->local_filename
= g_build_filename (reports_dir,
alert_report_item->remote_filename,
NULL);
g_file_set_contents (alert_report_item->local_filename,
report_content, content_length,
&error);
g_free (report_content);
if (error)
{
g_warning ("%s: Failed to write report to %s: %s",
__func__,
alert_report_item->local_filename,
error->message);
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
alert_report_data_free (alert_report_item);
g_strfreev (report_formats);
return -1;
}
g_ptr_array_add (reports, alert_report_item);
is_first_report = FALSE;
}
point ++;
}
g_strfreev (report_formats);
// Find vFire credential
credential_id = alert_data (alert, "method",
"vfire_credential");
if (find_credential_with_permission (credential_id, &credential,
"get_credentials"))
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
free (credential_id);
return -1;
}
else if (credential == 0)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
free (credential_id);
return -4;
}
// vFire General data
base_url = alert_data (alert, "method",
"vfire_base_url");
// vFire Login data
session_type = alert_data (alert, "method", "vfire_session_type");
client_id = alert_data (alert, "method", "vfire_client_id");
username = credential_value (credential, "username");
password = credential_encrypted_value (credential, "password");
// Call input data
call_input = g_tree_new_full ((GCompareDataFunc) g_strcmp0,
NULL, g_free, g_free);
name_offset = strlen ("vfire_call_");
init_iterator (&data_iterator,
"SELECT name, data"
" FROM alert_method_data"
" WHERE alert = %llu"
" AND name %s 'vfire_call_%%';",
alert, sql_ilike_op ());
while (next (&data_iterator))
{
gchar *name, *value;
name = g_strdup (iterator_string (&data_iterator, 0)
+ name_offset);
value = g_strdup (iterator_string (&data_iterator, 1));
g_tree_replace (call_input, name, value);
}
cleanup_iterator (&data_iterator);
// Special case for descriptionterm
description_template = alert_data (alert, "method",
"vfire_call_description");
if (description_template == NULL)
description_template = g_strdup (ALERT_VFIRE_CALL_DESCRIPTION);
gchar *description;
description = alert_message_print (description_template,
event,
event_data,
task,
alert,
condition,
format_names->str,
filter,
alert_filter_get
? alert_filter_get->filter
: (get->filter ? get->filter : ""),
report_zone,
host_summary,
NULL,
0,
0,
0,
max_attach_length);
g_tree_replace (call_input,
g_strdup ("description"),
description);
// Create vFire ticket
ret = send_to_vfire (base_url, client_id, session_type, username,
password, reports, call_input, description_template,
script_message);
// Cleanup
gvm_file_remove_recurse (reports_dir);
if (alert_filter_get)
{
get_data_reset (alert_filter_get);
g_free (alert_filter_get);
}
free (base_url);
free (session_type);
free (client_id);
free (username);
free (password);
g_ptr_array_free (reports, TRUE);
g_tree_destroy (call_input);
free (description_template);
return ret;
}
/**
* @brief Escalate an event.
*
* @param[in] alert Alert.
* @param[in] task Task.
* @param[in] report Report. 0 for most recent report.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] method Method from alert.
* @param[in] condition Condition from alert, which was met by event.
* @param[in] get GET data for report.
* @param[in] notes_details If notes, Whether to include details.
* @param[in] overrides_details If overrides, Whether to include details.
* @param[out] script_message Custom error message from the script.
*
* @return 0 success, -1 error, -2 failed to find report format, -3 failed to
* find filter, -4 failed to find credential, -5 alert script failed.
*/
static int
escalate_2 (alert_t alert, task_t task, report_t report, event_t event,
const void* event_data, alert_method_t method,
alert_condition_t condition,
const get_data_t *get, int notes_details, int overrides_details,
gchar **script_message)
{
if (script_message)
*script_message = NULL;
{
char *name_alert;
gchar *event_desc, *alert_desc;
name_alert = alert_name (alert);
event_desc = event_description (event, event_data, NULL);
alert_desc = alert_condition_description (condition, alert);
g_log ("event alert", G_LOG_LEVEL_MESSAGE,
"The alert %s was triggered "
"(Event: %s, Condition: %s)",
name_alert,
event_desc,
alert_desc);
free (name_alert);
free (event_desc);
free (alert_desc);
}
switch (method)
{
case ALERT_METHOD_EMAIL:
{
char *to_address;
char *format_name;
format_name = NULL;
to_address = alert_data (alert, "method", "to_address");
if (to_address)
{
int ret;
gchar *body, *subject;
char *name, *notice, *from_address;
gchar *base64, *type, *extension;
base64 = NULL;
type = NULL;
extension = NULL;
from_address = alert_data (alert,
"method",
"from_address");
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
ret = email_secinfo (alert, task, event, event_data, method,
condition, to_address, from_address);
free (to_address);
free (from_address);
return ret;
}
if (event == EVENT_TICKET_RECEIVED)
{
ret = email_ticket (alert, task, event, event_data, method,
condition, to_address, from_address,
"Ticket received");
free (to_address);
free (from_address);
return ret;
}
if (event == EVENT_ASSIGNED_TICKET_CHANGED)
{
ret = email_ticket (alert, task, event, event_data, method,
condition, to_address, from_address,
"Assigned ticket changed");
free (to_address);
free (from_address);
return ret;
}
if (event == EVENT_OWNED_TICKET_CHANGED)
{
ret = email_ticket (alert, task, event, event_data, method,
condition, to_address, from_address,
"Owned ticket changed");
free (to_address);
free (from_address);
return ret;
}
notice = alert_data (alert, "method", "notice");
name = task_name (task);
if (notice && strcmp (notice, "0") == 0)
{
gchar *event_desc, *condition_desc, *report_content;
gchar *alert_subject, *message;
gchar *term, *report_zone, *host_summary;
report_format_t report_format = 0;
gsize content_length;
filter_t filter;
/* Message with inlined report. */
term = NULL;
report_zone = NULL;
host_summary = NULL;
/* report_content_for_alert always sets this, but init it
* anyway, to make it easier for the compiler to see. */
filter = 0;
ret = report_content_for_alert
(alert, 0, task, get,
"notice_report_format",
NULL,
/* TXT fallback */
"a3810a62-1f62-11e1-9219-406186ea4fc5",
notes_details, overrides_details,
&report_content, &content_length, &extension,
NULL, &term, &report_zone, &host_summary,
&report_format, &filter);
if (ret || report_content == NULL)
{
free (notice);
free (name);
free (to_address);
free (from_address);
g_free (term);
g_free (report_zone);
g_free (host_summary);
return -1;
}
format_name = report_format_name (report_format);
condition_desc = alert_condition_description (condition,
alert);
event_desc = event_description (event, event_data, NULL);
subject = g_strdup_printf ("[GVM] Task '%s': %s",
name ? name : "Internal Error",
event_desc);
g_free (event_desc);
alert_subject = alert_data (alert, "method", "subject");
if (alert_subject && strlen (alert_subject))
{
g_free (subject);
subject = alert_subject_print (alert_subject, event,
event_data,
alert, task, 0);
}
g_free (alert_subject);
message = alert_data (alert, "method", "message");
if (message == NULL || strlen (message) == 0)
{
g_free (message);
message = g_strdup (ALERT_MESSAGE_INCLUDE);
}
body = alert_message_print (message, event, event_data,
task, alert, condition,
format_name, filter,
term, report_zone,
host_summary, report_content,
content_length,
content_length
> max_content_length,
0,
max_content_length);
g_free (message);
g_free (report_content);
g_free (condition_desc);
g_free (term);
g_free (report_zone);
g_free (host_summary);
}
else if (notice && strcmp (notice, "2") == 0)
{
gchar *event_desc, *condition_desc, *report_content;
report_format_t report_format = 0;
gsize content_length;
gchar *alert_subject, *message;
gchar *term, *report_zone, *host_summary;
filter_t filter;
/* Message with attached report. */
term = NULL;
report_zone = NULL;
host_summary = NULL;
/* report_content_for_alert always sets this, but init it
* anyway, to make it easier for the compiler to see. */
filter = 0;
ret = report_content_for_alert
(alert, 0, task, get,
"notice_attach_format",
NULL,
/* TXT fallback */
"a3810a62-1f62-11e1-9219-406186ea4fc5",
notes_details, overrides_details,
&report_content, &content_length, &extension,
&type, &term, &report_zone, &host_summary,
&report_format, &filter);
if (ret || report_content == NULL)
{
free (notice);
free (name);
free (to_address);
free (from_address);
g_free (term);
g_free (report_zone);
g_free (host_summary);
return -1;
}
format_name = report_format_name (report_format);
condition_desc = alert_condition_description (condition,
alert);
event_desc = event_description (event, event_data, NULL);
subject = g_strdup_printf ("[GVM] Task '%s': %s",
name ? name : "Internal Error",
event_desc);
g_free (event_desc);
alert_subject = alert_data (alert, "method", "subject");
if (alert_subject && strlen (alert_subject))
{
g_free (subject);
subject = alert_subject_print (alert_subject, event,
event_data,
alert, task, 0);
}
g_free (alert_subject);
if (max_attach_length <= 0
|| content_length <= max_attach_length)
base64 = g_base64_encode ((guchar*) report_content,
content_length);
g_free (report_content);
message = alert_data (alert, "method", "message");
if (message == NULL || strlen (message) == 0)
{
g_free (message);
message = g_strdup (ALERT_MESSAGE_ATTACH);
}
body = alert_message_print (message, event, event_data,
task, alert, condition,
format_name, filter,
term, report_zone,
host_summary, NULL, 0,
base64 == NULL,
0,
max_attach_length);
g_free (message);
g_free (condition_desc);
g_free (term);
g_free (report_zone);
g_free (host_summary);
}
else
{
gchar *event_desc, *generic_desc, *condition_desc;
gchar *alert_subject, *message;
/* Simple notice message. */
format_name = NULL;
event_desc = event_description (event, event_data, name);
generic_desc = event_description (event, event_data, NULL);
condition_desc = alert_condition_description (condition,
alert);
subject = g_strdup_printf ("[GVM] Task '%s':"
" An event occurred",
name);
alert_subject = alert_data (alert, "method", "subject");
if (alert_subject && strlen (alert_subject))
{
g_free (subject);
subject = alert_subject_print (alert_subject, event,
event_data,
alert, task, 0);
}
g_free (alert_subject);
message = alert_data (alert, "method", "message");
if (message && strlen (message))
body = alert_message_print (message, event, event_data,
task, alert, condition,
NULL, 0, NULL, NULL, NULL,
NULL, 0, 0, 0, 0);
else
body = g_strdup_printf (SIMPLE_NOTICE_FORMAT,
event_desc,
generic_desc,
condition_desc);
g_free (message);
g_free (event_desc);
g_free (generic_desc);
g_free (condition_desc);
}
free (notice);
gchar *fname_format, *file_name;
gchar *report_id, *creation_time, *modification_time;
char *recipient_credential_id;
credential_t recipient_credential;
fname_format
= sql_string ("SELECT value FROM settings"
" WHERE name"
" = 'Report Export File Name'"
" AND " ACL_GLOBAL_OR_USER_OWNS ()
" ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
current_credentials.uuid);
report_id = report_uuid (report);
creation_time
= sql_string ("SELECT iso_time (start_time)"
" FROM reports"
" WHERE id = %llu",
report);
modification_time
= sql_string ("SELECT iso_time (end_time)"
" FROM reports"
" WHERE id = %llu",
report);
file_name
= gvm_export_file_name (fname_format,
current_credentials.username,
"report", report_id,
creation_time, modification_time,
name, format_name);
/* Get credential */
recipient_credential_id = alert_data (alert, "method",
"recipient_credential");
recipient_credential = 0;
if (recipient_credential_id)
{
find_credential_with_permission (recipient_credential_id,
&recipient_credential, NULL);
}
ret = email (to_address, from_address, subject, body, base64,
type, file_name ? file_name : "openvas-report",
extension, recipient_credential);
free (extension);
free (type);
free (name);
free (format_name);
g_free (base64);
free (to_address);
free (from_address);
g_free (subject);
g_free (body);
g_free (fname_format);
g_free (file_name);
g_free (report_id);
g_free (creation_time);
g_free (modification_time);
free (recipient_credential_id);
return ret;
}
return -1;
}
case ALERT_METHOD_HTTP_GET:
{
char *url;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"HTTP Get\" not support",
__func__);
return -1;
}
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
g_warning ("%s: Event \"%s NVTs arrived\" with method"
" \"HTTP Get\" not support",
__func__,
event == EVENT_NEW_SECINFO ? "New" : "Updated");
return -1;
}
url = alert_data (alert, "method", "URL");
if (url)
{
int ret, formatting;
gchar *point, *end;
GString *new_url;
new_url = g_string_new ("");
for (formatting = 0, point = url, end = (url + strlen (url));
point < end;
point++)
if (formatting)
{
switch (*point)
{
case '$':
g_string_append_c (new_url, '$');
break;
case 'c':
{
gchar *condition_desc;
condition_desc = alert_condition_description
(condition, alert);
g_string_append (new_url, condition_desc);
g_free (condition_desc);
break;
}
case 'e':
{
gchar *event_desc;
event_desc = event_description (event, event_data,
NULL);
g_string_append (new_url, event_desc);
g_free (event_desc);
break;
}
case 'n':
{
char *name = task_name (task);
g_string_append (new_url, name);
free (name);
break;
}
default:
g_string_append_c (new_url, '$');
g_string_append_c (new_url, *point);
break;
}
formatting = 0;
}
else if (*point == '$')
formatting = 1;
else
g_string_append_c (new_url, *point);
ret = http_get (new_url->str);
g_string_free (new_url, TRUE);
g_free (url);
return ret;
}
return -1;
}
case ALERT_METHOD_SCP:
{
credential_t credential;
char *credential_id;
char *private_key, *password, *username, *host, *path, *known_hosts;
gchar *report_content, *alert_path;
gsize content_length;
report_format_t report_format;
int ret;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"SCP\" not support",
__func__);
return -1;
}
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
gchar *message;
credential_id = alert_data (alert, "method", "scp_credential");
if (find_credential_with_permission (credential_id,
&credential,
"get_credentials"))
{
return -1;
}
else if (credential == 0)
{
return -4;
}
else
{
message = new_secinfo_message (event, event_data, alert);
username = credential_value (credential, "username");
password = credential_encrypted_value (credential,
"password");
private_key = credential_encrypted_value (credential,
"private_key");
host = alert_data (alert, "method", "scp_host");
path = alert_data (alert, "method", "scp_path");
known_hosts = alert_data (alert, "method", "scp_known_hosts");
alert_path = scp_alert_path_print (path, task);
free (path);
ret = scp_to_host (username, password, private_key,
host, alert_path, known_hosts,
message, strlen (message),
script_message);
g_free (message);
free (private_key);
free (password);
free (username);
free (host);
g_free (alert_path);
free (known_hosts);
return ret;
}
}
ret = report_content_for_alert
(alert, 0, task, get,
"scp_report_format",
NULL,
/* XML fallback. */
"a994b278-1f62-11e1-96ac-406186ea4fc5",
notes_details, overrides_details,
&report_content, &content_length, NULL,
NULL, NULL, NULL, NULL,
&report_format, NULL);
if (ret || report_content == NULL)
{
g_warning ("%s: Empty Report", __func__);
return -1;
}
credential_id = alert_data (alert, "method", "scp_credential");
if (find_credential_with_permission (credential_id, &credential,
"get_credentials"))
{
g_free (report_content);
return -1;
}
else if (credential == 0)
{
g_free (report_content);
return -4;
}
else
{
username = credential_value (credential, "username");
password = credential_encrypted_value (credential, "password");
private_key = credential_encrypted_value (credential,
"private_key");
host = alert_data (alert, "method", "scp_host");
path = alert_data (alert, "method", "scp_path");
known_hosts = alert_data (alert, "method", "scp_known_hosts");
alert_path = scp_alert_path_print (path, task);
free (path);
ret = scp_to_host (username, password, private_key,
host, alert_path, known_hosts,
report_content, content_length,
script_message);
free (private_key);
free (password);
free (username);
free (host);
g_free (alert_path);
free (known_hosts);
}
g_free (report_content);
return ret;
}
case ALERT_METHOD_SEND:
{
char *host, *port;
gchar *report_content;
gsize content_length;
report_format_t report_format;
int ret;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"Send\" not support",
__func__);
return -1;
}
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
gchar *message;
message = new_secinfo_message (event, event_data, alert);
host = alert_data (alert, "method", "send_host");
port = alert_data (alert, "method", "send_port");
g_debug ("send host: %s", host);
g_debug ("send port: %s", port);
ret = send_to_host (host, port, message, strlen (message),
script_message);
g_free (message);
free (host);
free (port);
return ret;
}
ret = report_content_for_alert
(alert, 0, task, get,
"send_report_format",
NULL,
/* XML fallback. */
"a994b278-1f62-11e1-96ac-406186ea4fc5",
notes_details, overrides_details,
&report_content, &content_length, NULL,
NULL, NULL, NULL, NULL,
&report_format, NULL);
if (ret || report_content == NULL)
{
g_warning ("%s: Empty Report", __func__);
return -1;
}
host = alert_data (alert, "method", "send_host");
port = alert_data (alert, "method", "send_port");
g_debug ("send host: %s", host);
g_debug ("send port: %s", port);
ret = send_to_host (host, port, report_content, content_length,
script_message);
free (host);
free (port);
g_free (report_content);
return ret;
}
case ALERT_METHOD_SMB:
{
char *credential_id, *username, *password;
char *share_path, *file_path_format;
gboolean file_path_is_dir;
report_format_t report_format;
gchar *file_path, *report_content, *extension;
gsize content_length;
credential_t credential;
int ret;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"SMP\" not support",
__func__);
return -1;
}
if (report == 0)
switch (sql_int64 (&report,
"SELECT max (id) FROM reports"
" WHERE task = %llu",
task))
{
case 0:
if (report)
break;
case 1: /* Too few rows in result of query. */
case -1:
return -1;
break;
default: /* Programming error. */
assert (0);
return -1;
}
if (task == 0 && report)
{
ret = report_task (report, &task);
if (ret)
return ret;
}
credential_id = alert_data (alert, "method", "smb_credential");
share_path = alert_data (alert, "method", "smb_share_path");
file_path_format
= sql_string ("SELECT value FROM tags"
" WHERE name = 'smb-alert:file_path'"
" AND EXISTS"
" (SELECT * FROM tag_resources"
" WHERE resource_type = 'task'"
" AND resource = %llu"
" AND tag = tags.id)"
" ORDER BY modification_time LIMIT 1;",
task);
if (file_path_format == NULL)
file_path_format = alert_data (alert, "method", "smb_file_path");
file_path_is_dir = (g_str_has_suffix (file_path_format, "\\")
|| g_str_has_suffix (file_path_format, "/"));
report_content = NULL;
extension = NULL;
report_format = 0;
g_debug ("smb_credential: %s", credential_id);
g_debug ("smb_share_path: %s", share_path);
g_debug ("smb_file_path: %s (%s)",
file_path_format, file_path_is_dir ? "dir" : "file");
ret = report_content_for_alert
(alert, report, task, get,
"smb_report_format",
NULL,
"a994b278-1f62-11e1-96ac-406186ea4fc5", /* XML fallback */
notes_details, overrides_details,
&report_content, &content_length, &extension,
NULL, NULL, NULL, NULL, &report_format, NULL);
if (ret || report_content == NULL)
{
free (credential_id);
free (share_path);
free (file_path_format);
g_free (report_content);
g_free (extension);
return ret ? ret : -1;
}
if (file_path_is_dir)
{
char *dirname, *filename;
dirname = generate_report_filename (report, report_format,
file_path_format, FALSE);
filename = generate_report_filename (report, report_format,
NULL, TRUE);
file_path = g_strdup_printf ("%s\\%s", dirname, filename);
free (dirname);
free (filename);
}
else
{
file_path = generate_report_filename (report, report_format,
file_path_format, TRUE);
}
credential = 0;
ret = find_credential_with_permission (credential_id, &credential,
"get_credentials");
if (ret || credential == 0)
{
if (ret == 0)
{
g_warning ("%s: Could not find credential %s",
__func__, credential_id);
}
free (credential_id);
free (share_path);
free (file_path);
g_free (report_content);
g_free (extension);
return ret ? -1 : -4;
}
username = credential_value (credential, "username");
password = credential_encrypted_value (credential, "password");
ret = smb_send_to_host (password, username, share_path, file_path,
report_content, content_length,
script_message);
g_free (username);
g_free (password);
free (credential_id);
free (share_path);
free (file_path);
g_free (report_content);
g_free (extension);
return ret;
}
case ALERT_METHOD_SNMP:
{
char *community, *agent, *snmp_message;
int ret;
gchar *message;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"SNMP\" not support",
__func__);
return -1;
}
community = alert_data (alert, "method", "snmp_community");
agent = alert_data (alert, "method", "snmp_agent");
snmp_message = alert_data (alert, "method", "snmp_message");
g_debug ("snmp_message: %s", snmp_message);
g_debug ("snmp_community: %s", community);
g_debug ("snmp_agent: %s", agent);
if (snmp_message)
{
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
int count;
gchar *list, *example, *type;
type = g_strdup (event_data);
if (type && (example = strstr (type, "_example")))
example[0] = '\0';
list = new_secinfo_list (event, event_data, alert, &count);
g_free (list);
message = alert_subject_print (snmp_message, event, type,
alert, task, count);
g_free (type);
}
else
message = alert_subject_print (snmp_message, event, event_data,
alert, task, 0);
}
else
{
gchar *event_desc;
event_desc = event_description (event, event_data, NULL);
message = g_strdup_printf ("%s", event_desc);
g_free (event_desc);
}
ret = snmp_to_host (community, agent, message, script_message);
free (agent);
free (community);
free (snmp_message);
g_free (message);
return ret;
}
case ALERT_METHOD_SOURCEFIRE:
{
char *ip, *port, *pkcs12, *pkcs12_credential_id;
credential_t pkcs12_credential;
gchar *pkcs12_password, *report_content;
gsize content_length;
report_format_t report_format;
int ret;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"Sourcefire\" not support",
__func__);
return -1;
}
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
g_warning ("%s: Event \"%s NVTs arrived\" with method"
" \"Sourcefire\" not support",
__func__,
event == EVENT_NEW_SECINFO ? "New" : "Updated");
return -1;
}
ret = report_content_for_alert
(alert, report, task, get,
NULL,
"Sourcefire",
NULL,
notes_details, overrides_details,
&report_content, &content_length, NULL,
NULL, NULL, NULL, NULL, &report_format, NULL);
if (ret || report_content == NULL)
{
g_warning ("%s: Empty Report", __func__);
return -1;
}
ip = alert_data (alert, "method", "defense_center_ip");
port = alert_data (alert, "method", "defense_center_port");
if (port == NULL)
port = g_strdup ("8307");
pkcs12 = alert_data (alert, "method", "pkcs12");
pkcs12_credential_id = alert_data (alert, "method",
"pkcs12_credential");
if (pkcs12_credential_id == NULL
|| strcmp (pkcs12_credential_id, "") == 0)
{
pkcs12_password = g_strdup ("");
}
else if (find_credential_with_permission (pkcs12_credential_id,
&pkcs12_credential,
"get_credentials"))
{
g_free (ip);
g_free (port);
g_free (pkcs12);
g_free (pkcs12_credential_id);
return -1;
}
else if (pkcs12_credential == 0)
{
g_free (ip);
g_free (port);
g_free (pkcs12);
g_free (pkcs12_credential_id);
return -4;
}
else
{
g_free (pkcs12_credential_id);
pkcs12_password = credential_encrypted_value (pkcs12_credential,
"password");
}
g_debug (" sourcefire ip: %s", ip);
g_debug (" sourcefire port: %s", port);
g_debug ("sourcefire pkcs12: %s", pkcs12);
ret = send_to_sourcefire (ip, port, pkcs12, pkcs12_password,
report_content);
free (ip);
g_free (port);
free (pkcs12);
g_free (report_content);
g_free (pkcs12_password);
return ret;
}
case ALERT_METHOD_SYSLOG:
{
char *submethod;
gchar *message, *event_desc, *level;
event_desc = event_description (event, event_data, NULL);
message = g_strdup_printf ("%s: %s", event_name (event), event_desc);
g_free (event_desc);
submethod = alert_data (alert, "method", "submethod");
level = g_strdup_printf ("event %s", submethod);
g_free (submethod);
g_debug (" syslog level: %s", level);
g_debug ("syslog message: %s", message);
g_log (level, G_LOG_LEVEL_MESSAGE, "%s", message);
g_free (level);
g_free (message);
return 0;
}
case ALERT_METHOD_TIPPINGPOINT:
{
int ret;
report_format_t report_format;
gchar *report_content, *extension;
size_t content_length;
char *credential_id, *username, *password, *hostname, *certificate;
credential_t credential;
char *tls_cert_workaround_str;
int tls_cert_workaround;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"TippingPoint SMS\" not support",
__func__);
return -1;
}
/* TLS certificate subject workaround setting */
tls_cert_workaround_str
= alert_data (alert, "method",
"tp_sms_tls_workaround");
if (tls_cert_workaround_str)
tls_cert_workaround = !!(atoi (tls_cert_workaround_str));
else
tls_cert_workaround = 0;
g_free (tls_cert_workaround_str);
/* SSL / TLS Certificate */
certificate = alert_data (alert, "method",
"tp_sms_tls_certificate");
/* Hostname / IP address */
hostname = alert_data (alert, "method",
"tp_sms_hostname");
/* Credential */
credential_id = alert_data (alert, "method",
"tp_sms_credential");
if (find_credential_with_permission (credential_id, &credential,
"get_credentials"))
{
g_free (certificate);
g_free (hostname);
g_free (credential_id);
return -1;
}
else if (credential == 0)
{
g_free (certificate);
g_free (hostname);
g_free (credential_id);
return -4;
}
else
{
g_free (credential_id);
username = credential_value (credential, "username");
password = credential_encrypted_value (credential, "password");
}
/* Report content */
extension = NULL;
ret = report_content_for_alert
(alert, report, task, get,
NULL, /* Report format not configurable */
NULL,
"a994b278-1f62-11e1-96ac-406186ea4fc5", /* XML fallback */
notes_details, overrides_details,
&report_content, &content_length, &extension,
NULL, NULL, NULL, NULL, &report_format, NULL);
g_free (extension);
if (ret)
{
g_free (username);
g_free (password);
g_free (hostname);
g_free (certificate);
return ret;
}
/* Send report */
ret = send_to_tippingpoint (report_content, content_length,
username, password, hostname,
certificate, tls_cert_workaround,
script_message);
g_free (username);
g_free (password);
g_free (hostname);
g_free (certificate);
g_free (report_content);
return ret;
}
case ALERT_METHOD_VERINICE:
{
credential_t credential;
char *url, *username, *password;
gchar *credential_id, *report_content;
gsize content_length;
report_format_t report_format;
int ret;
if ((event == EVENT_TICKET_RECEIVED)
|| (event == EVENT_ASSIGNED_TICKET_CHANGED)
|| (event == EVENT_OWNED_TICKET_CHANGED))
{
g_warning ("%s: Ticket events with method"
" \"Verinice\" not support",
__func__);
return -1;
}
if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
{
g_warning ("%s: Event \"%s NVTs arrived\" with method"
" \"Verinice\" not support",
__func__,
event == EVENT_NEW_SECINFO ? "New" : "Updated");
return -1;
}
ret = report_content_for_alert
(alert, report, task, get,
"verinice_server_report_format",
"Verinice ISM",
NULL,
notes_details, overrides_details,
&report_content, &content_length, NULL,
NULL, NULL, NULL, NULL, &report_format, NULL);
if (ret || report_content == NULL)
{
g_warning ("%s: Empty Report", __func__);
return -1;
}
url = alert_data (alert, "method", "verinice_server_url");
credential_id = alert_data (alert, "method",
"verinice_server_credential");
if (find_credential_with_permission (credential_id, &credential,
"get_credentials"))
{
free (url);
g_free (report_content);
return -1;
}
else if (credential == 0)
{
free (url);
g_free (report_content);
return -4;
}
else
{
username = credential_value (credential, "username");
password = credential_encrypted_value (credential, "password");
g_debug (" verinice url: %s", url);
g_debug ("verinice username: %s", username);
ret = send_to_verinice (url, username, password, report_content,
content_length);
free (url);
g_free (username);
g_free (password);
g_free (report_content);
return ret;
}
}
case ALERT_METHOD_VFIRE:
{
int ret;
ret = escalate_to_vfire (alert, task, report, event,
event_data, method, condition,
get, notes_details, overrides_details,
script_message);
return ret;
}
case ALERT_METHOD_START_TASK:
{
gvm_connection_t connection;
char *task_id, *report_id, *owner_id, *owner_name;
gmp_authenticate_info_opts_t auth_opts;
/* Run the callback to fork a child connected to the Manager. */
if (manage_fork_connection == NULL)
{
g_warning ("%s: no connection fork available", __func__);
return -1;
}
task_id = alert_data (alert, "method", "start_task_task");
if (task_id == NULL || strcmp (task_id, "") == 0)
{
g_warning ("%s: start_task_task missing or empty", __func__);
return -1;
}
owner_id = sql_string ("SELECT uuid FROM users"
" WHERE id = (SELECT owner FROM alerts"
" WHERE id = %llu)",
alert);
owner_name = sql_string ("SELECT name FROM users"
" WHERE id = (SELECT owner FROM alerts"
" WHERE id = %llu)",
alert);
if (owner_id == NULL)
{
g_warning ("%s: could not find alert owner",
__func__);
free (owner_id);
free (owner_name);
return -1;
}
switch (manage_fork_connection (&connection, owner_id))
{
case 0:
/* Child. Break, stop task, exit. */
break;
case -1:
/* Parent on error. */
g_free (task_id);
g_warning ("%s: fork failed", __func__);
return -1;
break;
default:
/* Parent. Continue with whatever lead to this escalation. */
g_free (task_id);
free (owner_id);
free (owner_name);
return 0;
break;
}
/* Start the task. */
auth_opts = gmp_authenticate_info_opts_defaults;
auth_opts.username = owner_name;
auth_opts.password = "dummy";
if (gmp_authenticate_info_ext_c (&connection, auth_opts))
{
g_free (task_id);
free (owner_id);
free (owner_name);
gvm_connection_free (&connection);
exit (EXIT_FAILURE);
}
if (gmp_start_task_report_c (&connection, task_id, &report_id))
{
g_free (task_id);
free (owner_id);
free (owner_name);
gvm_connection_free (&connection);
exit (EXIT_FAILURE);
}
g_free (task_id);
g_free (report_id);
free (owner_id);
free (owner_name);
gvm_connection_free (&connection);
exit (EXIT_SUCCESS);
}
case ALERT_METHOD_ERROR:
default:
break;
}
return -1;
}
/**
* @brief Escalate an event with preset report filtering.
*
* @param[in] alert Alert.
* @param[in] task Task.
* @param[in] report Report.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] method Method from alert.
* @param[in] condition Condition from alert, which was met by event.
* @param[out] script_message Custom error message from alert script.
*
* @return 0 success, -1 error, -2 failed to find report format for alert,
* -3 failed to find filter for alert, -4 failed to find credential,
* -5 alert script failed.
*/
static int
escalate_1 (alert_t alert, task_t task, report_t report, event_t event,
const void* event_data, alert_method_t method,
alert_condition_t condition, gchar **script_message)
{
int ret;
get_data_t get;
char *results_filter;
memset (&get, 0, sizeof (get_data_t));
get.details = 1;
results_filter = setting_filter ("Results");
if (results_filter && strlen (results_filter))
{
get.filt_id = results_filter;
get.filter = filter_term (results_filter);
}
else
{
get.filt_id = g_strdup ("0");
get.filter = g_strdup_printf ("notes=1 overrides=1 sort-reverse=severity"
" rows=%d",
method == ALERT_METHOD_EMAIL ? 1000 : -1);
}
ret = escalate_2 (alert, task, report, event, event_data, method, condition,
&get, 1, 1, script_message);
free (results_filter);
g_free (get.filter);
return ret;
}
/**
* @brief Escalate an alert with task and event data.
*
* @param[in] alert_id Alert UUID.
* @param[in] task_id Task UUID.
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[out] script_message Custom error message from alert script.
*
* @return 0 success, 1 failed to find alert, 2 failed to find task,
* 99 permission denied, -1 error, -2 failed to find report format
* for alert, -3 failed to find filter for alert, -4 failed to find
* credential for alert, -5 alert script failed.
*/
int
manage_alert (const char *alert_id, const char *task_id, event_t event,
const void* event_data, gchar **script_message)
{
alert_t alert;
task_t task;
alert_condition_t condition;
alert_method_t method;
if (acl_user_may ("test_alert") == 0)
return 99;
if (find_alert_with_permission (alert_id, &alert, "test_alert"))
return -1;
if (alert == 0)
return 1;
if (task_id == NULL || strcmp (task_id, "0") == 0)
task = 0;
else
{
if (find_task_with_permission (task_id, &task, NULL))
return -1;
if (task == 0)
return 2;
}
condition = alert_condition (alert);
method = alert_method (alert);
return escalate_1 (alert, task, 0, event, event_data, method, condition,
script_message);
}
/**
* @brief Header for "New NVTs" alert message.
*/
#define NEW_NVTS_HEADER \
/* Open-Xchange (OX) AppSuite XHTML File HTML Injection Vuln... NoneAvailable 0.0 100% */ \
"Name Solution Type Severity QOD\n" \
"------------------------------------------------------------------------------------------\n"
/**
* @brief Header for "New NVTs" alert message, when there's an OID.
*/
#define NEW_NVTS_HEADER_OID \
/* Open-Xchange (OX) AppSuite XHTML File HTML Injection Vuln... NoneAvailable 0.0 100% 1.3... */ \
"Name Solution Type Severity QOD OID\n" \
"------------------------------------------------------------------------------------------------\n"
/**
* @brief Header for "New CVEs" alert message.
*/
#define NEW_CVES_HEADER \
/* CVE-2014-100001 6.8 Cross-site request forgery (CSRF) vulnerability in... */ \
"Name Severity Description\n" \
"--------------------------------------------------------------------------------\n"
/**
* @brief Header for "New CPEs" alert message.
*/
#define NEW_CPES_HEADER \
/* cpe:/a:.joomclan:com_joomclip 1024cms... */ \
"Name Title\n" \
"------------------------------------------------------------------------------------------\n"
/**
* @brief Header for "New CERT-Bund Advisories" alert message.
*/
#define NEW_CERT_BUNDS_HEADER \
/* CB-K13/0849 Novell SUSE Linux Enterprise Server: Mehrere Schwachstellen... */ \
"Name Title\n" \
"------------------------------------------------------------------------------------------\n"
/**
* @brief Header for "New DFN-CERT Advisories" alert message.
*/
#define NEW_DFN_CERTS_HEADER \
/* DFN-CERT-2008-1100 Denial of Service Schwachstelle in der... */ \
"Name Title\n" \
"------------------------------------------------------------------------------------------\n"
/**
* @brief Header for "New CERT-Bund Advisories" alert message.
*/
#define NEW_OVAL_DEFS_HEADER \
/* oval:org.mitre.oval:def:100116 libtiff Malloc Error Denial of Service */ \
"OVAL ID Title\n" \
"------------------------------------------------------------------------------------------\n"
/**
* @brief Test an alert.
*
* @param[in] alert_id Alert UUID.
* @param[out] script_message Custom message from the alert script.
*
* @return 0 success, 1 failed to find alert, 2 failed to find task,
* 99 permission denied, -1 error, -2 failed to find report format
* for alert, -3 failed to find filter for alert, -4 failed to find
* credential for alert, -5 alert script failed.
*/
int
manage_test_alert (const char *alert_id, gchar **script_message)
{
int ret;
alert_t alert;
task_t task;
report_t report;
result_t result;
char *task_id, *report_id;
time_t now;
char now_string[26];
gchar *clean;
if (acl_user_may ("test_alert") == 0)
return 99;
if (find_alert_with_permission (alert_id, &alert, "test_alert"))
return -1;
if (alert == 0)
return 1;
if (alert_event (alert) == EVENT_NEW_SECINFO
|| alert_event (alert) == EVENT_UPDATED_SECINFO)
{
char *alert_event_data;
gchar *type;
alert_event_data = alert_data (alert, "event", "secinfo_type");
type = g_strdup_printf ("%s_example", alert_event_data ?: "NVT");
free (alert_event_data);
if (alert_event (alert) == EVENT_NEW_SECINFO)
ret = manage_alert (alert_id, "0", EVENT_NEW_SECINFO, (void*) type,
script_message);
else
ret = manage_alert (alert_id, "0", EVENT_UPDATED_SECINFO, (void*) type,
script_message);
g_free (type);
return ret;
}
task = make_task (g_strdup ("Temporary Task for Alert"),
g_strdup (""),
0, /* Exclude from assets. */
0); /* Skip event and log. */
report_id = gvm_uuid_make ();
if (report_id == NULL)
return -1;
task_uuid (task, &task_id);
report = make_report (task, report_id, TASK_STATUS_DONE);
result = make_result (task, "127.0.0.1", "localhost", "telnet (23/tcp)",
"1.3.6.1.4.1.25623.1.0.10330", "Alarm",
"A telnet server seems to be running on this port.",
NULL);
now = time (NULL);
if (strlen (ctime_r (&now, now_string)) == 0)
{
ret = -1;
goto exit;
}
clean = g_strdup (now_string);
if (clean[strlen (clean) - 1] == '\n')
clean[strlen (clean) - 1] = '\0';
set_task_start_time_ctime (task, g_strdup (clean));
set_scan_start_time_ctime (report, g_strdup (clean));
set_scan_host_start_time_ctime (report, "127.0.0.1", clean);
if (result)
report_add_result (report, result);
set_scan_host_end_time_ctime (report, "127.0.0.1", clean);
set_scan_end_time_ctime (report, clean);
g_free (clean);
ret = manage_alert (alert_id,
task_id,
EVENT_TASK_RUN_STATUS_CHANGED,
(void*) TASK_STATUS_DONE,
script_message);
exit:
/* No one should be running this task, so we don't worry about the lock. We
* could guarantee that no one runs the task, but this is a very rare case. */
delete_task (task, 1);
free (task_id);
free (report_id);
return ret;
}
/**
* @brief Return whether an event applies to a task and an alert.
*
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] event_resource Event resource.
* @param[in] alert Alert.
*
* @return 1 if event applies, else 0.
*/
static int
event_applies (event_t event, const void *event_data,
resource_t event_resource, alert_t alert)
{
switch (event)
{
case EVENT_TASK_RUN_STATUS_CHANGED:
{
int ret;
char *alert_event_data;
if (alert_applies_to_task (alert, event_resource) == 0)
return 0;
alert_event_data = alert_data (alert, "event", "status");
if (alert_event_data == NULL)
return 0;
ret = (task_run_status (event_resource) == (task_status_t) event_data)
&& (strcmp (alert_event_data,
run_status_name_internal ((task_status_t)
event_data))
== 0);
free (alert_event_data);
return ret;
}
case EVENT_NEW_SECINFO:
case EVENT_UPDATED_SECINFO:
{
char *alert_event_data;
alert_event_data = alert_data (alert, "event", "secinfo_type");
if (alert_event_data == NULL)
return 0;
if (strcasecmp (alert_event_data, event_data) == 0)
return 1;
return 0;
}
case EVENT_TICKET_RECEIVED:
case EVENT_ASSIGNED_TICKET_CHANGED:
return ticket_assigned_to (event_resource) == alert_owner (alert);
case EVENT_OWNED_TICKET_CHANGED:
return ticket_owner (event_resource) == alert_owner (alert);
default:
return 0;
}
}
/**
* @brief Return the SecInfo count.
*
* @param[in] alert Alert.
* @param[in] filter_id Condition filter id.
*
* @return 1 if met, else 0.
*/
static time_t
alert_secinfo_count (alert_t alert, char *filter_id)
{
get_data_t get;
int db_count, uuid_was_null;
event_t event;
gboolean get_modified;
time_t feed_version_epoch;
char *secinfo_type;
event = alert_event (alert);
get_modified = (event == EVENT_UPDATED_SECINFO);
if (current_credentials.uuid == NULL)
{
current_credentials.uuid = alert_owner_uuid (alert);
uuid_was_null = 1;
}
else
uuid_was_null = 0;
memset (&get, '\0', sizeof (get));
if (filter_id && strlen (filter_id) && strcmp (filter_id, "0"))
get.filt_id = filter_id;
secinfo_type = alert_data (alert, "event", "secinfo_type");
if (strcmp (secinfo_type, "nvt") == 0)
{
feed_version_epoch = nvts_feed_version_epoch ();
db_count = nvt_info_count_after (&get,
feed_version_epoch,
get_modified);
}
else if (strcmp (secinfo_type, "cert_bund_adv") == 0
|| strcmp (secinfo_type, "dfn_cert_adv") == 0)
{
feed_version_epoch = cert_check_time ();
db_count = secinfo_count_after (&get,
secinfo_type,
feed_version_epoch,
get_modified);
}
else // assume SCAP data
{
feed_version_epoch = scap_check_time ();
db_count = secinfo_count_after (&get,
secinfo_type,
feed_version_epoch,
get_modified);
}
if (uuid_was_null)
{
free (current_credentials.uuid);
current_credentials.uuid = NULL;
}
return db_count;
}
/**
* @brief Return whether the condition of an alert is met by a task.
*
* @param[in] task Task.
* @param[in] report Report.
* @param[in] alert Alert.
* @param[in] condition Condition.
*
* @return 1 if met, else 0.
*/
static int
condition_met (task_t task, report_t report, alert_t alert,
alert_condition_t condition)
{
switch (condition)
{
case ALERT_CONDITION_ALWAYS:
return 1;
break;
case ALERT_CONDITION_FILTER_COUNT_AT_LEAST:
{
char *filter_id, *count_string;
report_t last_report;
int holes, infos, logs, warnings, false_positives;
int count;
double severity;
/* True if there are at least the given number of results matched by
* the given filter in the last finished report. */
filter_id = alert_data (alert, "condition", "filter_id");
count_string = alert_data (alert, "condition", "count");
if (count_string)
{
count = atoi (count_string);
free (count_string);
}
else
count = 0;
if (task == 0)
{
int db_count;
/* SecInfo event. */
db_count = alert_secinfo_count (alert, filter_id);
if (db_count >= count)
return 1;
break;
}
if (report)
last_report = report;
else
{
last_report = 0;
if (task_last_report (task, &last_report))
g_warning ("%s: failed to get last report", __func__);
}
g_debug ("%s: last_report: %llu", __func__, last_report);
if (last_report)
{
int db_count;
get_data_t get;
memset (&get, 0, sizeof (get_data_t));
get.type = "result";
get.filt_id = filter_id;
report_counts_id (last_report, &holes, &infos, &logs,
&warnings, &false_positives, &severity,
&get, NULL);
db_count = holes + infos + logs + warnings
+ false_positives;
g_debug ("%s: count: %i vs %i", __func__, db_count, count);
if (db_count >= count)
{
g_free (filter_id);
return 1;
}
}
g_free (filter_id);
break;
}
case ALERT_CONDITION_FILTER_COUNT_CHANGED:
{
char *direction, *filter_id, *count_string;
report_t last_report;
int holes, infos, logs, warnings, false_positives;
int count;
double severity;
/* True if the number of results matched by the given filter in the
* last finished report changed in the given direction with respect
* to the second last finished report. */
direction = alert_data (alert, "condition", "direction");
filter_id = alert_data (alert, "condition", "filter_id");
count_string = alert_data (alert, "condition", "count");
if (count_string)
{
count = atoi (count_string);
free (count_string);
}
else
count = 0;
if (report)
last_report = report;
else
{
last_report = 0;
if (task_last_report (task, &last_report))
g_warning ("%s: failed to get last report", __func__);
}
if (last_report)
{
report_t second_last_report;
int last_count;
get_data_t get;
get.type = "result";
get.filt_id = filter_id;
report_counts_id (last_report, &holes, &infos, &logs,
&warnings, &false_positives, &severity,
&get, NULL);
last_count = holes + infos + logs + warnings
+ false_positives;
second_last_report = 0;
if (task_second_last_report (task, &second_last_report))
g_warning ("%s: failed to get second last report", __func__);
if (second_last_report)
{
int cmp, second_last_count;
report_counts_id (second_last_report, &holes, &infos,
&logs, &warnings, &false_positives,
&severity, &get, NULL);
second_last_count = holes + infos + logs + warnings
+ false_positives;
cmp = last_count - second_last_count;
g_debug ("cmp: %i (vs %i)", cmp, count);
g_debug ("direction: %s", direction);
g_debug ("last_count: %i", last_count);
g_debug ("second_last_count: %i", second_last_count);
if (count < 0)
{
count = -count;
if (direction == NULL
|| strcasecmp (direction, "increased") == 0)
{
free (direction);
direction = g_strdup ("decreased");
}
else if (strcasecmp (direction, "decreased") == 0)
{
free (direction);
direction = g_strdup ("increased");
}
}
if (direction == NULL)
{
/* Same as "increased". */
if (cmp >= count)
return 1;
}
else if (((strcasecmp (direction, "changed") == 0)
&& (abs (cmp) >= count))
|| ((strcasecmp (direction, "increased") == 0)
&& (cmp >= count))
|| ((strcasecmp (direction, "decreased") == 0)
&& (cmp <= count)))
{
free (direction);
free (filter_id);
return 1;
}
}
else
{
g_debug ("direction: %s", direction);
g_debug ("last_count: %i", last_count);
g_debug ("second_last_count NULL");
if (((strcasecmp (direction, "changed") == 0)
|| (strcasecmp (direction, "increased") == 0))
&& (last_count > 0))
{
free (direction);
free (filter_id);
return 1;
}
}
}
free (direction);
free (filter_id);
break;
}
case ALERT_CONDITION_SEVERITY_AT_LEAST:
{
char *condition_severity_str;
/* True if the threat level of the last finished report is at
* least the given level. */
condition_severity_str = alert_data (alert, "condition", "severity");
if (condition_severity_str)
{
double condition_severity_dbl, task_severity_dbl;
condition_severity_dbl = g_ascii_strtod (condition_severity_str,
0);
task_severity_dbl = task_severity_double (task, 1,
MIN_QOD_DEFAULT, 0);
if (task_severity_dbl >= condition_severity_dbl)
{
free (condition_severity_str);
return 1;
}
}
free (condition_severity_str);
break;
}
case ALERT_CONDITION_SEVERITY_CHANGED:
{
char *direction;
double last_severity, second_last_severity;
/* True if the threat level of the last finished report changed
* in the given direction with respect to the second last finished
* report. */
direction = alert_data (alert, "condition", "direction");
last_severity = task_severity_double (task, 1,
MIN_QOD_DEFAULT, 0);
second_last_severity = task_severity_double (task, 1,
MIN_QOD_DEFAULT, 1);
if (direction
&& last_severity > SEVERITY_MISSING
&& second_last_severity > SEVERITY_MISSING)
{
double cmp = last_severity - second_last_severity;
g_debug ("cmp: %f", cmp);
g_debug ("direction: %s", direction);
g_debug ("last_level: %1.1f", last_severity);
g_debug ("second_last_level: %1.1f", second_last_severity);
if (((strcasecmp (direction, "changed") == 0) && cmp)
|| ((strcasecmp (direction, "increased") == 0) && (cmp > 0))
|| ((strcasecmp (direction, "decreased") == 0) && (cmp < 0)))
{
free (direction);
return 1;
}
}
else if (direction
&& last_severity > SEVERITY_MISSING)
{
g_debug ("direction: %s", direction);
g_debug ("last_level: %1.1f", last_severity);
g_debug ("second_last_level NULL");
if ((strcasecmp (direction, "changed") == 0)
|| (strcasecmp (direction, "increased") == 0))
{
free (direction);
return 1;
}
}
free (direction);
break;
}
default:
break;
}
return 0;
}
/**
* @brief Produce an event.
*
* @param[in] event Event.
* @param[in] event_data Event type specific details.
* @param[in] resource_1 Event type specific resource 1. For example,
* a task for EVENT_TASK_RUN_STATUS_CHANGED.
* @param[in] resource_2 Event type specific resource 2.
*/
void
event (event_t event, void* event_data, resource_t resource_1,
resource_t resource_2)
{
iterator_t alerts;
GArray *alerts_triggered;
guint index;
g_debug (" EVENT %i on resource %llu", event, resource_1);
alerts_triggered = g_array_new (TRUE, TRUE, sizeof (alert_t));
if ((event == EVENT_TASK_RUN_STATUS_CHANGED)
&& (((task_status_t) event_data) == TASK_STATUS_DONE))
check_tickets (resource_1);
init_event_alert_iterator (&alerts, event);
while (next (&alerts))
{
alert_t alert = event_alert_iterator_alert (&alerts);
if (event_alert_iterator_active (&alerts)
&& event_applies (event, event_data, resource_1, alert))
{
alert_condition_t condition;
condition = alert_condition (alert);
if (condition_met (resource_1, resource_2, alert, condition))
g_array_append_val (alerts_triggered, alert);
}
}
cleanup_iterator (&alerts);
/* Run the alerts outside the iterator, because they may take some
* time and the iterator would prevent update processes (GMP MODIFY_XXX,
* CREATE_XXX, ...) from locking the database. */
index = alerts_triggered->len;
while (index--)
{
alert_t alert;
alert_condition_t condition;
alert = g_array_index (alerts_triggered, alert_t, index);
condition = alert_condition (alert);
escalate_1 (alert,
resource_1,
resource_2,
event,
event_data,
alert_method (alert),
condition,
NULL);
}
g_array_free (alerts_triggered, TRUE);
}
/**
* @brief Initialise an alert task iterator.
*
* Iterate over all tasks that use the alert.
*
* @param[in] iterator Iterator.
* @param[in] alert Alert.
* @param[in] ascending Whether to sort ascending or descending.
*/
void
init_alert_task_iterator (iterator_t* iterator, alert_t alert,
int ascending)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (alert);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_tasks"));
available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT tasks.name, tasks.uuid, %s FROM tasks, task_alerts"
" WHERE tasks.id = task_alerts.task"
" AND task_alerts.alert = %llu"
" AND hidden = 0"
" ORDER BY tasks.name %s;",
with_clause ? with_clause : "",
available,
alert,
ascending ? "ASC" : "DESC");
g_free (with_clause);
g_free (available);
}
/**
* @brief Return the name from an alert task iterator.
*
* @param[in] iterator Iterator.
*
* @return Name of the task or NULL if iteration is complete.
*/
const char*
alert_task_iterator_name (iterator_t* iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = iterator_string (iterator, 0);
return ret;
}
/**
* @brief Return the uuid from an alert task iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID of the task or NULL if iteration is complete.
*/
const char*
alert_task_iterator_uuid (iterator_t* iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = iterator_string (iterator, 1);
return ret;
}
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
alert_task_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 2);
}
/* Task functions. */
/**
* @brief Generate an extra WHERE clause for selecting tasks
*
* @param[in] trash Whether to get tasks from the trashcan.
* @param[in] usage_type The usage type to limit the selection to.
*
* @return Newly allocated where clause string.
*/
static gchar *
tasks_extra_where (int trash, const char *usage_type)
{
gchar *extra_where = NULL;
if (usage_type && strcmp (usage_type, ""))
{
gchar *quoted_usage_type;
quoted_usage_type = sql_quote (usage_type);
extra_where = g_strdup_printf (" AND hidden = %d"
" AND usage_type = '%s'",
trash ? 2 : 0,
quoted_usage_type);
g_free (quoted_usage_type);
}
else
extra_where = g_strdup_printf (" AND hidden = %d",
trash ? 2 : 0);
return extra_where;
}
/**
* @brief Append value to field of task.
*
* @param[in] task Task.
* @param[in] field Field.
* @param[in] value Value.
*/
static void
append_to_task_string (task_t task, const char* field, const char* value)
{
char* current;
gchar* quote;
current = sql_string ("SELECT %s FROM tasks WHERE id = %llu;",
field,
task);
if (current)
{
gchar* new = g_strconcat ((const gchar*) current, value, NULL);
free (current);
quote = sql_nquote (new, strlen (new));
g_free (new);
}
else
quote = sql_nquote (value, strlen (value));
sql ("UPDATE tasks SET %s = '%s', modification_time = m_now ()"
" WHERE id = %llu;",
field,
quote,
task);
g_free (quote);
}
/**
* @brief Filter columns for task iterator.
*/
#define TASK_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "status", "total", "first_report", \
"last_report", "threat", "trend", "severity", "schedule", "next_due", \
"first", "last", "false_positive", "log", "low", "medium", "high", \
"hosts", "result_hosts", "fp_per_host", "log_per_host", "low_per_host", \
"medium_per_host", "high_per_host", "target", "usage_type", NULL }
/**
* @brief Task iterator columns.
*/
#define TASK_ITERATOR_COLUMNS_INNER \
{ "run_status", NULL, KEYWORD_TYPE_INTEGER }, \
{ \
"(SELECT count(*) FROM reports" \
" WHERE task = tasks.id)", \
"total", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT uuid FROM reports WHERE task = tasks.id" \
/* TODO 1 == TASK_STATUS_DONE */ \
" AND scan_run_status = 1" \
" ORDER BY date ASC LIMIT 1)", \
"first_report", \
KEYWORD_TYPE_STRING \
}, \
{ "run_status_name (run_status)", "status", KEYWORD_TYPE_STRING }, \
{ \
"(SELECT uuid FROM reports WHERE task = tasks.id" \
/* TODO 1 == TASK_STATUS_DONE */ \
" AND scan_run_status = 1" \
" ORDER BY date DESC LIMIT 1)", \
"last_report", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT count(*) FROM reports" \
/* TODO 1 == TASK_STATUS_DONE */ \
" WHERE task = tasks.id AND scan_run_status = 1)", \
NULL, \
KEYWORD_TYPE_INTEGER \
}, \
{ "hosts_ordering", NULL, KEYWORD_TYPE_STRING }, \
{ "scanner", NULL, KEYWORD_TYPE_INTEGER }, \
{ "usage_type", NULL, KEYWORD_TYPE_STRING }
/**
* @brief Task iterator WHERE columns.
*/
#define TASK_ITERATOR_WHERE_COLUMNS_INNER \
{ \
"task_threat_level (id, opts.override, opts.min_qod)", \
"threat", \
KEYWORD_TYPE_STRING \
}, \
{ \
"task_trend (id, opts.override, opts.min_qod)", \
"trend", \
KEYWORD_TYPE_STRING \
}, \
{ \
"task_severity (id, opts.override, opts.min_qod)", \
"severity", \
KEYWORD_TYPE_DOUBLE \
}, \
{ \
"(SELECT schedules.name FROM schedules" \
" WHERE schedules.id = tasks.schedule)", \
"schedule", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(CASE WHEN schedule_next_time IS NULL" \
" THEN -1" \
" WHEN schedule_next_time = 0 AND tasks.schedule > 0" \
" THEN (SELECT first_time" \
" FROM schedules" \
" WHERE schedules.id = tasks.schedule)" \
" ELSE schedule_next_time" \
" END)", \
"next_due", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT date FROM reports WHERE task = tasks.id" \
/* TODO 1 == TASK_STATUS_DONE */ \
" AND scan_run_status = 1" \
" ORDER BY date ASC LIMIT 1)", \
"first", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT date FROM reports WHERE task = tasks.id" \
/* TODO 1 == TASK_STATUS_DONE */ \
" AND scan_run_status = 1" \
" ORDER BY date DESC LIMIT 1)", \
"last", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod," \
" 'False Positive')" \
" END", \
"false_positive", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod, 'Log')" \
" END", \
"log", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod, 'Low')" \
" END", \
"low", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod, 'Medium')" \
" END", \
"medium", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod, 'High')" \
" END", \
"high", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" report_host_count (task_last_report (id))" \
" END", \
"hosts", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" report_result_host_count (task_last_report (id), opts.min_qod)" \
" END", \
"result_hosts", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" coalesce (report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod," \
" 'False Positive') * 1.0" \
" / nullif (report_result_host_count (task_last_report (id),"\
" opts.min_qod), 0)," \
" 0)" \
" END", \
"fp_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" coalesce (report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod," \
" 'Log') * 1.0" \
" / nullif (report_result_host_count (task_last_report (id),"\
" opts.min_qod), 0)," \
" 0)" \
" END", \
"log_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" coalesce (report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod," \
" 'Low') * 1.0" \
" / nullif (report_result_host_count (task_last_report (id),"\
" opts.min_qod), 0)," \
" 0)" \
" END", \
"low_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" coalesce (report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod," \
" 'Medium') * 1.0" \
" / nullif (report_result_host_count (task_last_report (id),"\
" opts.min_qod), 0)," \
" 0)" \
" END", \
"medium_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE" \
" coalesce (report_severity_count (task_last_report (id)," \
" opts.override, opts.min_qod," \
" 'High') * 1.0" \
" / nullif (report_result_host_count (task_last_report (id),"\
" opts.min_qod), 0)," \
" 0)" \
" END", \
"high_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT name FROM targets WHERE id = target)", \
"target", \
KEYWORD_TYPE_STRING \
}
/**
* @brief Task iterator WHERE columns.
*/
#define TASK_ITERATOR_WHERE_COLUMNS \
{ \
TASK_ITERATOR_WHERE_COLUMNS_INNER, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Task iterator columns.
*/
#define TASK_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (tasks), \
TASK_ITERATOR_COLUMNS_INNER, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Task iterator minimal columns.
*/
#define TASK_ITERATOR_COLUMNS_MIN \
{ \
GET_ITERATOR_COLUMNS (tasks), \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Task iterator minimal WHERE columns.
*/
#define TASK_ITERATOR_WHERE_COLUMNS_MIN \
{ \
TASK_ITERATOR_COLUMNS_INNER, \
TASK_ITERATOR_WHERE_COLUMNS_INNER, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Generate the extra_tables string for a task iterator.
*
* @param[in] override Whether to apply overrides.
* @param[in] min_qod Minimum QoD of results to count.
* @param[in] ignore_severity Whether to ignore severity data.
* @return Newly allocated string with the extra_tables clause.
*/
static gchar*
task_iterator_opts_table (int override, int min_qod, int ignore_severity)
{
return g_strdup_printf (", (SELECT"
" %d AS override,"
" %d AS min_qod,"
" %d AS ignore_severity)"
" AS opts",
override,
min_qod,
ignore_severity);
}
/**
* @brief Initialise a task iterator, limited to current user's tasks.
*
* @param[in] iterator Task iterator.
* @param[in] trash Whether to iterate over trashcan tasks.
* @param[in] ignore_severity Whether to ignore severity data.
*/
static void
init_user_task_iterator (iterator_t* iterator, int trash, int ignore_severity)
{
static column_t select_columns[] = TASK_ITERATOR_COLUMNS;
gchar *extra_tables;
gchar *columns;
extra_tables = task_iterator_opts_table (0, MIN_QOD_DEFAULT,
ignore_severity);
columns = columns_build_select (select_columns);
init_iterator (iterator,
"SELECT %s"
" FROM tasks%s"
" WHERE " ACL_USER_OWNS ()
"%s;",
columns,
extra_tables,
current_credentials.uuid,
trash ? " AND hidden = 2" : " AND hidden < 2");
g_free (extra_tables);
g_free (columns);
}
/**
* @brief Initialise a task iterator.
*
* @param[in] iterator Task iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find target, 2 failed to find filter,
* -1 error.
*/
int
init_task_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = TASK_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = TASK_ITERATOR_COLUMNS;
static column_t where_columns[] = TASK_ITERATOR_WHERE_COLUMNS;
static column_t columns_min[] = TASK_ITERATOR_COLUMNS_MIN;
static column_t where_columns_min[] = TASK_ITERATOR_WHERE_COLUMNS_MIN;
char *filter;
int overrides, min_qod;
const char *usage_type;
gchar *extra_tables, *extra_where;
int ret;
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
overrides = filter_term_apply_overrides (filter ? filter : get->filter);
min_qod = filter_term_min_qod (filter ? filter : get->filter);
free (filter);
extra_tables = task_iterator_opts_table (overrides, min_qod, 0);
usage_type = get_data_get_extra (get, "usage_type");
extra_where = tasks_extra_where (get->trash, usage_type);
ret = init_get_iterator2 (iterator,
"task",
get,
/* SELECT columns. */
get->minimal ? columns_min : columns,
get->minimal ? columns_min : columns,
/* Filterable columns not in SELECT columns. */
get->minimal ? where_columns_min : where_columns,
get->minimal ? where_columns_min : where_columns,
filter_columns,
0,
extra_tables,
extra_where,
NULL,
current_credentials.uuid ? TRUE : FALSE,
FALSE,
NULL);
g_free (extra_tables);
g_free (extra_where);
return ret;
}
/**
* @brief Get the run status from a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Task run status.
*/
task_status_t
task_iterator_run_status (iterator_t* iterator)
{
task_status_t ret;
if (iterator->done) return TASK_STATUS_INTERRUPTED;
ret = (unsigned int) iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT);
return ret;
}
/**
* @brief Get the number of reports of a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Count of all task reports.
*/
int
task_iterator_total_reports (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
}
/**
* @brief Get the first report UUID from a task iterator.
*
* @param[in] iterator Iterator.
*
* @return First report UUID.
*/
const char *
task_iterator_first_report (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
}
/**
* @brief Get the run status name from a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Task run status name.
*/
const char *
task_iterator_run_status_name (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
}
/**
* @brief Get the last report UUID from a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Last report UUID.
*/
const char *
task_iterator_last_report (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
}
/**
* @brief Get the number of reports of a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Count of all task reports.
*/
int
task_iterator_finished_reports (iterator_t *iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
}
/**
* @brief Get the hosts ordering value from a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Task hosts ordering.
*/
const char *
task_iterator_hosts_ordering (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
}
/**
* @brief Get the UUID of task scanner from a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Task scanner if found, NULL otherwise.
*/
scanner_t
task_iterator_scanner (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
}
/**
* @brief Get the UUID of task scanner from a task iterator.
*
* @param[in] iterator Iterator.
*
* @return Task scanner if found, NULL otherwise.
*/
const char *
task_iterator_usage_type (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
}
/**
* @brief Return whether a task is in use by a task.
*
* @param[in] task Task.
*
* @return 0.
*/
int
task_in_use (task_t task)
{
task_status_t status;
status = task_run_status (task);
return status == TASK_STATUS_DELETE_REQUESTED
|| status == TASK_STATUS_DELETE_WAITING
|| status == TASK_STATUS_DELETE_ULTIMATE_REQUESTED
|| status == TASK_STATUS_DELETE_ULTIMATE_WAITING
|| status == TASK_STATUS_REQUESTED
|| status == TASK_STATUS_RUNNING
|| status == TASK_STATUS_QUEUED
|| status == TASK_STATUS_STOP_REQUESTED
|| status == TASK_STATUS_STOP_WAITING;
}
/**
* @brief Return whether a trashcan task is referenced by a task.
*
* @param[in] task Task.
*
* @return 0.
*/
int
trash_task_in_use (task_t task)
{
return task_in_use (task);
}
/**
* @brief Return whether a task is an Alterable Task.
*
* @param[in] task Task.
*
* @return 1 if Alterable, else 0.
*/
int
task_alterable (task_t task)
{
return sql_int ("SELECT alterable FROM tasks"
" WHERE id = %llu",
task);
}
/**
* @brief Return whether a task is writable.
*
* @param[in] task Task.
*
* @return 1 if writable, else 0.
*/
int
task_writable (task_t task)
{
return sql_int ("SELECT hidden = 0 FROM tasks"
" WHERE id = %llu",
task);
}
/**
* @brief Return whether a trashcan task is writable.
*
* @param[in] task Task.
*
* @return 1 if writable, else 0.
*/
int
trash_task_writable (task_t task)
{
return sql_int ("SELECT hidden = 2 FROM tasks"
" WHERE id = %llu",
task);
}
/**
* @brief Get the average duration of all finished reports of a task.
*
* @param[in] task Task.
*
* @return Average scan duration in seconds.
*/
int
task_average_scan_duration (task_t task)
{
return sql_int ("SELECT avg (end_time - start_time) FROM reports"
" WHERE task = %llu"
" AND scan_run_status = %d"
" AND coalesce (end_time, 0) != 0"
" AND coalesce (start_time, 0) != 0;",
task, TASK_STATUS_DONE);
}
/**
* @brief Initialize the manage library: open db.
*
* @param[in] database Location of manage database.
*
* @return 1 if open already, else 0.
*/
static int
init_manage_open_db (const db_conn_info_t *database)
{
if (sql_is_open ())
return 1;
/* Open the database. */
if (sql_open (database))
{
g_warning ("%s: sql_open failed", __func__);
abort ();
}
/* Ensure the user session variables always exists. */
sql ("SET SESSION \"gvmd.user.id\" = 0;");
sql ("SET SESSION \"gvmd.tz_override\" = '';");
/* Attach the SCAP and CERT databases. */
manage_attach_databases ();
return 0;
}
/**
* @brief Initialize the manage library: define SQL functions.
*/
static void
init_manage_create_functions ()
{
lockfile_t lockfile;
/* Lock to avoid an error return from Postgres when multiple processes
* create a function at the same time. */
if (lockfile_lock (&lockfile, "gvm-create-functions"))
abort ();
if (manage_create_sql_functions ())
{
lockfile_unlock (&lockfile);
g_warning ("%s: failed to create functions", __func__);
abort ();
}
lockfile_unlock (&lockfile);
}
/**
* @brief Initialize the manage library for a process.
*
* Open the SQL database, attach secondary databases, and define functions.
*
* @param[in] database Location of manage database.
*/
void
init_manage_process (const db_conn_info_t *database)
{
if (init_manage_open_db (database))
return;
init_manage_create_functions ();
}
/**
* @brief Reinitialize the manage library for a process.
*
* This is mandatory after a fork, to not carry open databases around (refer
* to database documentation).
*/
void
reinit_manage_process ()
{
cleanup_manage_process (FALSE);
init_manage_process (&gvmd_db_conn_info);
}
/**
* @brief Update the memory cache of NVTs.
*
* @param[in] nvt NVT.
*
* @return NVTi if found, else NULL.
*/
nvti_t *
lookup_nvti (const gchar *nvt)
{
return nvtis_lookup (nvti_cache, nvt);
}
/**
* @brief Update the memory cache of NVTs.
*/
static void
update_nvti_cache ()
{
iterator_t nvts;
nvtis_free (nvti_cache);
nvti_cache = nvtis_new ();
/* Because there are many NVTs and many refs it's slow to query the refs
* for each NVT. So this query gets the NVTs and their refs at the same
* time.
*
* The NVT data is duplicated in the result of the query when there are
* multiple refs for an NVT, but the loop below uses nvtis_lookup to
* check if we've already seen the NVT. This also means we don't have
* to sort the data by NVT, which would make the query too slow. */
init_iterator (&nvts,
"SELECT nvts.oid, vt_refs.type, vt_refs.ref_id,"
" vt_refs.ref_text"
" FROM nvts"
" LEFT OUTER JOIN vt_refs ON nvts.oid = vt_refs.vt_oid;");
while (next (&nvts))
{
nvti_t *nvti;
nvti = nvtis_lookup (nvti_cache, iterator_string (&nvts, 0));
if (nvti == NULL)
{
nvti = nvti_new ();
nvti_set_oid (nvti, iterator_string (&nvts, 0));
nvtis_add (nvti_cache, nvti);
}
if (iterator_null (&nvts, 2))
/* No refs. */;
else
nvti_add_vtref (nvti,
vtref_new (iterator_string (&nvts, 1),
iterator_string (&nvts, 2),
iterator_string (&nvts, 3)));
}
cleanup_iterator (&nvts);
malloc_trim (0);
}
/**
* @brief Update the memory cache of NVTs, if this has been requested.
*
* @return 0 success, 1 failed to get lock, -1 error.
*/
int
manage_update_nvti_cache ()
{
int ret;
ret = sql_begin_immediate_giveup ();
if (ret)
return ret;
if (sql_int ("SELECT value FROM %s.meta"
" WHERE name = 'update_nvti_cache';",
sql_schema ()))
{
update_nvti_cache ();
sql ("UPDATE %s.meta SET value = 0 WHERE name = 'update_nvti_cache';",
sql_schema ());
}
sql_commit ();
return 0;
}
/**
* @brief Ensure the predefined scanner exists.
*
* @return 0 if success, -1 if error.
*/
static int
check_db_scanners ()
{
if (sql_int ("SELECT count(*) FROM scanners WHERE uuid = '%s';",
SCANNER_UUID_DEFAULT) == 0)
{
sql ("INSERT INTO scanners"
" (uuid, owner, name, host, port, type, ca_pub, credential,"
" creation_time, modification_time)"
" VALUES ('" SCANNER_UUID_DEFAULT "', NULL, 'OpenVAS Default',"
" '%s', 0, %d, NULL, NULL, m_now (),"
" m_now ());",
OPENVAS_DEFAULT_SOCKET,
SCANNER_TYPE_OPENVAS);
}
if (sql_int ("SELECT count(*) FROM scanners WHERE uuid = '%s';",
SCANNER_UUID_CVE) == 0)
sql ("INSERT INTO scanners"
" (uuid, owner, name, host, port, type, ca_pub, credential,"
" creation_time, modification_time)"
" VALUES ('" SCANNER_UUID_CVE "', NULL, 'CVE',"
" '', 0, %d, NULL, NULL, m_now (), m_now ());",
SCANNER_TYPE_CVE);
return 0;
}
/**
* @brief Initialize the default settings.
*
* Ensure all the default manager settings exist.
*/
static void
check_db_settings ()
{
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '6765549a-934e-11e3-b358-406186ea4fc5'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('6765549a-934e-11e3-b358-406186ea4fc5', NULL,"
" 'User Interface Language',"
" 'Preferred language to be used in client user interfaces.',"
" 'Browser Language');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '" SETTING_UUID_ROWS_PER_PAGE "'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('" SETTING_UUID_ROWS_PER_PAGE "', NULL, 'Rows Per Page',"
" 'The default number of rows displayed in any listing.',"
" 10);");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '" SETTING_UUID_MAX_ROWS_PER_PAGE "'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('" SETTING_UUID_MAX_ROWS_PER_PAGE "', NULL, 'Max Rows Per Page',"
" 'The default maximum number of rows displayed in any listing.',"
" 1000);");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '77ec2444-e7f2-4a80-a59b-f4237782d93f'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('77ec2444-e7f2-4a80-a59b-f4237782d93f', NULL, 'Dynamic Severity',"
" 'Whether to use dynamic severity scores by default.',"
" '0');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '578a1c14-e2dc-45ef-a591-89d31391d007'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('578a1c14-e2dc-45ef-a591-89d31391d007', NULL, 'Auto-Refresh',"
" 'The delay between automatic page refreshs in seconds.',"
" '0');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = 'a6ac88c5-729c-41ba-ac0a-deea4a3441f2'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('a6ac88c5-729c-41ba-ac0a-deea4a3441f2', NULL,"
" 'Details Export File Name',"
" 'File name format string for the export of resource details.',"
" '%%T-%%U');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '0872a6ed-4f85-48c5-ac3f-a5ef5e006745'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('0872a6ed-4f85-48c5-ac3f-a5ef5e006745', NULL,"
" 'List Export File Name',"
" 'File name format string for the export of resource lists.',"
" '%%T-%%D');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = 'e1a2ae0b-736e-4484-b029-330c9e15b900'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('e1a2ae0b-736e-4484-b029-330c9e15b900', NULL,"
" 'Report Export File Name',"
" 'File name format string for the export of reports.',"
" '%%T-%%U');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '7eda49c5-096c-4bef-b1ab-d080d87300df'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('7eda49c5-096c-4bef-b1ab-d080d87300df', NULL,"
" 'Default Severity',"
" 'Severity to use if none is specified or available from SecInfo.',"
" '10.0');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = 'a09285b0-2d47-49b6-a4ef-946ee71f1d5c'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('a09285b0-2d47-49b6-a4ef-946ee71f1d5c', NULL,"
" 'Auto Cache Rebuild',"
" 'Whether to rebuild report caches on changes affecting severity.',"
" '1');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '" SETTING_UUID_LSC_DEB_MAINTAINER "'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('" SETTING_UUID_LSC_DEB_MAINTAINER "', NULL,"
" 'Debian LSC Package Maintainer',"
" 'Maintainer email address used in generated Debian LSC packages.',"
" '');");
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '" SETTING_UUID_FEED_IMPORT_ROLES "'"
" AND " ACL_IS_GLOBAL () ";")
== 0)
sql ("INSERT into settings (uuid, owner, name, comment, value)"
" VALUES"
" ('" SETTING_UUID_FEED_IMPORT_ROLES "', NULL,"
" 'Feed Import Roles',"
" 'Roles given access to new resources from feed.',"
" '" ROLE_UUID_ADMIN "," ROLE_UUID_USER "');");
}
/**
* @brief Add command permission to role.
*
* Caller must ensure args are SQL escaped.
*
* @param[in] role_id Role.
* @param[in] permission Permission.
*/
static void
add_role_permission (const gchar *role_id, const gchar *permission)
{
if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
" WHERE owner IS NULL"
" AND name = lower ('%s')"
" AND resource_type = ''"
" AND resource = 0"
" AND subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '%s'));",
permission,
role_id) == 0)
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (), NULL, lower ('%s'), '', '',"
" 0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
" (SELECT id FROM roles WHERE uuid = '%s'),"
" " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
permission,
role_id);
}
/**
* @brief Add resource permission to role.
*
* Caller must ensure args are SQL escaped.
*
* @param[in] role_id Role ID.
* @param[in] permission Permission.
* @param[in] type Resource type.
* @param[in] resource_id Resource ID.
*/
void
add_role_permission_resource (const gchar *role_id, const gchar *permission,
const gchar *type, const gchar *resource_id)
{
if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
" WHERE owner IS NULL"
" AND name = lower ('%s')"
" AND resource_type = '%s'"
" AND resource = (SELECT id FROM %ss"
" WHERE uuid = '%s')"
" AND subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '%s'));",
permission,
type,
type,
resource_id,
role_id)
== 0)
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (), NULL, lower ('%s'), '', '%s',"
" (SELECT id FROM %ss WHERE uuid = '%s'), '%s',"
" " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
" (SELECT id FROM roles WHERE uuid = '%s'),"
" " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
permission,
type,
type,
resource_id,
resource_id,
role_id);
}
/**
* @brief Ensure that the databases are the right versions.
*
* @return 0 success, -1 error, -2 database is wrong version.
*/
static int
check_db_versions ()
{
char *database_version;
int scap_db_version, cert_db_version;
database_version = sql_string ("SELECT value FROM %s.meta"
" WHERE name = 'database_version';",
sql_schema ());
if (database_version)
{
if (strcmp (database_version,
G_STRINGIFY (GVMD_DATABASE_VERSION)))
{
g_message ("%s: database version of database: %s",
__func__,
database_version);
g_message ("%s: database version supported by manager: %s",
__func__,
G_STRINGIFY (GVMD_DATABASE_VERSION));
g_free (database_version);
return -2;
}
g_free (database_version);
}
/* Check SCAP database version. */
scap_db_version = manage_scap_db_version ();
if (scap_db_version == -1)
g_message ("No SCAP database found");
else if (scap_db_version != manage_scap_db_supported_version ())
{
g_message ("%s: database version of SCAP database: %i",
__func__,
scap_db_version);
g_message ("%s: SCAP database version supported by manager: %s",
__func__,
G_STRINGIFY (GVMD_SCAP_DATABASE_VERSION));
return -2;
}
/* Check CERT database version. */
cert_db_version = manage_cert_db_version ();
if (cert_db_version == -1)
g_message ("No CERT database found");
else if (cert_db_version != manage_cert_db_supported_version ())
{
g_message ("%s: database version of CERT database: %i",
__func__,
cert_db_version);
g_message ("%s: CERT database version supported by manager: %s",
__func__,
G_STRINGIFY (GVMD_CERT_DATABASE_VERSION));
return -2;
}
return 0;
}
/**
* @brief Ensures the sanity of nvts cache in DB.
*/
static void
check_db_nvt_selectors ()
{
/* Ensure every part of the predefined selector exists.
* This restores entries lost due to the error solved 2010-08-13 by r8805. */
if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
" '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_ALL) ";")
== 0)
{
sql ("INSERT into nvt_selectors (name, exclude, type, family_or_nvt)"
" VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 0, "
G_STRINGIFY (NVT_SELECTOR_TYPE_ALL) ", NULL);");
}
if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
" '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
" AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.810002';")
== 0)
{
sql ("INSERT into nvt_selectors"
" (name, exclude, type, family_or_nvt, family)"
" VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
/* OID of the "CPE Inventory" NVT. */
" '1.3.6.1.4.1.25623.1.0.810002', 'Service detection');");
}
if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
" '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
" AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.810003';")
== 0)
{
sql ("INSERT into nvt_selectors"
" (name, exclude, type, family_or_nvt, family)"
" VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
/* OID of the "Host Summary" NVT. */
" '1.3.6.1.4.1.25623.1.0.810003', 'General');");
}
if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
" '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_FAMILY)
" AND family_or_nvt = 'Port scanners';")
== 0)
{
sql ("INSERT into nvt_selectors"
" (name, exclude, type, family_or_nvt, family)"
" VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
G_STRINGIFY (NVT_SELECTOR_TYPE_FAMILY) ","
" 'Port scanners', 'Port scanners');");
}
if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
" '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
" AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.14259';")
== 0)
{
sql ("INSERT into nvt_selectors"
" (name, exclude, type, family_or_nvt, family)"
" VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 0, "
G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
/* OID of the "Nmap (NASL wrapper)" NVT. */
" '1.3.6.1.4.1.25623.1.0.14259', 'Port scanners');");
}
if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
" '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
" AND family_or_nvt = '" OID_PING_HOST "';")
== 0)
{
sql ("INSERT into nvt_selectors"
" (name, exclude, type, family_or_nvt, family)"
" VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 0, "
G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
" '" OID_PING_HOST "', 'Port scanners');");
}
if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
" '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
" AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.80109';")
== 0)
{
sql ("INSERT into nvt_selectors"
" (name, exclude, type, family_or_nvt, family)"
" VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
/* OID of the "w3af (NASL wrapper)" NVT. */
" '1.3.6.1.4.1.25623.1.0.80109', 'Web application abuses');");
}
}
/**
* @brief Add permissions for all global resources.
*
* @param[in] role_uuid UUID of role.
*/
static void
add_permissions_on_globals (const gchar *role_uuid)
{
iterator_t scanners;
/* Scanners are global when created from the command line. */
init_iterator (&scanners,
"SELECT id, uuid FROM scanners WHERE owner is NULL;");
while (next (&scanners))
add_role_permission_resource (role_uuid, "GET_SCANNERS",
"scanner",
iterator_string (&scanners, 1));
cleanup_iterator (&scanners);
add_role_permission_resource (role_uuid, "GET_ROLES",
"role",
ROLE_UUID_ADMIN);
add_role_permission_resource (role_uuid, "GET_ROLES",
"role",
ROLE_UUID_GUEST);
add_role_permission_resource (role_uuid, "GET_ROLES",
"role",
ROLE_UUID_INFO);
add_role_permission_resource (role_uuid, "GET_ROLES",
"role",
ROLE_UUID_MONITOR);
add_role_permission_resource (role_uuid, "GET_ROLES",
"role",
ROLE_UUID_USER);
add_role_permission_resource (role_uuid, "GET_ROLES",
"role",
ROLE_UUID_OBSERVER);
}
/**
* @brief Ensure the predefined permissions exists.
*/
static void
check_db_permissions ()
{
command_t *command;
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE uuid = '" PERMISSION_UUID_ADMIN_EVERYTHING "';")
== 0)
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" VALUES"
" ('" PERMISSION_UUID_ADMIN_EVERYTHING "', NULL, 'Everything', '', '',"
" 0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
" (SELECT id FROM roles WHERE uuid = '" ROLE_UUID_ADMIN "'),"
" " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());");
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE uuid = '" PERMISSION_UUID_SUPER_ADMIN_EVERYTHING "';")
== 0)
{
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" VALUES"
" ('" PERMISSION_UUID_SUPER_ADMIN_EVERYTHING "', NULL, 'Everything',"
" '', '', 0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
" (SELECT id FROM roles WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "'),"
" " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());");
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (), NULL, 'Super',"
" '', '', 0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
" (SELECT id FROM roles WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "'),"
" " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());");
}
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_GUEST "')"
" AND resource = 0;")
<= 1)
{
/* Clean-up any remaining permissions. */
sql ("DELETE FROM permissions WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_GUEST "');");
}
add_role_permission (ROLE_UUID_GUEST, "AUTHENTICATE");
add_role_permission (ROLE_UUID_GUEST, "HELP");
add_role_permission (ROLE_UUID_GUEST, "GET_AGGREGATES");
add_role_permission (ROLE_UUID_GUEST, "GET_FILTERS");
add_role_permission (ROLE_UUID_GUEST, "GET_INFO");
add_role_permission (ROLE_UUID_GUEST, "GET_NVTS");
add_role_permission (ROLE_UUID_GUEST, "GET_SETTINGS");
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_INFO "')"
" AND resource = 0;")
<= 1)
{
/* Clean-up any remaining permissions. */
sql ("DELETE FROM permissions WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_INFO "');");
}
add_role_permission (ROLE_UUID_INFO, "AUTHENTICATE");
add_role_permission (ROLE_UUID_INFO, "HELP");
add_role_permission (ROLE_UUID_INFO, "GET_AGGREGATES");
add_role_permission (ROLE_UUID_INFO, "GET_INFO");
add_role_permission (ROLE_UUID_INFO, "GET_NVTS");
add_role_permission (ROLE_UUID_INFO, "GET_SETTINGS");
add_role_permission (ROLE_UUID_INFO, "MODIFY_SETTING");
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_MONITOR "')"
" AND resource = 0;")
<= 1)
{
/* Clean-up any remaining permissions. */
sql ("DELETE FROM permissions WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_MONITOR "');");
}
add_role_permission (ROLE_UUID_MONITOR, "AUTHENTICATE");
add_role_permission (ROLE_UUID_MONITOR, "GET_SETTINGS");
add_role_permission (ROLE_UUID_MONITOR, "GET_SYSTEM_REPORTS");
add_role_permission (ROLE_UUID_MONITOR, "HELP");
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_USER "')"
" AND resource = 0;")
<= 1)
{
/* Clean-up any remaining permissions. */
sql ("DELETE FROM permissions WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_USER "');");
}
command = gmp_commands;
while (command[0].name)
{
if (strstr (command[0].name, "DESCRIBE_AUTH") == NULL
&& strcmp (command[0].name, "GET_VERSION")
&& strstr (command[0].name, "GROUP") == NULL
&& strstr (command[0].name, "ROLE") == NULL
&& strstr (command[0].name, "SYNC") == NULL
&& strstr (command[0].name, "USER") == NULL)
add_role_permission (ROLE_UUID_USER, command[0].name);
command++;
}
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_OBSERVER "')"
" AND resource = 0;")
<= 1)
{
/* Clean-up any remaining permissions. */
sql ("DELETE FROM permissions WHERE subject_type = 'role'"
" AND subject = (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_OBSERVER "');");
}
command = gmp_commands;
while (command[0].name)
{
if ((strstr (command[0].name, "GET") == command[0].name)
&& strcmp (command[0].name, "GET_GROUPS")
&& strcmp (command[0].name, "GET_ROLES")
&& strcmp (command[0].name, "GET_USERS")
&& strcmp (command[0].name, "GET_VERSION"))
add_role_permission (ROLE_UUID_OBSERVER, command[0].name);
command++;
}
add_role_permission (ROLE_UUID_OBSERVER, "AUTHENTICATE");
add_role_permission (ROLE_UUID_OBSERVER, "HELP");
add_role_permission (ROLE_UUID_OBSERVER, "MODIFY_SETTING");
add_permissions_on_globals (ROLE_UUID_ADMIN);
add_permissions_on_globals (ROLE_UUID_GUEST);
add_permissions_on_globals (ROLE_UUID_OBSERVER);
add_permissions_on_globals (ROLE_UUID_USER);
}
/**
* @brief Ensure the predefined roles exists.
*/
static void
check_db_roles ()
{
if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_ADMIN "';")
== 0)
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" ('" ROLE_UUID_ADMIN "', NULL, 'Admin',"
" 'Administrator. Full privileges.',"
" m_now (), m_now ());");
if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_GUEST "';")
== 0)
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" ('" ROLE_UUID_GUEST "', NULL, 'Guest',"
" 'Guest.',"
" m_now (), m_now ());");
if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_INFO "';")
== 0)
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" ('" ROLE_UUID_INFO "', NULL, 'Info',"
" 'Information browser.',"
" m_now (), m_now ());");
if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_MONITOR "';")
== 0)
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" ('" ROLE_UUID_MONITOR "', NULL, 'Monitor',"
" 'Performance monitor.',"
" m_now (), m_now ());");
if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_USER "';")
== 0)
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" ('" ROLE_UUID_USER "', NULL, 'User',"
" 'Standard user.',"
" m_now (), m_now ());");
if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "';")
== 0)
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" ('" ROLE_UUID_SUPER_ADMIN "', NULL, 'Super Admin',"
" 'Super administrator. Full privileges with access to all users.',"
" m_now (), m_now ());");
if (sql_int ("SELECT count(*) FROM roles"
" WHERE uuid = '" ROLE_UUID_OBSERVER "';")
== 0)
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" ('" ROLE_UUID_OBSERVER "', NULL, 'Observer',"
" 'Observer.',"
" m_now (), m_now ());");
}
/**
* @brief Cleanup the auth_cache table.
*/
static void
clean_auth_cache ()
{
sql ("DELETE FROM auth_cache;");
}
/**
* @brief Tries to migrate sensor type scanners to match the relays.
*
* @return A string describing the results or NULL on error.
*/
static gchar *
manage_migrate_relay_sensors ()
{
iterator_t scanners;
int gmp_successes, gmp_failures, osp_failures;
gmp_successes = gmp_failures = osp_failures = 0;
if (get_relay_mapper_path () == NULL)
{
g_warning ("%s: No relay mapper set", __func__);
return NULL;
}
init_iterator (&scanners,
"SELECT id, uuid, type, host, port FROM scanners"
" WHERE type = %d",
SCANNER_TYPE_OSP_SENSOR);
while (next (&scanners))
{
scanner_type_t type;
const char *scanner_id, *host;
int port;
scanner_id = iterator_string (&scanners, 1);
type = iterator_int (&scanners, 2);
host = iterator_string (&scanners, 3);
port = iterator_int (&scanners, 4);
if (relay_supports_scanner_type (host, port, type) == FALSE)
{
if (type == SCANNER_TYPE_OSP_SENSOR)
{
g_message ("%s: No relay found for OSP Sensor %s (%s:%d).",
__func__, scanner_id, host, port);
osp_failures++;
}
else
g_warning ("%s: Unexpected type for scanner %s: %d",
__func__, scanner_id, type);
}
}
cleanup_iterator (&scanners);
if (gmp_successes == 0 && gmp_failures == 0 && osp_failures == 0)
return g_strdup ("All GMP or OSP sensors up to date.");
else
{
GString *message = g_string_new ("");
g_string_append_printf (message,
"%d sensors(s) not matching:",
gmp_successes + gmp_failures + osp_failures);
if (gmp_successes)
g_string_append_printf (message,
" %d GMP scanner(s) migrated to OSP.",
gmp_successes);
if (gmp_failures)
g_string_append_printf (message,
" %d GMP scanner(s) not migrated.",
gmp_failures);
if (osp_failures)
g_string_append_printf (message,
" %d OSP sensor(s) not migrated.",
osp_failures);
return g_string_free (message, FALSE);
}
}
/**
* @brief Ensure that the database is in order.
*
* Only called by init_manage_internal, and ultimately only by the main process.
*
* @param[in] check_encryption_key Whether to check encryption key.
*
* @return 0 success, -1 error.
*/
static int
check_db (int check_encryption_key)
{
/* The file locks managed at startup ensure that this is the only Manager
* process accessing the db. Nothing else should be accessing the db, access
* should always go through Manager. */
sql_begin_immediate ();
if (check_db_extensions ())
goto fail;
create_tables ();
check_db_sequences ();
set_db_version (GVMD_DATABASE_VERSION);
check_db_roles ();
check_db_nvt_selectors ();
check_db_nvts ();
check_db_port_lists ();
clean_auth_cache ();
if (check_db_scanners ())
goto fail;
if (check_db_report_formats ())
goto fail;
if (check_db_report_formats_trash ())
goto fail;
check_db_permissions ();
check_db_settings ();
cleanup_schedule_times ();
if (check_encryption_key && check_db_encryption_key ())
goto fail;
sql_commit ();
return 0;
fail:
sql_rollback ();
return -1;
}
/**
* @brief Stop any active tasks.
*/
static void
stop_active_tasks ()
{
iterator_t tasks;
get_data_t get;
/* Set requested and running tasks to stopped. */
assert (current_credentials.uuid == NULL);
memset (&get, '\0', sizeof (get));
get.ignore_pagination = 1;
init_task_iterator (&tasks, &get);
while (next (&tasks))
{
switch (task_iterator_run_status (&tasks))
{
case TASK_STATUS_DELETE_REQUESTED:
case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
case TASK_STATUS_DELETE_ULTIMATE_WAITING:
case TASK_STATUS_DELETE_WAITING:
case TASK_STATUS_REQUESTED:
case TASK_STATUS_RUNNING:
case TASK_STATUS_QUEUED:
case TASK_STATUS_STOP_REQUESTED:
case TASK_STATUS_STOP_WAITING:
{
task_t index = get_iterator_resource (&tasks);
/* Set the current user, for event checks. */
current_credentials.uuid = task_owner_uuid (index);
task_last_report_any_status (index, &global_current_report);
set_task_interrupted (index,
"Task process exited abnormally"
" (e.g. machine lost power or process was"
" sent SIGKILL)."
" Setting scan status to Interrupted.");
global_current_report = 0;
free (current_credentials.uuid);
break;
}
default:
break;
}
}
cleanup_iterator (&tasks);
current_credentials.uuid = NULL;
/* Set requested and running reports to stopped. */
sql ("UPDATE reports SET scan_run_status = %u"
" WHERE scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u;",
TASK_STATUS_INTERRUPTED,
TASK_STATUS_DELETE_REQUESTED,
TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
TASK_STATUS_DELETE_ULTIMATE_WAITING,
TASK_STATUS_DELETE_WAITING,
TASK_STATUS_REQUESTED,
TASK_STATUS_RUNNING,
TASK_STATUS_QUEUED,
TASK_STATUS_STOP_REQUESTED,
TASK_STATUS_STOP_WAITING);
}
/**
* @brief Clean up database tables.
*
* Remove superfluous entries from tables.
*/
static void
cleanup_tables ()
{
/* Remove group and role assignments of deleted users.
*
* This should be a migrator, but this way is easier to backport. */
sql ("DELETE FROM group_users"
" WHERE \"user\" NOT IN (SELECT id FROM users);");
sql ("DELETE FROM group_users_trash"
" WHERE \"user\" NOT IN (SELECT id FROM users);");
sql ("DELETE FROM role_users"
" WHERE \"user\" NOT IN (SELECT id FROM users);");
sql ("DELETE FROM role_users_trash"
" WHERE \"user\" NOT IN (SELECT id FROM users);");
/*
* Remove permissions of deleted users, groups and roles.
*/
sql ("DELETE FROM permissions"
" WHERE (subject_type = 'user'"
" AND subject NOT IN (SELECT id FROM users))"
" OR (subject_type = 'group'"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
" AND subject NOT IN (SELECT id FROM groups))"
" OR (subject_type = 'group'"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
" AND subject NOT IN (SELECT id FROM groups_trash))"
" OR (subject_type = 'role'"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
" AND subject NOT IN (SELECT id FROM roles))"
" OR (subject_type = 'role'"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
" AND subject NOT IN (SELECT id FROM roles_trash));");
sql ("DELETE FROM permissions_trash"
" WHERE (subject_type = 'user'"
" AND subject NOT IN (SELECT id FROM users))"
" OR (subject_type = 'group'"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
" AND subject NOT IN (SELECT id FROM groups))"
" OR (subject_type = 'group'"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
" AND subject NOT IN (SELECT id FROM groups_trash))"
" OR (subject_type = 'role'"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
" AND subject NOT IN (SELECT id FROM roles))"
" OR (subject_type = 'role'"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
" AND subject NOT IN (SELECT id FROM roles_trash));");
sql ("DELETE FROM permissions_get_tasks"
" WHERE \"user\" NOT IN (SELECT id FROM users);");
}
/**
* @brief Initialize the manage library.
*
* Check DB version, do startup database checks, load the NVT cache.
* Optionally also stop active tasks.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of database.
* @param[in] max_ips_per_target Max number of IPs per target.
* @param[in] max_email_attachment_size Max size of email attachments.
* @param[in] max_email_include_size Max size of email inclusions.
* @param[in] max_email_message_size Max size of email user message text.
* @param[in] stop_tasks Stop any active tasks.
* @param[in] fork_connection Function to fork a connection that will
* accept GMP requests. Used to start tasks
* with GMP when an alert occurs.
* @param[in] skip_db_check Skip DB check.
* @param[in] check_encryption_key Check encryption key if doing DB check.
*
* @return 0 success, -1 error, -2 database is wrong version,
* -4 max_ips_per_target out of range.
*/
static int
init_manage_internal (GSList *log_config,
const db_conn_info_t *database,
int max_ips_per_target,
int max_email_attachment_size,
int max_email_include_size,
int max_email_message_size,
int stop_tasks,
manage_connection_forker_t fork_connection,
int skip_db_check,
int check_encryption_key)
{
int ret;
/* Summary of init cases:
*
* daemon [--foreground]
* init_gmpd cache 0
* init_manage
* serve_and_schedule
* forks child (serve_gmp)
* init_gmpd_process
* init_manage_process
* ...
* event
* fork_connection_for_event
* fork one
* init_gmpd_process
* init_manage_process
* serve_client
* fork two
* gmp_auth, gmp_start_task_report.
* ...
* manage_schedule
* fork_connection_for_scheduler
* fork one
* init_gmpd_process
* init_manage_process
* serve_client
* fork two
* gmp_auth, gmp_start_task_report, gmp_resume_task_report.
* --create-user --delete-user --get-users
* manage_create, ...
* init_manage_helper
* --encrypt/decrypt-all-credentials
* manage_encrypt_...
* init_manage_helper
* --migrate
* manage_migrate
* init_manage_process (sorts out db state itself) */
if ((max_ips_per_target <= 0)
|| (max_ips_per_target > MANAGE_ABSOLUTE_MAX_IPS_PER_TARGET))
return -4;
max_hosts = max_ips_per_target;
if (max_email_attachment_size)
max_attach_length = max_email_attachment_size;
if (max_email_include_size)
max_content_length = max_email_include_size;
if (max_email_message_size)
max_email_message_length = max_email_message_size;
g_log_set_handler (G_LOG_DOMAIN,
ALL_LOG_LEVELS,
(GLogFunc) gvm_log_func,
log_config);
memset (¤t_credentials, '\0', sizeof (current_credentials));
init_manage_open_db (database);
/* Check that the versions of the databases are correct. */
ret = check_db_versions ();
if (ret)
return ret;
init_manage_create_functions ();
/* Ensure the database is complete, removing superfluous rows.
*
* Assume that all other running processes are from the same Manager version,
* because some of these checks will modify the database if it is out of
* date. This is relevant because the caller may be a command option process
* like a --create-user process. */
if (skip_db_check == 0)
{
/* This only happens for init_manage callers with skip_db_check set to 0
* and init_manage_helper callers. So there are only 2 callers:
*
* 1 the main process
* 2 a helper processes (--create-user, --get-users, etc) when the
* main process is not running. */
ret = check_db (check_encryption_key);
if (ret)
return ret;
cleanup_tables ();
/* Set max_hosts in db, so database server side can access it. */
sql ("DELETE FROM meta WHERE name = 'max_hosts';");
sql ("INSERT INTO meta (name, value) VALUES ('max_hosts', %i);", max_hosts);
}
if (stop_tasks)
/* Stop any active tasks. */
stop_active_tasks ();
/* Load the NVT cache into memory. */
if (nvti_cache == NULL)
update_nvti_cache ();
if (skip_db_check == 0)
/* Requires NVT cache. */
check_db_configs ();
sql_close ();
gvmd_db_conn_info.name = database->name ? g_strdup (database->name) : NULL;
gvmd_db_conn_info.host = database->host ? g_strdup (database->host) : NULL;
gvmd_db_conn_info.port = database->port ? g_strdup (database->port) : NULL;
gvmd_db_conn_info.user = database->user ? g_strdup (database->user) : NULL;
if (fork_connection)
manage_fork_connection = fork_connection;
return 0;
}
/**
* @brief Initialize the manage library.
*
* Check DB version, do startup database checks, load the NVT cache.
*
* Ensure all tasks are in a clean initial state.
*
* Beware that calling this function while tasks are running may lead to
* problems.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of database.
* @param[in] max_ips_per_target Max number of IPs per target.
* @param[in] max_email_attachment_size Max size of email attachments.
* @param[in] max_email_include_size Max size of email inclusions.
* @param[in] max_email_message_size Max size of email user message text.
* @param[in] fork_connection Function to fork a connection that will
* accept GMP requests. Used to start tasks
* with GMP when an alert occurs.
* @param[in] skip_db_check Skip DB check.
*
* @return 0 success, -1 error, -2 database is wrong version, -3 database needs
* to be initialised from server, -4 max_ips_per_target out of range.
*/
int
init_manage (GSList *log_config, const db_conn_info_t *database,
int max_ips_per_target, int max_email_attachment_size,
int max_email_include_size, int max_email_message_size,
manage_connection_forker_t fork_connection,
int skip_db_check)
{
return init_manage_internal (log_config,
database,
max_ips_per_target,
max_email_attachment_size,
max_email_include_size,
max_email_message_size,
1, /* Stop active tasks. */
fork_connection,
skip_db_check,
1); /* Check encryption key if checking db. */
}
/**
* @brief Initialize the manage library for a helper program.
*
* This should be called at the beginning of any program that accesses the
* database. Forked processes should call init_manage_process. The daemon
* itself calls init_manage, including in NVT cache mode.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of database.
* @param[in] max_ips_per_target Max number of IPs per target.
*
* @return 0 success, -1 error, -2 database is wrong version, -3 database needs
* to be initialised from server, -4 max_ips_per_target out of range.
*/
int
init_manage_helper (GSList *log_config, const db_conn_info_t *database,
int max_ips_per_target)
{
return init_manage_internal (log_config,
database,
max_ips_per_target,
0, /* Default max_email_attachment_size. */
0, /* Default max_email_include_size. */
0, /* Default max_email_message_size */
0, /* Stop active tasks. */
NULL,
/* Skip DB check if main process is running, to
* avoid locking issues when creating tables.
*
* Safe because main process did the check. */
lockfile_locked ("gvm-serving")
? 1 /* Skip DB check. */
: 0, /* Do DB check. */
0); /* Dummy. */
}
/**
* @brief Cleanup the manage library.
*
* Optionally put any running task in the interrupted state and close the
* database.
*
* @param[in] cleanup If TRUE perform all cleanup operations, else only
* those required at the start of a forked process.
*/
void
cleanup_manage_process (gboolean cleanup)
{
if (sql_is_open ())
{
if (cleanup)
{
if (current_scanner_task)
{
if (global_current_report)
{
result_t result;
result = make_result (current_scanner_task,
"", "", "", "", "Error Message",
"Interrupting scan because GVM is"
" exiting.",
NULL);
report_add_result (global_current_report, result);
}
set_task_run_status (current_scanner_task, TASK_STATUS_INTERRUPTED);
}
sql_close ();
}
else
sql_close_fork ();
}
}
/**
* @brief Cleanup as immediately as possible.
*
* Put any running task in the error state and close the database.
*
* Intended for handlers for signals like SIGSEGV and SIGABRT.
*
* @param[in] signal Dummy argument for use as signal handler.
*/
void
manage_cleanup_process_error (int signal)
{
g_debug ("Received %s signal", strsignal (signal));
if (sql_is_open ())
{
if (current_scanner_task)
{
g_warning ("%s: Error exit, setting running task to Interrupted",
__func__);
set_task_interrupted (current_scanner_task,
"Error exit, setting running task to"
" Interrupted.");
}
sql_close ();
}
}
/**
* @brief Cleanup as immediately as possible.
*/
void
manage_reset_currents ()
{
global_current_report = 0;
current_scanner_task = (task_t) 0;
free_credentials (¤t_credentials);
}
/**
* @brief Get user hash.
*
* This is for "file" users, now entirely stored in db.
*
* @param[in] username User name.
*
* @return Hash.
*/
gchar *
manage_user_hash (const gchar *username)
{
gchar *hash, *quoted_username;
quoted_username = sql_quote (username);
hash = sql_string ("SELECT password FROM users WHERE name = '%s';",
quoted_username);
g_free (quoted_username);
return hash;
}
/**
* @brief Get user uuid.
*
* @param[in] username User name.
* @param[in] method Authentication method.
*
* @return UUID.
*/
static gchar *
user_uuid_method (const gchar *username, auth_method_t method)
{
gchar *uuid, *quoted_username, *quoted_method;
quoted_username = sql_quote (username);
quoted_method = sql_quote (auth_method_name (method));
uuid = sql_string ("SELECT uuid FROM users"
" WHERE name = '%s' AND method = '%s';",
quoted_username,
quoted_method);
g_free (quoted_username);
g_free (quoted_method);
return uuid;
}
/**
* @brief Check whether LDAP is enabled.
*
* @return 0 no, else yes.
*/
static int
ldap_auth_enabled ()
{
if (gvm_auth_ldap_enabled ())
return sql_int ("SELECT coalesce ((SELECT CAST (value AS INTEGER) FROM meta"
" WHERE name = 'ldap_enable'),"
" 0);");
return 0;
}
/**
* @brief Check whether RADIUS is enabled.
*
* @return 0 no, else yes.
*/
static int
radius_auth_enabled ()
{
if (gvm_auth_radius_enabled ())
return sql_int ("SELECT coalesce ((SELECT CAST (value AS INTEGER) FROM meta"
" WHERE name = 'radius_enable'),"
" 0);");
return 0;
}
/**
* @brief Check if user exists.
*
* @param[in] name User name.
* @param[in] method Auth method.
*
* @return 1 yes, 0 no.
*/
static int
user_exists_method (const gchar *name, auth_method_t method)
{
gchar *quoted_name, *quoted_method;
int ret;
quoted_name = sql_quote (name);
quoted_method = sql_quote (auth_method_name (method));
ret = sql_int ("SELECT count (*) FROM users"
" WHERE name = '%s' AND method = '%s';",
quoted_name,
quoted_method);
g_free (quoted_name);
g_free (quoted_method);
return ret;
}
/**
* @brief Get user uuid, trying all authentication methods.
*
* @param[in] name User name.
*
* @return UUID.
*/
static gchar *
user_uuid_any_method (const gchar *name)
{
if (ldap_auth_enabled ()
&& user_exists_method (name, AUTHENTICATION_METHOD_LDAP_CONNECT))
return user_uuid_method (name, AUTHENTICATION_METHOD_LDAP_CONNECT);
if (radius_auth_enabled ()
&& user_exists_method (name, AUTHENTICATION_METHOD_RADIUS_CONNECT))
return user_uuid_method (name, AUTHENTICATION_METHOD_RADIUS_CONNECT);
if (user_exists_method (name, AUTHENTICATION_METHOD_FILE))
return user_uuid_method (name, AUTHENTICATION_METHOD_FILE);
return NULL;
}
/**
* @brief Ensure the user exists in the database.
*
* @param[in] name User name.
* @param[in] method Auth method.
*
* @return 0 success.
*/
static int
user_ensure_in_db (const gchar *name, const gchar *method)
{
gchar *quoted_name, *quoted_method;
if ((method == NULL) || (strcasecmp (method, "file") == 0))
/* A "file" user, now entirely stored in db. */
return 0;
/* SELECT then INSERT instead of using "INSERT OR REPLACE", so that the
* id stays the same. */
quoted_name = sql_quote (name);
quoted_method = sql_quote (method);
if (sql_int ("SELECT count(*)"
" FROM users WHERE name = '%s' and method = '%s';",
quoted_name,
quoted_method))
{
g_free (quoted_method);
g_free (quoted_name);
return 0;
}
sql ("INSERT INTO users"
" (uuid, owner, name, comment, password, timezone, method, hosts,"
" hosts_allow, ifaces, ifaces_allow, creation_time, modification_time)"
" VALUES"
" (make_uuid (),"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', '', NULL, NULL, '%s', '', 2, '', 2, m_now (), m_now ());",
current_credentials.uuid,
quoted_name,
quoted_method);
g_free (quoted_method);
g_free (quoted_name);
return 0;
}
/**
* @brief Check if user exists.
*
* @param[in] name User name.
*
* @return 1 yes, 0 no.
*/
static int
user_exists (const gchar *name)
{
if (ldap_auth_enabled ()
&& user_exists_method (name, AUTHENTICATION_METHOD_LDAP_CONNECT))
return 1;
if (radius_auth_enabled ()
&& user_exists_method (name, AUTHENTICATION_METHOD_RADIUS_CONNECT))
return 1;
return user_exists_method (name, AUTHENTICATION_METHOD_FILE);
}
/**
* @brief Set credentials for authenticate.
*
* @param[in] credentials Credentials.
*
* @return 0 success, 99 permission denied.
*/
static int
credentials_setup (credentials_t *credentials)
{
assert (credentials->uuid);
credentials->role
= g_strdup (acl_user_is_super_admin (credentials->uuid)
? "Super Admin"
: (acl_user_is_admin (credentials->uuid)
? "Admin"
: (acl_user_is_observer (credentials->uuid)
? "Observer"
: (acl_user_is_user (credentials->uuid)
? "User"
: ""))));
if (acl_user_may ("authenticate") == 0)
{
free (credentials->uuid);
credentials->uuid = NULL;
g_free (credentials->role);
credentials->role = NULL;
return 99;
}
credentials->timezone = sql_string ("SELECT timezone FROM users"
" WHERE uuid = '%s';",
credentials->uuid);
credentials->severity_class
= sql_string ("SELECT value FROM settings"
" WHERE name = 'Severity Class'"
" AND " ACL_GLOBAL_OR_USER_OWNS ()
" ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
credentials->uuid);
credentials->dynamic_severity
= sql_int ("SELECT value FROM settings"
" WHERE name = 'Dynamic Severity'"
" AND " ACL_GLOBAL_OR_USER_OWNS ()
" ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
credentials->uuid);
credentials->default_severity
= sql_double ("SELECT value FROM settings"
" WHERE name = 'Default Severity'"
" AND " ACL_GLOBAL_OR_USER_OWNS ()
" ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
credentials->uuid);
return 0;
}
/**
* @brief Search for LDAP or RADIUS credentials in the recently-used
* authentication cache.
*
* @param[in] username Username.
* @param[in] password Password.
* @param[in] method 0 for LDAP, 1 for RADIUS.
*
* @return 0 on success, -1 on failure.
*/
static int
auth_cache_find (const char *username, const char *password, int method)
{
char *hash, *quoted_username;
int ret;
quoted_username = sql_quote (username);
hash = sql_string ("SELECT hash FROM auth_cache WHERE username = '%s'"
" AND method = %i AND creation_time >= m_now () - 300;",
quoted_username, method);
g_free (quoted_username);
if (!hash)
return -1;
// verify for VALID or OUTDATED but don't update
ret = manage_authentication_verify(hash, password);
switch(ret){
case GMA_HASH_INVALID:
ret = 1;
break;
case GMA_HASH_VALID_BUT_DATED:
ret = 0;
break;
case GMA_SUCCESS:
ret = 0;
break;
default:
ret = -1;
break;
}
g_free (hash);
return ret;
}
/**
* @brief Add LDAP or RADIUS credentials to the recently-used authentication
* cache.
*
* @param[in] username Username.
* @param[in] password Password.
* @param[in] method 0 for LDAP, 1 for RADIUS.
*/
static void
auth_cache_insert (const char *username, const char *password, int method)
{
char *hash, *quoted_username;
quoted_username = sql_quote (username);
hash = manage_authentication_hash(password);
sql ("INSERT INTO auth_cache (username, hash, method, creation_time)"
" VALUES ('%s', '%s', %i, m_now ());", quoted_username, hash, method);
/* Cleanup cache */
sql ("DELETE FROM auth_cache WHERE creation_time < m_now () - 300");
}
/**
* @brief Authenticate, trying any method.
*
* @param[in] username Username.
* @param[in] password Password.
* @param[out] auth_method Auth method return.
*
* @return 0 authentication success, 1 authentication failure, 99 permission
* denied, -1 error.
*/
static int
authenticate_any_method (const gchar *username, const gchar *password,
auth_method_t *auth_method)
{
int ret;
gchar *hash;
if (gvm_auth_ldap_enabled ()
&& ldap_auth_enabled ()
&& user_exists_method (username, AUTHENTICATION_METHOD_LDAP_CONNECT))
{
ldap_auth_info_t info;
int allow_plaintext;
gchar *authdn, *host, *cacert;
*auth_method = AUTHENTICATION_METHOD_LDAP_CONNECT;
/* Search the LDAP authentication cache first. */
if (auth_cache_find (username, password, 0) == 0)
return 0;
manage_get_ldap_info (NULL, &host, &authdn, &allow_plaintext, &cacert);
info = ldap_auth_info_new (host, authdn, allow_plaintext);
g_free (host);
g_free (authdn);
ret = ldap_connect_authenticate (username, password, info, cacert);
ldap_auth_info_free (info);
free (cacert);
if (ret == 0)
auth_cache_insert (username, password, 0);
return ret;
}
if (gvm_auth_radius_enabled ()
&& radius_auth_enabled ()
&& user_exists_method (username, AUTHENTICATION_METHOD_RADIUS_CONNECT))
{
char *key = NULL, *host = NULL;
*auth_method = AUTHENTICATION_METHOD_RADIUS_CONNECT;
if (auth_cache_find (username, password, 1) == 0)
return 0;
manage_get_radius_info (NULL, &host, &key);
ret = radius_authenticate (host, key, username, password);
g_free (host);
g_free (key);
if (ret == 0)
auth_cache_insert (username, password, 1);
return ret;
}
*auth_method = AUTHENTICATION_METHOD_FILE;
hash = manage_user_hash (username);
ret = manage_authentication_verify(hash, password);
switch(ret){
case GMA_HASH_INVALID:
ret = 1;
break;
case GMA_HASH_VALID_BUT_DATED:
g_free(hash);
hash = manage_authentication_hash(password);
sql ("UPDATE users SET password = '%s', modification_time = m_now () WHERE name = '%s';",
hash, username);
ret = 0;
break;
case GMA_SUCCESS:
ret = 0;
break;
default:
ret = -1;
break;
}
g_free (hash);
return ret;
}
/**
* @brief Authenticate credentials.
*
* @param[in] credentials Credentials.
*
* @return 0 authentication success, 1 authentication failure, 99 permission
* denied, -1 error.
*/
int
authenticate (credentials_t* credentials)
{
if (credentials->username && credentials->password)
{
int fail;
auth_method_t auth_method;
if (authenticate_allow_all)
{
/* This flag is set when Manager makes a connection to itself, for
* scheduled tasks and alerts. Take the stored uuid
* to be able to tell apart locally authenticated vs remotely
* authenticated users (in order to fetch the correct rules). */
credentials->uuid = g_strdup (get_scheduled_user_uuid ());
if (*credentials->uuid)
{
if (credentials_setup (credentials))
return 99;
manage_session_init (credentials->uuid);
return 0;
}
return -1;
}
fail = authenticate_any_method (credentials->username,
credentials->password,
&auth_method);
if (fail == 0)
{
gchar *quoted_name, *quoted_method;
/* Authentication succeeded. */
user_ensure_in_db (credentials->username,
auth_method_name (auth_method));
quoted_name = sql_quote (credentials->username);
quoted_method = sql_quote (auth_method_name (auth_method));
credentials->uuid = sql_string ("SELECT uuid FROM users"
" WHERE name = '%s'"
" AND method = '%s';",
quoted_name,
quoted_method);
g_free (quoted_name);
g_free (quoted_method);
if (credentials_setup (credentials))
{
free (credentials->uuid);
credentials->uuid = NULL;
credentials->role = NULL;
return 99;
}
manage_session_init (credentials->uuid);
return 0;
}
return fail;
}
return 1;
}
/**
* @brief Return number of resources of a certain type for current user.
*
* @param[in] type Type.
* @param[in] get GET params.
*
* @return The number of resources associated with the current user.
*/
int
resource_count (const char *type, const get_data_t *get)
{
static const char *filter_columns[] = { "owner", NULL };
static column_t select_columns[] = {{ "owner", NULL }, { NULL, NULL }};
get_data_t count_get;
gchar *extra_where;
int rc;
memset (&count_get, '\0', sizeof (count_get));
count_get.trash = get->trash;
if (type_owned (type))
count_get.filter = "rows=-1 first=1 permission=any owner=any";
else
count_get.filter = "rows=-1 first=1 permission=any";
if (strcasecmp (type, "config") == 0)
{
const gchar *usage_type = get_data_get_extra (get, "usage_type");
extra_where = configs_extra_where (usage_type);
}
else if (strcmp (type, "task") == 0)
{
const gchar *usage_type = get_data_get_extra (get, "usage_type");
extra_where = tasks_extra_where (get->trash, usage_type);
}
else if (strcmp (type, "report") == 0)
{
extra_where = g_strdup (" AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 0");
}
else if (strcmp (type, "result") == 0)
{
extra_where
= g_strdup (" AND (severity != " G_STRINGIFY (SEVERITY_ERROR) ")");
}
else if (strcmp (type, "vuln") == 0)
{
extra_where = g_strdup (" AND vuln_results_exist"
" (vulns.uuid,"
" cast (null AS integer),"
" cast (null AS integer),"
" cast (null AS text))");
}
else
extra_where = NULL;
rc = count (get->subtype ? get->subtype : type,
&count_get,
type_owned (type) ? select_columns : NULL,
type_owned (type) ? select_columns : NULL,
type_owned (type) ? filter_columns : NULL,
0, NULL,
extra_where,
type_owned (type));
g_free (extra_where);
return rc;
}
/**
* @brief Return the number of tasks associated with the current user.
*
* @param[in] get GET params.
*
* @return The number of tasks associated with the current user.
*/
unsigned int
task_count (const get_data_t *get)
{
static const char *extra_columns[] = TASK_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = TASK_ITERATOR_COLUMNS;
static column_t where_columns[] = TASK_ITERATOR_WHERE_COLUMNS;
char *filter;
int overrides, min_qod;
const char *usage_type;
gchar *extra_tables, *extra_where;
int ret;
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
overrides = filter_term_apply_overrides (filter ? filter : get->filter);
min_qod = filter_term_min_qod (filter ? filter : get->filter);
free (filter);
extra_tables = task_iterator_opts_table (overrides, min_qod, 0);
usage_type = get_data_get_extra (get, "usage_type");
extra_where = tasks_extra_where (get->trash, usage_type);
ret = count2 ("task", get,
columns,
columns,
where_columns,
where_columns,
extra_columns, 0,
extra_tables,
extra_where,
NULL,
TRUE);
g_free (extra_tables);
g_free (extra_where);
return ret;
}
/**
* @brief Return the UUID of a task.
*
* @param[in] task Task.
* @param[out] id Pointer to a newly allocated string.
*
* @return 0.
*/
int
task_uuid (task_t task, char ** id)
{
*id = sql_string ("SELECT uuid FROM tasks WHERE id = %llu;",
task);
return 0;
}
/**
* @brief Return whether a task is in the trashcan.
*
* @param[in] task Task.
*
* @return 1 if in trashcan, else 0.
*/
int
task_in_trash (task_t task)
{
return sql_int ("SELECT hidden = 2"
" FROM tasks WHERE id = %llu;",
task);
}
/**
* @brief Return whether a task is in the trashcan.
*
* Assume the UUID is properly formatted.
*
* @param[in] task_id Task UUID.
*
* @return 1 if in trashcan, else 0.
*/
int
task_in_trash_id (const gchar *task_id)
{
return sql_int ("SELECT hidden = 2"
" FROM tasks WHERE uuid = '%s';",
task_id);
}
/**
* @brief Return the name of the owner of a task.
*
* @param[in] task Task.
*
* @return Newly allocated user name.
*/
char*
task_owner_name (task_t task)
{
return sql_string ("SELECT name FROM users WHERE id ="
" (SELECT owner FROM tasks WHERE id = %llu);",
task);
}
/**
* @brief Return the name of the owner of a task.
*
* @param[in] task Task.
*
* @return Newly allocated user name.
*/
static char*
task_owner_uuid (task_t task)
{
return sql_string ("SELECT uuid FROM users WHERE id ="
" (SELECT owner FROM tasks WHERE id = %llu);",
task);
}
/**
* @brief Return the name of a task.
*
* @param[in] task Task.
*
* @return Task name.
*/
char*
task_name (task_t task)
{
return sql_string ("SELECT name FROM tasks WHERE id = %llu;",
task);
}
/**
* @brief Return the comment of a task.
*
* @param[in] task Task.
*
* @return Comment of task.
*/
char*
task_comment (task_t task)
{
return sql_string ("SELECT comment FROM tasks WHERE id = %llu;",
task);
}
/**
* @brief Return the hosts ordering of a task.
*
* @param[in] task Task.
*
* @return Hosts ordering of task.
*/
char*
task_hosts_ordering (task_t task)
{
return sql_string ("SELECT hosts_ordering FROM tasks WHERE id = %llu;",
task);
}
/**
* @brief Return the observers of a task.
*
* @param[in] task Task.
*
* @return Observers of task.
*/
char*
task_observers (task_t task)
{
iterator_t users;
GString *observers;
observers = g_string_new ("");
init_task_user_iterator (&users, task);
if (next (&users))
{
g_string_append (observers, task_user_iterator_name (&users));
while (next (&users))
g_string_append_printf (observers,
" %s",
task_user_iterator_name (&users));
}
cleanup_iterator (&users);
return g_string_free (observers, FALSE);
}
/**
* @brief Return the config of a task.
*
* @param[in] task Task.
*
* @return Config of task.
*/
config_t
task_config (task_t task)
{
config_t config;
switch (sql_int64 (&config,
"SELECT config FROM tasks WHERE id = %llu;",
task))
{
case 0:
return config;
default: /* Programming error. */
case 1: /* Too few rows in result of query. */
case -1: /* Error. */
/* Every task should have a config. */
assert (0);
return 0;
break;
}
}
/**
* @brief Return the UUID of the config of a task.
*
* @param[in] task Task.
*
* @return UUID of config of task.
*/
char*
task_config_uuid (task_t task)
{
if (task_config_in_trash (task))
return sql_string ("SELECT uuid FROM configs_trash WHERE id ="
" (SELECT config FROM tasks WHERE id = %llu);",
task);
return sql_string ("SELECT uuid FROM configs WHERE id ="
" (SELECT config FROM tasks WHERE id = %llu);",
task);
}
/**
* @brief Return the name of the config of a task.
*
* @param[in] task Task.
*
* @return Name of config of task.
*/
char*
task_config_name (task_t task)
{
if (task_config_in_trash (task))
return sql_string ("SELECT name FROM configs_trash WHERE id ="
" (SELECT config FROM tasks WHERE id = %llu);",
task);
return sql_string ("SELECT name FROM configs WHERE id ="
" (SELECT config FROM tasks WHERE id = %llu);",
task);
}
/**
* @brief Return whether the config of a task is in the trashcan.
*
* @param[in] task Task.
*
* @return 1 if in trashcan, else 0.
*/
int
task_config_in_trash (task_t task)
{
return sql_int ("SELECT config_location = " G_STRINGIFY (LOCATION_TRASH)
" FROM tasks WHERE id = %llu;",
task);
}
/**
* @brief Set the config of a task.
*
* @param[in] task Task.
* @param[in] config Config.
*/
void
set_task_config (task_t task, config_t config)
{
sql ("UPDATE tasks SET config = %llu, modification_time = m_now ()"
" WHERE id = %llu;",
config,
task);
}
/**
* @brief Return the target of a task.
*
* @param[in] task Task.
*
* @return Target of task.
*/
target_t
task_target (task_t task)
{
target_t target = 0;
switch (sql_int64 (&target,
"SELECT target FROM tasks WHERE id = %llu;",
task))
{
case 0:
return target;
break;
case 1: /* Too few rows in result of query. */
default: /* Programming error. */
assert (0);
case -1:
return 0;
break;
}
}
/**
* @brief Set the target of a task.
*
* @param[in] task Task.
* @param[in] target Target.
*/
void
set_task_target (task_t task, target_t target)
{
sql ("UPDATE tasks SET target = %llu, modification_time = m_now ()"
" WHERE id = %llu;",
target,
task);
}
/**
* @brief Set the hosts ordering of a task.
*
* @param[in] task Task.
* @param[in] ordering Hosts ordering.
*/
void
set_task_hosts_ordering (task_t task, const char *ordering)
{
char *quoted_ordering = sql_quote (ordering ?: "");
sql ("UPDATE tasks SET hosts_ordering = '%s', modification_time = m_now ()"
" WHERE id = %llu;", quoted_ordering, task);
g_free (quoted_ordering);
}
/**
* @brief Return whether the target of a task is in the trashcan.
*
* @param[in] task Task.
*
* @return 1 if in trash, else 0.
*/
int
task_target_in_trash (task_t task)
{
return sql_int ("SELECT target_location = " G_STRINGIFY (LOCATION_TRASH)
" FROM tasks WHERE id = %llu;",
task);
}
/**
* @brief Return the scanner of a task.
*
* @param[in] task Task.
*
* @return scanner of task.
*/
scanner_t
task_scanner (task_t task)
{
scanner_t scanner = 0;
switch (sql_int64 (&scanner, "SELECT scanner FROM tasks WHERE id = %llu;",
task))
{
case 0:
return scanner;
break;
case 1: /* Too few rows in result of query. */
default: /* Programming error. */
assert (0);
case -1:
return 0;
break;
}
}
/**
* @brief Set the scanner of a task.
*
* @param[in] task Task.
* @param[in] scanner Scanner.
*/
void
set_task_scanner (task_t task, scanner_t scanner)
{
sql ("UPDATE tasks SET scanner = %llu, modification_time = m_now ()"
" WHERE id = %llu;", scanner, task);
if (scanner_type (scanner) == SCANNER_TYPE_CVE)
sql ("UPDATE tasks SET config = 0 WHERE id = %llu;", task);
}
/**
* @brief Return whether the scanner of a task is in the trashcan.
*
* @param[in] task Task.
*
* @return 1 if in trash, else 0.
*/
int
task_scanner_in_trash (task_t task)
{
return sql_int ("SELECT scanner_location = " G_STRINGIFY (LOCATION_TRASH)
" FROM tasks WHERE id = %llu;", task);
}
/**
* @brief Set the usage_type of a task.
*
* @param[in] task Task.
* @param[in] usage_type New usage type ("scan" or "audit").
*/
void
set_task_usage_type (task_t task, const char *usage_type)
{
const char *actual_usage_type;
if (usage_type && strcasecmp (usage_type, "audit") == 0)
actual_usage_type = "audit";
else
actual_usage_type = "scan";
sql ("UPDATE tasks SET usage_type = '%s', modification_time = m_now ()"
" WHERE id = %llu;", actual_usage_type, task);
}
/**
* @brief Return the run state of a task.
*
* @param[in] task Task.
*
* @return Task run status.
*/
task_status_t
task_run_status (task_t task)
{
return (unsigned int) sql_int ("SELECT run_status FROM tasks WHERE id = %llu;",
task);
}
/**
* @brief Set a report's scheduled flag.
*
* Set flag if task was scheduled, else clear flag.
*
* @param[in] report Report.
*/
void
set_report_scheduled (report_t report)
{
if (authenticate_allow_all == 1)
/* The task was scheduled. */
sql ("UPDATE reports SET flags = 1 WHERE id = %llu;",
report);
else
sql ("UPDATE reports SET flags = 0 WHERE id = %llu;",
report);
}
/**
* @brief Get a report's scheduled flag.
*
* @param[in] report Report.
*
* @return Scheduled flag.
*/
static int
report_scheduled (report_t report)
{
return sql_int ("SELECT flags FROM reports WHERE id = %llu;",
report);
}
/**
* @brief Set the run state of a task.
*
* @param[in] task Task.
* @param[in] status New run status.
*/
static void
set_task_run_status_internal (task_t task, task_status_t status)
{
if ((task == current_scanner_task) && global_current_report)
{
sql ("UPDATE reports SET scan_run_status = %u WHERE id = %llu;",
status,
global_current_report);
if (setting_auto_cache_rebuild_int ())
report_cache_counts (global_current_report, 0, 0, NULL);
}
sql ("UPDATE tasks SET run_status = %u WHERE id = %llu;",
status,
task);
}
/**
* @brief Set the run state of a task.
*
* Logs and generates event.
*
* @param[in] task Task.
* @param[in] status New run status.
*/
void
set_task_run_status (task_t task, task_status_t status)
{
char *uuid;
char *name;
set_task_run_status_internal (task, status);
task_uuid (task, &uuid);
name = task_name (task);
g_log ("event task", G_LOG_LEVEL_MESSAGE,
"Status of task %s (%s) has changed to %s",
name, uuid, run_status_name (status));
free (uuid);
free (name);
event (EVENT_TASK_RUN_STATUS_CHANGED,
(void*) status,
task,
(task == current_scanner_task) ? global_current_report : 0);
}
/**
* @brief Return number of results in a task.
*
* @param[in] task Task.
* @param[in] min_qod Minimum QOD.
*
* @return Result count.
*/
int
task_result_count (task_t task, int min_qod)
{
return sql_int ("SELECT count (*) FROM results"
" WHERE task = %llu"
" AND qod > %i"
" AND severity > " G_STRINGIFY (SEVERITY_ERROR) ";",
task,
min_qod);
}
/**
* @brief Return the running report of a task.
*
* @param[in] task Task.
*
* @return Current report of task if task is active, else (report_t) 0.
*/
report_t
task_running_report (task_t task)
{
task_status_t run_status = task_run_status (task);
if (run_status == TASK_STATUS_REQUESTED
|| run_status == TASK_STATUS_RUNNING
|| run_status == TASK_STATUS_QUEUED)
{
return (unsigned int) sql_int ("SELECT max(id) FROM reports"
" WHERE task = %llu AND end_time IS NULL"
" AND (scan_run_status = %u "
" OR scan_run_status = %u);",
task,
TASK_STATUS_RUNNING,
TASK_STATUS_QUEUED);
}
return (report_t) 0;
}
/**
* @brief Return the current report of a task.
*
* @param[in] iterator Iterator.
*
* @return Current report of task if task is active, else (report_t) 0.
*/
report_t
task_iterator_current_report (iterator_t *iterator)
{
task_t task = get_iterator_resource (iterator);
task_status_t run_status = task_iterator_run_status (iterator);
if (run_status == TASK_STATUS_REQUESTED
|| run_status == TASK_STATUS_RUNNING
|| run_status == TASK_STATUS_QUEUED
|| run_status == TASK_STATUS_DELETE_REQUESTED
|| run_status == TASK_STATUS_DELETE_ULTIMATE_REQUESTED
|| run_status == TASK_STATUS_STOP_REQUESTED
|| run_status == TASK_STATUS_STOPPED
|| run_status == TASK_STATUS_INTERRUPTED)
{
return (unsigned int) sql_int ("SELECT max(id) FROM reports"
" WHERE task = %llu"
" AND (scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u"
" OR scan_run_status = %u);",
task,
TASK_STATUS_REQUESTED,
TASK_STATUS_RUNNING,
TASK_STATUS_QUEUED,
TASK_STATUS_DELETE_REQUESTED,
TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
TASK_STATUS_STOP_REQUESTED,
TASK_STATUS_STOPPED,
TASK_STATUS_INTERRUPTED);
}
return (report_t) 0;
}
/**
* @brief Return the upload progress of a task.
*
* @param[in] task Task.
*
* @return Task upload progress, as a percentage, or -1 on error.
*/
int
task_upload_progress (task_t task)
{
report_t report;
report = task_running_report (task);
if (report)
{
int count;
get_data_t get;
memset (&get, 0, sizeof (get_data_t));
get.filter = g_strdup ("min_qod=0");
count = result_count (&get, report, NULL);
get_data_reset (&get);
return sql_int ("SELECT"
" greatest (least (((%i * 100) / upload_result_count), 100), -1)"
" FROM tasks"
" WHERE id = %llu;",
count,
task);
}
return -1;
}
/**
* @brief Set the start time of a task.
*
* @param[in] task Task.
* @param[in] time New time. Seconds since epoch.
*/
void
set_task_start_time_epoch (task_t task, int time)
{
sql ("UPDATE tasks SET start_time = %i, modification_time = m_now ()"
" WHERE id = %llu;",
time,
task);
}
/**
* @brief Set the start time of a task.
*
* @param[in] task Task.
* @param[in] time New time. UTC ctime format. Freed before return.
*/
void
set_task_start_time_ctime (task_t task, char* time)
{
sql ("UPDATE tasks SET start_time = %i, modification_time = m_now ()"
" WHERE id = %llu;",
parse_utc_ctime (time),
task);
free (time);
}
/**
* @brief Get most recently completed report that precedes a report.
*
* @param[in] task The task.
* @param[out] report Report.
* @param[out] previous Report return, 0 if successfully failed to select report.
*
* @return 0 success, -1 error.
*/
static int
task_report_previous (task_t task, report_t report, report_t *previous)
{
switch (sql_int64 (previous,
"SELECT id FROM reports"
" WHERE task = %llu"
" AND scan_run_status = %u"
" AND date < (SELECT date FROM reports"
" WHERE id = %llu)"
" ORDER BY date DESC LIMIT 1;",
task,
TASK_STATUS_DONE,
report))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*previous = 0;
return 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return -1;
break;
}
return 0;
}
/**
* @brief Get the report from the most recently completed invocation of task.
*
* @param[in] task The task.
* @param[out] report Report return, 0 if successfully failed to select report.
*
* @return 0 success, -1 error.
*/
int
task_last_report (task_t task, report_t *report)
{
switch (sql_int64 (report,
"SELECT id FROM reports WHERE task = %llu"
" AND scan_run_status = %u"
" ORDER BY date DESC LIMIT 1;",
task,
TASK_STATUS_DONE))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*report = 0;
return 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return -1;
break;
}
return 0;
}
/**
* @brief Get the report from the most recently invocation of task.
*
* @param[in] task The task.
* @param[out] report Report return, 0 if successfully failed to select report.
*
* @return 0 success, -1 error.
*/
static int
task_last_report_any_status (task_t task, report_t *report)
{
switch (sql_int64 (report,
"SELECT id FROM reports WHERE task = %llu"
" ORDER BY date DESC LIMIT 1;",
task))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*report = 0;
return 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return -1;
break;
}
return 0;
}
/**
* @brief Get the report from second most recently completed invocation of task.
*
* @param[in] task The task.
* @param[out] report Report return, 0 if successfully failed to select report.
*
* @return 0 success, -1 error.
*/
static int
task_second_last_report (task_t task, report_t *report)
{
switch (sql_int64 (report,
"SELECT id FROM reports WHERE task = %llu"
" AND scan_run_status = %u"
" ORDER BY date DESC LIMIT 1 OFFSET 1;",
task,
TASK_STATUS_DONE))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*report = 0;
return 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return -1;
break;
}
return 0;
}
/**
* @brief Get the report from the most recently stopped invocation of task.
*
* @param[in] task The task.
* @param[out] report Report return, 0 if successfully failed to select report.
*
* @return 0 success, -1 error.
*/
int
task_last_resumable_report (task_t task, report_t *report)
{
switch (sql_int64 (report,
"SELECT id FROM reports WHERE task = %llu"
" AND (scan_run_status = %u"
" OR scan_run_status = %u)"
" ORDER BY date DESC LIMIT 1;",
task,
TASK_STATUS_STOPPED,
TASK_STATUS_INTERRUPTED))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*report = 0;
return 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return -1;
break;
}
return 0;
}
/**
* @brief Get report ID from second most recently completed invocation of task.
*
* @param[in] task The task.
*
* @return The UUID of the report as a newly allocated string.
*/
gchar*
task_second_last_report_id (task_t task)
{
return sql_string ("SELECT uuid FROM reports WHERE task = %llu"
" AND scan_run_status = %u"
" ORDER BY date DESC LIMIT 1 OFFSET 1;",
task,
TASK_STATUS_DONE);
}
/**
* @brief Add an alert to a task.
*
* @param[in] task Task.
* @param[in] alert Alert.
*/
void
add_task_alert (task_t task, alert_t alert)
{
sql ("INSERT INTO task_alerts (task, alert, alert_location)"
" VALUES (%llu, %llu, " G_STRINGIFY (LOCATION_TABLE) ");",
task,
alert);
}
/**
* @brief Set the alerts on a task, removing any previous alerts.
*
* @param[in] task Task.
* @param[in] alerts Alerts.
* @param[out] alert_id_return ID of alert on "failed to find" error.
*
* @return 0 success, -1 error, 1 failed to find alert.
*/
static int
set_task_alerts (task_t task, array_t *alerts, gchar **alert_id_return)
{
alert_t alert = 0;
guint index;
sql_begin_immediate ();
sql ("DELETE FROM task_alerts where task = %llu;", task);
index = alerts->len;
while (index--)
{
gchar *alert_id;
alert_id = (gchar*) g_ptr_array_index (alerts, index);
if (strcmp (alert_id, "0") == 0)
continue;
if (find_alert_with_permission (alert_id, &alert, "get_alerts"))
{
sql_rollback ();
return -1;
}
if (alert == 0)
{
sql_rollback ();
if (alert_id_return) *alert_id_return = alert_id;
return 1;
}
sql ("INSERT INTO task_alerts (task, alert, alert_location)"
" VALUES (%llu, %llu, " G_STRINGIFY (LOCATION_TABLE) ");",
task,
alert);
}
sql_commit ();
return 0;
}
/**
* @brief Set the alterable state of a task.
*
* @param[in] task Task.
* @param[in] alterable Whether task is alterable.
*/
void
set_task_alterable (task_t task, int alterable)
{
sql ("UPDATE tasks SET alterable = %i WHERE id = %llu;",
alterable,
task);
}
/**
* @brief Set observer groups on a task, removing any previous groups.
*
* @param[in] task Task.
* @param[in] groups Groups.
* @param[out] group_id_return ID of group on "failed to find" error.
*
* @return 0 success, -1 error, 1 failed to find group.
*/
int
set_task_groups (task_t task, array_t *groups, gchar **group_id_return)
{
group_t group = 0;
guint index;
sql_begin_immediate ();
sql ("DELETE FROM permissions"
" WHERE resource_type = 'task'"
" AND resource = %llu"
" AND subject_type = 'group'"
" AND name = 'get';",
task);
index = 0;
while (index < groups->len)
{
gchar *group_id;
group_id = (gchar*) g_ptr_array_index (groups, index);
if (strcmp (group_id, "0") == 0)
{
index++;
continue;
}
if (find_group_with_permission (group_id, &group, "modify_group"))
{
sql_rollback ();
return -1;
}
if (group == 0)
{
sql_rollback ();
if (group_id_return) *group_id_return = group_id;
return 1;
}
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource,"
" resource_uuid, resource_location, subject_type, subject,"
" subject_location, creation_time, modification_time)"
" VALUES"
" (make_uuid (),"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" 'get_tasks', '', 'task', %llu,"
" (SELECT uuid FROM tasks WHERE tasks.id = %llu),"
" " G_STRINGIFY (LOCATION_TABLE) ", 'group', %llu,"
" " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
current_credentials.uuid, task, task, group);
index++;
}
sql_commit ();
return 0;
}
/**
* @brief Set the schedule of a task.
*
* @param[in] task Task.
* @param[in] schedule Schedule.
* @param[in] periods Number of schedule periods.
*
* @return 0 success, -1 error.
*/
int
set_task_schedule (task_t task, schedule_t schedule, int periods)
{
sql ("UPDATE tasks"
" SET schedule = %llu,"
" schedule_periods = %i,"
" schedule_next_time = (SELECT next_time_ical (icalendar, timezone)"
" FROM schedules"
" WHERE id = %llu),"
" modification_time = m_now ()"
" WHERE id = %llu;",
schedule, periods, schedule, task);
return 0;
}
/**
* @brief Set the schedule of a task.
*
* @param[in] task_id Task UUID.
* @param[in] schedule Schedule.
* @param[in] periods Number of schedule periods. -1 to use existing value.
*
* @return 0 success, -1 error.
*/
int
set_task_schedule_uuid (const gchar *task_id, schedule_t schedule, int periods)
{
gchar *quoted_task_id, *schedule_periods;
if (periods == -1)
schedule_periods = g_strdup ("");
else
schedule_periods = g_strdup_printf ("schedule_periods = %i,",
periods);
quoted_task_id = sql_quote (task_id);
sql ("UPDATE tasks"
" SET schedule = %llu,"
"%s"
" schedule_next_time = (SELECT next_time_ical (icalendar, timezone)"
" FROM schedules"
" WHERE id = %llu),"
" modification_time = m_now ()"
" WHERE uuid = '%s';",
schedule, schedule_periods, schedule, quoted_task_id);
g_free (quoted_task_id);
g_free (schedule_periods);
return 0;
}
/**
* @brief Set the schedule periods of a task, given a UUID.
*
* The task modification time stays the same.
*
* @param[in] task_id Task UUID.
* @param[in] periods Schedule periods.
*
* @return 0 success, -1 error.
*/
int
set_task_schedule_periods (const gchar *task_id, int periods)
{
gchar *quoted_task_id;
quoted_task_id = sql_quote (task_id);
sql ("UPDATE tasks"
" SET schedule_periods = %i"
" WHERE uuid = '%s';",
periods, quoted_task_id);
g_free (quoted_task_id);
return 0;
}
/**
* @brief Set the schedule periods of a task, given an ID.
*
* The task modification time stays the same.
*
* @param[in] task Task UUID.
* @param[in] periods Schedule periods.
*
* @return 0 success, -1 error.
*/
int
set_task_schedule_periods_id (task_t task, int periods)
{
sql ("UPDATE tasks"
" SET schedule_periods = %i"
" WHERE id = %llu;",
periods, task);
return 0;
}
/**
* @brief Return the schedule of a task.
*
* @param[in] task Task.
*
* @return Schedule.
*/
schedule_t
task_schedule (task_t task)
{
schedule_t schedule = 0;
switch (sql_int64 (&schedule,
"SELECT schedule FROM tasks WHERE id = %llu;",
task))
{
case 0:
return schedule;
break;
case 1: /* Too few rows in result of query. */
default: /* Programming error. */
assert (0);
case -1:
return 0;
break;
}
}
/**
* @brief Return the schedule of a task.
*
* @param[in] task_id ID of task.
*
* @return Schedule.
*/
schedule_t
task_schedule_uuid (const gchar *task_id)
{
schedule_t schedule;
gchar *quoted_task_id;
quoted_task_id = sql_quote (task_id);
schedule = 0;
switch (sql_int64 (&schedule,
"SELECT schedule FROM tasks WHERE uuid = '%s';",
quoted_task_id))
{
case 0:
g_free (quoted_task_id);
return schedule;
break;
case 1: /* Too few rows in result of query. */
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_task_id);
return 0;
break;
}
}
/**
* @brief Get whether the task schedule is in the trash.
*
* @param[in] task Task.
*
* @return 1 if in trash, else 0.
*/
int
task_schedule_in_trash (task_t task)
{
return sql_int ("SELECT schedule_location = " G_STRINGIFY (LOCATION_TRASH)
" FROM tasks"
" WHERE id = %llu;",
task);
}
/**
* @brief Get the number of times the period schedule should run on the task.
*
* @param[in] task Task.
*
* @return Number of times.
*/
int
task_schedule_periods (task_t task)
{
return sql_int ("SELECT schedule_periods FROM tasks WHERE id = %llu;", task);
}
/**
* @brief Set the next time a scheduled task will be due.
*
* @param[in] task_id Task UUID.
*
* @return Task schedule periods.
*/
int
task_schedule_periods_uuid (const gchar *task_id)
{
gchar *quoted_task_id;
int ret;
quoted_task_id = sql_quote (task_id);
ret = sql_int ("SELECT schedule_periods FROM tasks WHERE uuid = '%s';",
quoted_task_id);
g_free (quoted_task_id);
return ret;
}
/**
* @brief Get next time a scheduled task will run, following schedule timezone.
*
* @param[in] task Task.
*
* @return If the task has a schedule, the next time the task will run (0 if it
* has already run), otherwise 0.
*/
int
task_schedule_next_time (task_t task)
{
int next_time;
next_time = sql_int ("SELECT schedule_next_time FROM tasks"
" WHERE id = %llu;",
task);
return next_time;
}
/**
* @brief Get the next time a scheduled task will be due.
*
* @param[in] task_id Task UUID.
*
* @return Next scheduled time.
*/
time_t
task_schedule_next_time_uuid (const gchar *task_id)
{
gchar *quoted_task_id;
time_t ret;
quoted_task_id = sql_quote (task_id);
ret = (time_t) sql_int ("SELECT schedule_next_time FROM tasks"
" WHERE uuid = '%s';",
quoted_task_id);
g_free (quoted_task_id);
return ret;
}
/**
* @brief Set the next time a scheduled task will be due.
*
* @param[in] task Task.
* @param[in] time New next time.
*/
void
set_task_schedule_next_time (task_t task, time_t time)
{
sql ("UPDATE tasks SET schedule_next_time = %i WHERE id = %llu;",
time, task);
}
/**
* @brief Set the next time a scheduled task will be due.
*
* @param[in] task_id Task UUID.
* @param[in] time New next time.
*/
void
set_task_schedule_next_time_uuid (const gchar *task_id, time_t time)
{
gchar *quoted_task_id;
quoted_task_id = sql_quote (task_id);
sql ("UPDATE tasks SET schedule_next_time = %i WHERE uuid = '%s';",
time, quoted_task_id);
g_free (quoted_task_id);
}
/**
* @brief Return the severity score of a task, taking overrides into account.
*
* @param[in] task Task.
* @param[in] overrides Whether to apply overrides.
* @param[in] min_qod Minimum QoD of results to count.
* @param[in] offset Offset of report to get severity from:
* 0 = use last report, 1 = use next to last report
*
* @return Severity score of last report on task as a double if there is one,
* else SEVERITY_MISSING.
*/
static double
task_severity_double (task_t task, int overrides, int min_qod, int offset)
{
report_t report;
if (current_credentials.uuid == NULL
|| task_target (task) == 0 /* Container task. */)
return SEVERITY_MISSING;
sql_int64 (&report,
"SELECT id FROM reports"
" WHERE reports.task = %llu"
" AND reports.scan_run_status = %u"
" ORDER BY reports.date DESC"
" LIMIT 1 OFFSET %d",
task, TASK_STATUS_DONE, offset);
return report_severity (report, overrides, min_qod);
}
/**
* @brief Set the observers of a task.
*
* @param[in] task Task.
* @param[in] observers Observers.
*
* @return 0 success, -1 error, 1 user name validation failed, 2 failed to find
* user.
*/
int
set_task_observers (task_t task, const gchar *observers)
{
gchar **split, **point;
GList *added;
// TODO the tricky bit here is if you have to own the task to set observers.
assert (current_credentials.username);
added = NULL;
split = g_strsplit_set (observers, " ,", 0);
sql_begin_immediate ();
sql ("DELETE FROM permissions"
" WHERE resource_type = 'task' AND resource = %llu"
" AND subject_type = 'user';",
task);
point = split;
while (*point)
{
user_t user;
gchar *name;
name = *point;
g_strstrip (name);
if (strcmp (name, "") == 0)
{
point++;
continue;
}
if ((strcmp (name, current_credentials.username) == 0)
|| g_list_find_custom (added, name, (GCompareFunc) strcmp))
{
point++;
continue;
}
added = g_list_prepend (added, name);
if (user_exists (name) == 0)
{
g_list_free (added);
g_strfreev (split);
sql_rollback ();
return 2;
}
if (find_user_by_name (name, &user))
{
g_list_free (added);
g_strfreev (split);
sql_rollback ();
return -1;
}
if (user == 0)
{
gchar *uuid;
if (validate_username (name))
{
g_list_free (added);
g_strfreev (split);
sql_rollback ();
return 1;
}
uuid = user_uuid_any_method (name);
if (uuid == NULL)
{
g_list_free (added);
g_strfreev (split);
sql_rollback ();
return -1;
}
if (sql_int ("SELECT count(*) FROM users WHERE uuid = '%s';",
uuid)
== 0)
{
gchar *quoted_name;
quoted_name = sql_quote (name);
sql ("INSERT INTO users"
" (uuid, name, creation_time, modification_time)"
" VALUES"
" ('%s', '%s', m_now (), m_now ());",
uuid,
quoted_name);
g_free (quoted_name);
user = sql_last_insert_id ();
}
else
{
/* user_find should have found it. */
assert (0);
g_free (uuid);
g_list_free (added);
g_strfreev (split);
sql_rollback ();
return -1;
}
g_free (uuid);
}
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource,"
" resource_uuid, resource_location, subject_type, subject,"
" subject_location, creation_time, modification_time)"
" VALUES"
" (make_uuid (),"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" 'get_tasks', '', 'task', %llu,"
" (SELECT uuid FROM tasks WHERE tasks.id = %llu),"
" " G_STRINGIFY (LOCATION_TABLE) ", 'user', %llu,"
" " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
current_credentials.uuid, task, task, user);
point++;
}
g_list_free (added);
g_strfreev (split);
sql_commit ();
return 0;
}
/**
* @brief Clear once-off schedules from tasks where the duration has passed.
*
* @param[in] task Task. 0 for all.
*/
void
clear_duration_schedules (task_t task)
{
gchar *task_element;
const gchar *duration_expired_element;
if (task)
task_element = g_strdup_printf (" AND id = %llu", task);
else
task_element = g_strdup ("");
duration_expired_element
= " AND (SELECT first_time + duration FROM schedules"
" WHERE schedules.id = schedule)"
" < m_now ()";
sql ("UPDATE tasks"
" SET schedule = 0,"
" schedule_next_time = 0,"
" modification_time = m_now ()"
" WHERE schedule > 0"
"%s"
" AND NOT (SELECT icalendar LIKE '%%\nBEGIN:VEVENT%%\nRRULE%%'"
" OR icalendar LIKE '%%\nBEGIN:VEVENT%%\nRDATE%%'"
" FROM schedules WHERE schedules.id = schedule)"
" AND (SELECT duration FROM schedules WHERE schedules.id = schedule) > 0"
"%s"
" AND run_status != %i"
" AND run_status != %i;",
task_element,
task ? "" : duration_expired_element,
TASK_STATUS_RUNNING,
TASK_STATUS_REQUESTED);
g_free (task_element);
}
/**
* @brief Update tasks with limited run schedules which have durations.
*
* If a task is given, assume that the task has finished. Otherwise only
* update the task if more time than the duration has passed the start time.
*
* @param[in] task Task. 0 for all.
*/
void
update_duration_schedule_periods (task_t task)
{
gchar *task_element;
const gchar *duration_expired_element;
if (task)
task_element = g_strdup_printf (" AND id = %llu", task);
else
task_element = g_strdup ("");
duration_expired_element
= /* The task has started, so assume that the start time was the last
* most recent start of the period. */
" AND (SELECT next_time_ical (icalendar, timezone, -1)"
" + duration"
" FROM schedules"
" WHERE schedules.id = schedule)"
" < m_now ()";
sql ("UPDATE tasks"
" SET schedule = 0,"
" schedule_next_time = 0,"
" modification_time = m_now ()"
" WHERE schedule > 0"
"%s"
" AND schedule_periods = 1"
" AND (SELECT icalendar LIKE '%%\nBEGIN:VEVENT%%\nRRULE%%'"
" OR icalendar LIKE '%%\nBEGIN:VEVENT%%\nRDATE%%'"
" FROM schedules WHERE schedules.id = schedule)"
" AND (SELECT duration FROM schedules WHERE schedules.id = schedule) > 0"
" AND schedule_next_time = 0" /* Set as flag when starting task. */
"%s"
" AND run_status != %i"
" AND run_status != %i;",
task_element,
task ? "" : duration_expired_element,
TASK_STATUS_RUNNING,
TASK_STATUS_REQUESTED);
g_free (task_element);
}
/**
* @brief Auto delete reports.
*/
void
auto_delete_reports ()
{
iterator_t tasks;
g_debug ("%s", __func__);
sql_begin_immediate ();
/* As in delete_report, this prevents other processes from getting the
* report ID. */
if (sql_int ("SELECT try_exclusive_lock('reports');") == 0)
{
sql_rollback ();
return;
}
init_iterator (&tasks,
"SELECT id, name,"
" (SELECT value FROM task_preferences"
" WHERE name = 'auto_delete_data'"
" AND task = tasks.id)"
" FROM tasks"
" WHERE owner is NOT NULL"
" AND hidden = 0"
" AND EXISTS (SELECT * FROM task_preferences"
" WHERE task = tasks.id"
" AND name = 'auto_delete'"
" AND value = 'keep');");
while (next (&tasks))
{
task_t task;
iterator_t reports;
const char *keep_string;
int keep;
task = iterator_int64 (&tasks, 0);
keep_string = iterator_string (&tasks, 2);
if (keep_string == NULL)
continue;
keep = atoi (keep_string);
if (keep < AUTO_DELETE_KEEP_MIN || keep > AUTO_DELETE_KEEP_MAX)
continue;
g_debug ("%s: %s (%i)", __func__,
iterator_string (&tasks, 1),
keep);
init_iterator (&reports,
"SELECT id FROM reports"
" WHERE task = %llu"
" AND start_time IS NOT NULL"
" AND start_time > 0"
" ORDER BY start_time DESC LIMIT %s OFFSET %i;",
task,
sql_select_limit (-1),
keep);
while (next (&reports))
{
int ret;
report_t report;
report = iterator_int64 (&reports, 0);
assert (report);
g_debug ("%s: delete %llu", __func__, report);
ret = delete_report_internal (report);
if (ret == 2)
{
/* Report is in use. */
g_debug ("%s: %llu is in use", __func__, report);
continue;
}
if (ret)
{
g_warning ("%s: failed to delete %llu (%i)",
__func__, report, ret);
sql_rollback ();
}
}
cleanup_iterator (&reports);
}
cleanup_iterator (&tasks);
sql_commit ();
}
/**
* @brief Get definitions file from a task's config.
*
* @param[in] task Task.
*
* @return Definitions file.
*/
static char *
task_definitions_file (task_t task)
{
assert (task);
return sql_string ("SELECT value FROM config_preferences"
" WHERE name = 'definitions_file' AND config = %llu;",
task_config (task));
}
/**
* @brief Set a task's schedule so that it runs again next scheduling round.
*
* @param task_id UUID of task.
*/
void
reschedule_task (const gchar *task_id)
{
task_t task;
task = 0;
switch (sql_int64 (&task,
"SELECT id FROM tasks WHERE uuid = '%s'"
" AND hidden != 2;",
task_id))
{
case 0:
g_warning ("%s: rescheduling task '%s'", __func__, task_id);
set_task_schedule_next_time (task, time (NULL) - 1);
break;
case 1: /* Too few rows in result of query. */
break;
default: /* Programming error. */
assert (0);
case -1:
break;
}
}
/* Results. */
/**
* @brief Find a result for a set of permissions, given a UUID.
*
* @param[in] uuid UUID of result.
* @param[out] result Result return, 0 if successfully failed to find
* result.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find result), TRUE on error.
*/
gboolean
find_result_with_permission (const char* uuid, result_t* result,
const char *permission)
{
gchar *quoted_uuid = sql_quote (uuid);
if (acl_user_has_access_uuid ("result", quoted_uuid, permission, 0) == 0)
{
g_free (quoted_uuid);
*result = 0;
return FALSE;
}
switch (sql_int64 (result,
"SELECT id FROM results WHERE uuid = '%s';",
quoted_uuid))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*result = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_uuid);
return TRUE;
break;
}
g_free (quoted_uuid);
return FALSE;
}
/**
* @brief Ensure an NVT occurs in the result_nvts table.
*
* @param[in] nvt NVT OID.
*/
static void
result_nvt_notice (const gchar *nvt)
{
if (nvt == NULL)
return;
sql ("INSERT INTO result_nvts (nvt) VALUES ('%s') ON CONFLICT DO NOTHING;",
nvt);
}
/**
* @brief Make an OSP result.
*
* @param[in] task The task associated with the result.
* @param[in] host Target host of result.
* @param[in] hostname Hostname of the result.
* @param[in] nvt The uuid of oval definition that produced the
* result, a title for the result otherwise.
* @param[in] type Type of result. "Alarm", etc.
* @param[in] description Description of the result.
* @param[in] port Result port.
* @param[in] severity Result severity.
* @param[in] qod Quality of detection.
* @param[in] path Result path, e.g. file location of a product.
*
* @return A result descriptor for the new result, 0 if error.
*/
result_t
make_osp_result (task_t task, const char *host, const char *hostname,
const char *nvt, const char *type, const char *description,
const char *port, const char *severity, int qod,
const char *path)
{
char *nvt_revision = NULL, *quoted_desc, *quoted_nvt, *result_severity;
char *quoted_port, *quoted_hostname, *quoted_path;
assert (task);
assert (type);
quoted_desc = sql_quote (description ?: "");
quoted_nvt = sql_quote (nvt ?: "");
quoted_port = sql_quote (port ?: "");
quoted_hostname = sql_quote (hostname ? hostname : "");
quoted_path = sql_quote (path ? path : "");
if (nvt)
{
if (g_str_has_prefix (nvt, "1.3.6.1.4.1.25623."))
nvt_revision = sql_string ("SELECT iso_time (modification_time)"
" FROM nvts WHERE oid='%s'",
quoted_nvt);
else if (g_str_has_prefix (nvt, "oval:"))
nvt_revision = ovaldef_version (nvt);
else if (g_str_has_prefix (nvt, "CVE-"))
nvt_revision = sql_string ("SELECT iso_time (modification_time)"
" FROM scap.cves WHERE uuid='%s'",
quoted_nvt);
}
if (!severity || !strcmp (severity, ""))
{
if (!strcmp (type, severity_to_type (SEVERITY_ERROR)))
result_severity = g_strdup (G_STRINGIFY (SEVERITY_ERROR));
else
{
if (nvt && g_str_has_prefix (nvt, "CVE-"))
{
result_severity = cve_cvss_base (nvt);
if (result_severity == NULL || strcmp (result_severity, "") == 0)
{
g_free (result_severity);
result_severity
= g_strdup_printf ("%0.1f",
setting_default_severity_dbl ());
g_debug ("%s: OSP CVE result without severity for '%s'",
__func__, nvt);
}
}
else
{
/*
result_severity
= g_strdup_printf ("%0.1f",
setting_default_severity_dbl ());
*/
g_warning ("%s: Non-CVE OSP result without severity for test %s",
__func__, nvt ? nvt : "(unknown)");
return 0;
}
}
}
else
result_severity = sql_quote (severity);
result_nvt_notice (quoted_nvt);
sql ("INSERT into results"
" (owner, date, task, host, hostname, port, nvt,"
" nvt_version, severity, type, qod, qod_type, description,"
" path, uuid, result_nvt)"
" VALUES (NULL, m_now(), %llu, '%s', '%s', '%s', '%s',"
" '%s', '%s', '%s', %d, '', '%s',"
" '%s', make_uuid (),"
" (SELECT id FROM result_nvts WHERE nvt = '%s'));",
task, host ?: "", quoted_hostname, quoted_port, quoted_nvt,
nvt_revision ?: "", result_severity ?: "0",
type, qod, quoted_desc, quoted_path, quoted_nvt);
g_free (result_severity);
g_free (nvt_revision);
g_free (quoted_desc);
g_free (quoted_nvt);
g_free (quoted_port);
g_free (quoted_hostname);
g_free (quoted_path);
return sql_last_insert_id ();
}
/**
* @brief Get QoD percentage for a qod_type string.
*
* @param[in] qod_type The QoD type string.
*
* @return A QoD percentage value, QOD_DEFAULT if string is NULL or unknown.
*/
int
qod_from_type (const char *qod_type)
{
if (qod_type == NULL)
return QOD_DEFAULT;
else if (strcmp (qod_type, "exploit") == 0)
return 100;
else if (strcmp (qod_type, "remote_vul") == 0)
return 99;
else if (strcmp (qod_type, "remote_app") == 0)
return 98;
else if (strcmp (qod_type, "package") == 0)
return 97;
else if (strcmp (qod_type, "registry") == 0)
return 97;
else if (strcmp (qod_type, "remote_active") == 0)
return 95;
else if (strcmp (qod_type, "remote_banner") == 0)
return 80;
else if (strcmp (qod_type, "executable_version") == 0)
return 80;
else if (strcmp (qod_type, "remote_analysis") == 0)
return 70;
else if (strcmp (qod_type, "remote_probe") == 0)
return 50;
else if (strcmp (qod_type, "remote_banner_unreliable") == 0)
return 30;
else if (strcmp (qod_type, "executable_version_unreliable") == 0)
return 30;
else if (strcmp (qod_type, "general_note") == 0)
return 1;
else
return QOD_DEFAULT;
}
/**
* @brief Identify a host, given an identifier.
*
* Find a host which has an identifier of the same name and value, and
* which has no identifiers of the same name and a different value.
*
* @param[in] host_name Host name.
* @param[in] identifier_name Host identifier name.
* @param[in] identifier_value Value of host identifier.
* @param[in] source_type Source of identification: result.
* @param[in] source Source identifier.
*
* @return Host if exists, else 0.
*/
static host_t
host_identify (const char *host_name, const char *identifier_name,
const char *identifier_value, const char *source_type,
const char *source)
{
host_t host = 0;
gchar *quoted_host_name, *quoted_identifier_name, *quoted_identifier_value;
quoted_host_name = sql_quote (host_name);
quoted_identifier_name = sql_quote (identifier_name);
quoted_identifier_value = sql_quote (identifier_value);
switch (sql_int64 (&host,
"SELECT hosts.id FROM hosts, host_identifiers"
" WHERE hosts.name = '%s'"
" AND hosts.owner = (SELECT id FROM users"
" WHERE uuid = '%s')"
" AND host = hosts.id"
" AND host_identifiers.owner = (SELECT id FROM users"
" WHERE uuid = '%s')"
" AND host_identifiers.name = '%s'"
" AND value = '%s';",
quoted_host_name,
current_credentials.uuid,
current_credentials.uuid,
quoted_identifier_name,
quoted_identifier_value))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
host = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
host = 0;
break;
}
if (host == 0)
switch (sql_int64 (&host,
"SELECT id FROM hosts"
" WHERE name = '%s'"
" AND owner = (SELECT id FROM users"
" WHERE uuid = '%s')"
" AND NOT EXISTS (SELECT * FROM host_identifiers"
" WHERE host = hosts.id"
" AND owner = (SELECT id FROM users"
" WHERE uuid = '%s')"
" AND name = '%s');",
quoted_host_name,
current_credentials.uuid,
current_credentials.uuid,
quoted_identifier_name))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
host = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
host = 0;
break;
}
g_free (quoted_host_name);
g_free (quoted_identifier_name);
g_free (quoted_identifier_value);
return host;
}
/**
* @brief Notice a host.
*
* When a host is detected during a scan, this makes the decision about which
* asset host is used for the host, as described in \ref asset_rules. This
* decision is revised at the end of the scan by \ref hosts_set_identifiers if
* there are any identifiers for the host.
*
* @param[in] host_name Name of host.
* @param[in] identifier_type Type of host identifier.
* @param[in] identifier_value Value of host identifier.
* @param[in] source_type Type of source identifier
* @param[in] source_id Source identifier.
* @param[in] check_add_to_assets Whether to check the 'Add to Assets'
* task preference.
* @param[in] check_for_existing_identifier Whether to check for an existing
* identifier like this one. Used
* for slaves, which call this
* repeatedly.
*
* @return Host if existed, else 0.
*/
host_t
host_notice (const char *host_name, const char *identifier_type,
const char *identifier_value, const char *source_type,
const char *source_id, int check_add_to_assets,
int check_for_existing_identifier)
{
host_t host;
gchar *quoted_identifier_value, *quoted_identifier_type, *quoted_source_type;
gchar *quoted_source_id;
/* Only add to assets if "Add to Assets" is set on the task. */
if (check_add_to_assets
&& g_str_has_prefix (source_type, "Report")
&& sql_int ("SELECT value = 'no' FROM task_preferences"
" WHERE task = (SELECT task FROM reports WHERE uuid = '%s')"
" AND name = 'in_assets';",
source_id))
return 0;
host = host_identify (host_name, identifier_type, identifier_value,
source_type, source_id);
if (host == 0)
{
gchar *quoted_host_name;
quoted_host_name = sql_quote (host_name);
sql ("INSERT into hosts"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" (make_uuid (), (SELECT id FROM users WHERE uuid = '%s'), '%s', '',"
" m_now (), m_now ());",
current_credentials.uuid,
quoted_host_name);
g_free (quoted_host_name);
host = sql_last_insert_id ();
}
quoted_identifier_value = sql_quote (identifier_value);
quoted_source_id = sql_quote (source_id);
quoted_source_type = sql_quote (source_type);
quoted_identifier_type = sql_quote (identifier_type);
if (check_for_existing_identifier
&& sql_int ("SELECT EXISTS (SELECT * FROM host_identifiers"
" WHERE host = %llu"
" AND owner = (SELECT id FROM users WHERE uuid = '%s')"
" AND name = '%s'"
" AND value = '%s'"
" AND source_type = '%s'"
" AND source_id = '%s');",
host,
current_credentials.uuid,
quoted_identifier_type,
quoted_identifier_value,
quoted_source_type,
quoted_source_id))
return 0;
sql ("INSERT into host_identifiers"
" (uuid, host, owner, name, comment, value, source_type, source_id,"
" source_data, creation_time, modification_time)"
" VALUES"
" (make_uuid (), %llu, (SELECT id FROM users WHERE uuid = '%s'), '%s',"
" '', '%s', '%s', '%s', '', m_now (), m_now ());",
host,
current_credentials.uuid,
quoted_identifier_type,
quoted_identifier_value,
quoted_source_type,
quoted_source_id);
sql ("UPDATE hosts SET modification_time = (SELECT modification_time"
" FROM host_identifiers"
" WHERE id = %llu)"
" WHERE id = %llu;",
sql_last_insert_id (),
host);
g_free (quoted_identifier_type);
g_free (quoted_identifier_value);
g_free (quoted_source_id);
g_free (quoted_source_type);
return host;
}
/**
* @brief Get a severity string from an nvt and result type.
*
* @param[in] nvt_id NVT oid.
* @param[in] type Result type.
*
* @return A severity string, NULL if unknown type or no nvt id for Alarm type.
*/
static char *
nvt_severity (const char *nvt_id, const char *type)
{
char *severity = NULL;
if (strcasecmp (type, "Alarm") == 0 && nvt_id)
severity = sql_string ("SELECT coalesce(cvss_base, '0.0')"
" FROM nvts WHERE uuid = '%s';", nvt_id);
else if (strcasecmp (type, "Alarm") == 0)
g_warning ("%s result type requires an NVT", type);
else if (strcasecmp (type, "Log Message") == 0)
severity = g_strdup (G_STRINGIFY (SEVERITY_LOG));
else if (strcasecmp (type, "Error Message") == 0)
severity = g_strdup (G_STRINGIFY (SEVERITY_ERROR));
else
g_warning ("Invalid result nvt type %s", type);
return severity;
}
/**
* @brief Make a result.
*
* @param[in] task The task associated with the result.
* @param[in] host Host IP address.
* @param[in] hostname Hostname.
* @param[in] port The port the result refers to.
* @param[in] nvt The OID of the NVT that produced the result.
* @param[in] type Type of result: "Alarm", "Error Message" or
* "Log Message".
* @param[in] description Description of the result.
* @param[in] path Result path, e.g. file location of a product.
*
* @return A result descriptor for the new result, 0 if error.
*/
result_t
make_result (task_t task, const char* host, const char *hostname,
const char* port, const char* nvt,
const char* type, const char* description,
const char* path)
{
result_t result;
gchar *nvt_revision, *severity, *qod, *qod_type;
gchar *quoted_nvt, *quoted_hostname, *quoted_descr, *quoted_path;
nvt_t nvt_id = 0;
if (nvt && strcmp (nvt, "") && (find_nvt (nvt, &nvt_id) || nvt_id <= 0))
{
g_warning ("NVT '%s' not found. Result not created", nvt);
return 0;
}
severity = nvt_severity (nvt, type);
if (!severity)
{
g_warning ("NVT '%s' has no severity. Result not created.", nvt);
return 0;
}
quoted_nvt = NULL;
if (nvt && strcmp (nvt, ""))
{
quoted_nvt = sql_quote (nvt);
qod = g_strdup_printf ("(SELECT qod FROM nvts WHERE id = %llu)",
nvt_id);
qod_type = g_strdup_printf ("(SELECT qod_type FROM nvts WHERE id = %llu)",
nvt_id);
if (g_str_has_prefix (nvt, "1.3.6.1.4.1.25623."))
nvt_revision = sql_string ("SELECT iso_time (modification_time)"
" FROM nvts WHERE oid='%s'",
quoted_nvt);
else if (g_str_has_prefix (nvt, "oval:"))
nvt_revision = ovaldef_version (nvt);
else if (g_str_has_prefix (nvt, "CVE-"))
nvt_revision = sql_string ("SELECT iso_time (modification_time)"
" FROM scap.cves WHERE uuid='%s'",
quoted_nvt);
else
nvt_revision = strdup ("");
}
else
{
qod = g_strdup (G_STRINGIFY (QOD_DEFAULT));
qod_type = g_strdup ("''");
nvt_revision = g_strdup ("");
}
if (!strcmp (severity, ""))
{
g_free (severity);
severity = g_strdup ("0.0");
}
quoted_hostname = sql_quote (hostname ? hostname : "");
quoted_descr = sql_quote (description ?: "");
quoted_path = sql_quote (path ? path : "");
result_nvt_notice (nvt);
sql ("INSERT into results"
" (owner, date, task, host, hostname, port,"
" nvt, nvt_version, severity, type,"
" description, uuid, qod, qod_type, path, result_nvt)"
" VALUES"
" (NULL, m_now (), %llu, '%s', '%s', '%s',"
" '%s', '%s', '%s', '%s',"
" '%s', make_uuid (), %s, %s, '%s',"
" (SELECT id FROM result_nvts WHERE nvt = '%s'));",
task, host ?: "", quoted_hostname, port ?: "",
quoted_nvt ?: "", nvt_revision, severity, type,
quoted_descr, qod, qod_type, quoted_path, quoted_nvt ? quoted_nvt : "");
g_free (quoted_nvt);
g_free (quoted_hostname);
g_free (quoted_descr);
g_free (qod);
g_free (qod_type);
g_free (nvt_revision);
g_free (severity);
g_free (quoted_path);
result = sql_last_insert_id ();
return result;
}
/**
* @brief Make a CVE result.
*
* @param[in] task The task associated with the result.
* @param[in] host Host.
* @param[in] nvt The OID of the NVT that produced the result.
* @param[in] cvss CVSS base.
* @param[in] description Description of the result.
*
* @return A result descriptor for the new result, 0 if error.
*/
result_t
make_cve_result (task_t task, const char* host, const char *nvt, double cvss,
const char* description)
{
gchar *quoted_descr;
quoted_descr = sql_quote (description ?: "");
result_nvt_notice (nvt);
sql ("INSERT into results"
" (owner, date, task, host, port, nvt, nvt_version, severity, type,"
" description, uuid, qod, qod_type, path, result_nvt)"
" VALUES"
" (NULL, m_now (), %llu, '%s', '', '%s',"
" (SELECT iso_time (modification_time)"
" FROM scap.cves WHERE uuid='%s'),"
" '%1.1f', '%s', '%s', make_uuid (), %i, '', '',"
" (SELECT id FROM result_nvts WHERE nvt = '%s'));",
task, host ?: "", nvt, nvt, cvss, severity_to_type (cvss),
quoted_descr, QOD_DEFAULT, nvt);
g_free (quoted_descr);
return sql_last_insert_id ();
}
/**
* @brief Return the UUID of a result.
*
* @param[in] result Result.
* @param[out] id Pointer to a newly allocated string.
*
* @return 0.
*/
int
result_uuid (result_t result, char ** id)
{
*id = sql_string ("SELECT uuid FROM results WHERE id = %llu;",
result);
return 0;
}
/**
* @brief Get product detection results corresponding to a given vulnerability
* detection result.
*
* @param[in] result Vulnerability detection result.
* @param[in] report Report of result.
* @param[in] host Host of result.
* @param[in] port Port of result.
* @param[in] path Path of result.
* @param[out] oid Detection script OID.
* @param[out] ref Detection result UUID.
* @param[out] product Product name.
* @param[out] location Product location.
* @param[out] name Detection script name.
*
* @return -1 on error, 0 on success.
*/
int
result_detection_reference (result_t result, report_t report,
const char *host,
const char *port,
const char *path,
char **oid, char **ref, char **product,
char **location, char **name)
{
gchar *quoted_location, *quoted_host;
if ((ref == NULL) || (product == NULL) || (location == NULL)
|| (name == NULL))
return -1;
if ((report == 0) || (host == NULL) || (oid == NULL))
return -1;
quoted_location = NULL;
*oid = *ref = *product = *location = *name = NULL;
quoted_host = sql_quote (host);
if (path && strcmp (path, ""))
{
*location = strdup (path);
}
else if (port && strcmp (port, "")
&& !(g_str_has_prefix (port, "general/")))
{
*location = strdup (port);
}
else
{
*location = sql_string ("SELECT value"
" FROM report_host_details"
" WHERE report_host = (SELECT id"
" FROM report_hosts"
" WHERE report = %llu"
" AND host = '%s')"
" AND name = 'detected_at'"
" AND source_name = (SELECT nvt"
" FROM results"
" WHERE id = %llu);",
report, quoted_host, result);
}
if (*location == NULL)
{
goto detect_cleanup;
}
quoted_location = sql_quote (*location);
*oid
= sql_string ("SELECT value"
" FROM report_host_details"
" WHERE report_host = (SELECT id"
" FROM report_hosts"
" WHERE report = %llu"
" AND host = '%s')"
" AND name = 'detected_by@%s'"
" AND source_name = (SELECT nvt"
" FROM results"
" WHERE id = %llu)"
" LIMIT 1",
report, quoted_host, quoted_location, result);
if (*oid == NULL)
{
*oid
= sql_string ("SELECT value"
" FROM report_host_details"
" WHERE report_host = (SELECT id"
" FROM report_hosts"
" WHERE report = %llu"
" AND host = '%s')"
" AND name = 'detected_by'"
" AND source_name = (SELECT nvt"
" FROM results"
" WHERE id = %llu)"
" LIMIT 1",
report, quoted_host, result);
}
if (*oid == NULL)
{
goto detect_cleanup;
}
*product = sql_string ("SELECT name"
" FROM report_host_details"
" WHERE report_host = (SELECT id"
" FROM report_hosts"
" WHERE report = %llu"
" AND host = '%s')"
" AND source_name = '%s'"
" AND name != 'detected_at'"
" AND value = '%s';",
report, quoted_host, *oid, quoted_location);
if (*product == NULL)
goto detect_cleanup;
if (g_str_has_prefix (*oid, "CVE-"))
*name = g_strdup (*oid);
else
*name = sql_string ("SELECT name FROM nvts WHERE oid = '%s';", *oid);
if (*name == NULL)
goto detect_cleanup;
/* Get the result produced by the detection NVT when it detected the
* product. The result port or description must include the product
* location in order for this to work. */
*ref = sql_string ("SELECT uuid"
" FROM results"
" WHERE report = %llu"
" AND host = '%s'"
" AND nvt = '%s'"
" AND (description LIKE '%%%s%%'"
" OR port LIKE '%%%s%%');",
report, quoted_host, *oid, quoted_location,
quoted_location);
if (*ref == NULL)
goto detect_cleanup;
g_free (quoted_host);
g_free (quoted_location);
return 0;
detect_cleanup:
g_free (quoted_host);
g_free (quoted_location);
return -1;
}
/* Prognostics. */
/**
* @brief Initialize an iterator of locations of an App for a report's host.
*
* @param[in] iterator Iterator.
* @param[in] report_host Report host.
* @param[in] app CPE.
*/
void
init_app_locations_iterator (iterator_t *iterator,
report_host_t report_host,
const gchar *app)
{
gchar *quoted_app;
assert (app);
quoted_app = sql_quote (app);
init_iterator (iterator,
"SELECT string_agg(DISTINCT value, ', ')"
" FROM report_host_details"
" WHERE report_host = %llu"
" AND name = '%s'"
" AND source_type = 'nvt'"
" AND source_name"
" IN (SELECT source_name FROM report_host_details"
" WHERE report_host = %llu"
" AND source_type = 'nvt'"
" AND name = 'App'"
" AND value = '%s');",
report_host,
quoted_app,
report_host,
quoted_app);
g_free (quoted_app);
}
/**
* @brief Get a location from an app locations iterator.
*
* @param[in] iterator Iterator.
*
* @return The location.
*/
const char *
app_locations_iterator_location (iterator_t *iterator)
{
return iterator_string (iterator, 0);
}
/**
* @brief Initialise a report host prognosis iterator.
*
* @param[in] iterator Iterator.
* @param[in] report_host Report host whose prognosis the iterator loops over.
* All report_hosts if NULL.
*/
void
init_host_prognosis_iterator (iterator_t* iterator, report_host_t report_host)
{
init_iterator (iterator,
"SELECT cves.name AS vulnerability,"
" max(cves.severity) AS severity,"
" max(cves.description) AS description,"
" cpes.name AS location,"
" (SELECT host FROM report_hosts"
" WHERE id = %llu) AS host"
" FROM scap.cves, scap.cpes, scap.affected_products,"
" report_host_details"
" WHERE report_host_details.report_host = %llu"
" AND cpes.name = report_host_details.value"
" AND report_host_details.name = 'App'"
" AND cpes.id=affected_products.cpe"
" AND cves.id=affected_products.cve"
" GROUP BY cves.id, vulnerability, location, host"
" ORDER BY cves.id ASC"
" LIMIT %s OFFSET 0;",
report_host,
report_host,
sql_select_limit (-1));
}
DEF_ACCESS (prognosis_iterator_cve, 0);
/**
* @brief Get the CVSS from a result iterator as a double.
*
* @param[in] iterator Iterator.
*
* @return CVSS.
*/
double
prognosis_iterator_cvss_double (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_double (iterator, 1);
}
DEF_ACCESS (prognosis_iterator_description, 2);
DEF_ACCESS (prognosis_iterator_cpe, 3);
/* Reports. */
/**
* @brief Whether to ignore the Max Rows Per Page settings.
*/
int ignore_max_rows_per_page = 0;
/**
* @brief Create a new GHashTable for containing resource rowids.
*
* @return The newly allocated GHashTable
*/
static GHashTable *
new_resources_hashtable ()
{
return g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, NULL);
}
/**
* @brief Add reports affected by an override to an existing GHashtable.
* This is used to add more reports to the hashtable from reports_for_override.
*
* @param[in] reports_table The GHashtable to contain the report rowids.
* @param[in] override The override that selected reports must be affected by.
*/
static void
reports_add_for_override (GHashTable *reports_table,
override_t override)
{
result_t result;
task_t task;
gchar *nvt_id;
iterator_t reports;
if (override == 0)
return;
sql_int64 (&result,
"SELECT result FROM overrides WHERE id = %llu",
override);
sql_int64 (&task,
"SELECT task FROM overrides WHERE id = %llu",
override);
nvt_id = sql_string ("SELECT nvt FROM overrides WHERE id = %llu",
override);
if (result)
{
report_t *report = g_malloc0 (sizeof (report_t));
sql_int64 (report,
"SELECT report FROM results"
" WHERE id = %llu AND nvt = '%s'",
result, nvt_id);
if (*report)
g_hash_table_add (reports_table, report);
else
g_free (report);
return;
}
else if (task)
{
init_iterator (&reports,
"SELECT DISTINCT report FROM results"
" WHERE task = %llu AND nvt = '%s'",
task, nvt_id);
}
else
{
init_iterator (&reports,
"SELECT DISTINCT report FROM results"
" WHERE nvt = '%s'",
nvt_id);
}
while (next (&reports))
{
report_t *report = g_malloc0 (sizeof (report_t));
*report = iterator_int64 (&reports, 0);
if (g_hash_table_contains (reports_table, report) == 0)
g_hash_table_add (reports_table, report);
else
g_free (report);
}
cleanup_iterator (&reports);
}
/**
* @brief Get reports affected by an override in a GHashTable.
*
* @param[in] override The override that selected reports must be affected by.
*
* @return A GHashtable containing the affected report rowids.
*/
static GHashTable *
reports_for_override (override_t override)
{
GHashTable *reports_table;
reports_table = new_resources_hashtable ();
reports_add_for_override (reports_table, override);
return reports_table;
}
/**
* @brief Add all reports to an existing GHashtable.
*
* @param[in] reports_table The GHashtable to contain the report rowids.
*/
static void
reports_add_all (GHashTable *reports_table)
{
iterator_t reports;
init_iterator (&reports,
"SELECT id FROM reports");
while (next (&reports))
{
report_t *report = g_malloc0 (sizeof (report_t));
*report = iterator_int64 (&reports, 0);
if (g_hash_table_contains (reports_table, report) == 0)
g_hash_table_add (reports_table, report);
else
g_free (report);
}
cleanup_iterator (&reports);
}
/**
* @brief Get all reports in a GHashTable.
*
* @return A GHashtable containing the report rowids.
*/
static GHashTable *
reports_hashtable ()
{
GHashTable *reports_table;
reports_table = new_resources_hashtable ();
reports_add_all (reports_table);
return reports_table;
}
/**
* @brief Clear the report count cache for all reports of a user.
*
* @param[in] uuid UUID of user.
*/
static void
reports_clear_count_cache (const gchar *uuid)
{
gchar *quoted_uuid;
quoted_uuid = sql_quote (uuid);
sql ("DELETE FROM report_counts"
" WHERE report_counts.user = (SELECT id FROM users"
" WHERE uuid = '%s');",
quoted_uuid);
g_free (quoted_uuid);
}
/**
* @brief Clear all report counts for all dynamic severity users.
*/
void
reports_clear_count_cache_dynamic ()
{
sql ("DELETE FROM report_counts"
" WHERE report_counts.user IN (SELECT owner FROM settings"
" WHERE name = 'Dynamic Severity'"
" AND value = '1');");
}
/**
* @brief Rebuild the report count cache for all reports and users.
*
* @param[in] clear Whether to clear the cache before rebuilding.
* @param[out] changes_out The number of processed user/report combinations.
*/
static void
reports_build_count_cache (int clear, int* changes_out)
{
int changes;
iterator_t reports;
changes = 0;
/* Clear cache of trashcan reports, we won't count them. */
sql ("DELETE FROM report_counts"
" WHERE (SELECT hidden = 2 FROM tasks"
" WHERE tasks.id = (SELECT task FROM reports"
" WHERE reports.id = report_counts.report));");
init_iterator (&reports,
"SELECT id FROM reports"
" WHERE (SELECT hidden = 0 FROM tasks"
" WHERE tasks.id = task);");
while (next (&reports))
{
report_t report = iterator_int64 (&reports, 0);
report_cache_counts (report, clear, clear, NULL);
changes ++;
}
cleanup_iterator (&reports);
if (changes_out)
*changes_out = changes;
}
/**
* @brief Initializes an iterator for updating the report cache
*
* @param[in] iterator Iterator.
* @param[in] report Report to select.
* @param[in] min_qod_limit Limit for min_qod.
* @param[in] add_defaults Whether to add default values.
* @param[in] users_where Optional SQL clause to limit users.
*/
void
init_report_counts_build_iterator (iterator_t *iterator, report_t report,
int min_qod_limit, int add_defaults,
const char *users_where)
{
gchar *report_id, *users_string;
report_id = sql_string ("SELECT uuid FROM reports WHERE id = %llu;", report);
users_string = acl_users_with_access_sql ("report", report_id, users_where);
if (users_string == NULL)
{
init_iterator (iterator, "SELECT NULL WHERE NOT t();");
g_free (report_id);
return;
}
if (add_defaults && MIN_QOD_DEFAULT <= min_qod_limit)
{
init_iterator (iterator,
"SELECT * FROM"
" (WITH users_with_access (\"user\") AS %s"
" SELECT DISTINCT min_qod, override, \"user\""
" FROM report_counts"
" WHERE report = %llu"
" AND \"user\" IN (SELECT \"user\""
" FROM users_with_access)"
" AND min_qod <= %d"
" UNION SELECT 0 as min_qod, 0, \"user\""
" FROM users_with_access"
" UNION SELECT 0 as min_qod, 1, \"user\""
" FROM users_with_access"
" UNION SELECT %d as min_qod, 0, \"user\""
" FROM users_with_access"
" UNION SELECT %d as min_qod, 1, \"user\""
" FROM users_with_access) AS inner_query"
" ORDER BY \"user\"",
users_string,
report,
min_qod_limit,
MIN_QOD_DEFAULT,
MIN_QOD_DEFAULT);
}
else if (add_defaults)
{
init_iterator (iterator,
"SELECT * FROM"
" (WITH users_with_access (\"user\") AS %s"
" SELECT DISTINCT min_qod, override, \"user\""
" FROM report_counts"
" WHERE report = %llu"
" AND min_qod <= %d"
" AND \"user\" IN (SELECT \"user\""
" FROM users_with_access)"
" UNION SELECT 0 as min_qod, 0, \"user\""
" FROM users_with_access"
" UNION SELECT 0 as min_qod, 1, \"user\""
" FROM users_with_access) AS inner_query"
" ORDER BY \"user\"",
users_string,
report,
min_qod_limit);
}
else
{
init_iterator (iterator,
"WITH users_with_access (\"user\") AS %s"
" SELECT DISTINCT min_qod, override, \"user\""
" FROM report_counts"
" WHERE report = %llu"
" AND min_qod <= %d"
" AND \"user\" IN (SELECT \"user\""
" FROM users_with_access)"
" ORDER BY \"user\"",
users_string,
report,
min_qod_limit);
}
g_free (users_string);
g_free (report_id);
}
/**
* @brief Get the min_qod from a report_counts build iterator.
*
* @param[in] iterator Iterator.
*
* @return The min_qod.
*/
static int
report_counts_build_iterator_min_qod (iterator_t *iterator)
{
return iterator_int (iterator, 0);
}
/**
* @brief Get the override flag from a report_counts build iterator.
*
* @param[in] iterator Iterator.
*
* @return Whether the report counts are using overrides.
*/
static int
report_counts_build_iterator_override (iterator_t *iterator)
{
return iterator_int (iterator, 1);
}
/**
* @brief Get the user from a report_counts build iterator.
*
* @param[in] iterator Iterator.
*
* @return The min_qod.
*/
static user_t
report_counts_build_iterator_user (iterator_t *iterator)
{
return iterator_int64 (iterator, 2);
}
/**
* @brief Cache report counts and clear existing caches if requested.
*
* @param[in] report Report to cache counts of.
* @param[in] clear_original Whether to clear existing cache for
* original severity.
* @param[in] clear_overridden Whether to clear existing cache for
* overridden severity.
* @param[in] users_where Optional SQL clause to limit users.
*/
static void
report_cache_counts (report_t report, int clear_original, int clear_overridden,
const char* users_where)
{
iterator_t cache_iterator;
int holes, infos, logs, warnings, false_positives;
double severity;
get_data_t *get = NULL;
gchar *old_user_id;
old_user_id = current_credentials.uuid;
init_report_counts_build_iterator (&cache_iterator, report, INT_MAX, 1,
users_where);
while (next (&cache_iterator))
{
int override = report_counts_build_iterator_override (&cache_iterator);
int min_qod = report_counts_build_iterator_min_qod (&cache_iterator);
user_t user = report_counts_build_iterator_user (&cache_iterator);
current_credentials.uuid
= sql_string ("SELECT uuid FROM users WHERE id = %llu",
user);
manage_session_init (current_credentials.uuid);
get = report_results_get_data (1, -1, override, min_qod);
if ((clear_original && override == 0) || (clear_overridden && override))
{
sql ("DELETE FROM report_counts"
" WHERE report = %llu"
" AND \"user\" = %llu"
" AND override = %d"
" AND min_qod = %d",
report, user, override, min_qod);
}
report_counts_id (report, &holes, &infos, &logs, &warnings,
&false_positives, &severity, get, NULL);
get_data_reset (get);
g_free (get);
g_free (current_credentials.uuid);
}
cleanup_iterator (&cache_iterator);
current_credentials.uuid = old_user_id;
manage_session_init (current_credentials.uuid);
}
/**
* @brief Clear report counts .
*
* @param[in] report Report.
* @param[in] clear_original Whether to clear existing cache for
* original severity.
* @param[in] clear_overridden Whether to clear existing cache for
* overridden severity.
* @param[in] users_where Optional SQL clause to limit users.
*/
static void
report_clear_count_cache (report_t report,
int clear_original, int clear_overridden,
const char* users_where)
{
gchar *extra_where = NULL;
if (users_where)
{
extra_where
= g_strdup_printf (" AND \"user\" IN (SELECT id FROM users WHERE %s)",
users_where);
}
if (clear_original && clear_overridden)
{
sql ("DELETE FROM report_counts"
" WHERE report = %llu"
"%s",
report,
extra_where ? extra_where : "");
}
else if (clear_original || clear_overridden)
{
int override = clear_overridden ? 1 : 0;
sql ("DELETE FROM report_counts"
" WHERE report = %llu"
" AND override = %d"
"%s",
report,
override,
extra_where ? extra_where : "");
}
}
/**
* @brief Make a report.
*
* @param[in] task The task associated with the report.
* @param[in] uuid The UUID of the report.
* @param[in] status The run status of the scan associated with the report.
*
* @return A report descriptor for the new report.
*/
report_t
make_report (task_t task, const char* uuid, task_status_t status)
{
sql ("INSERT into reports (uuid, owner, task, date, comment,"
" scan_run_status, slave_progress)"
" VALUES ('%s',"
" (SELECT owner FROM tasks WHERE tasks.id = %llu),"
" %llu, %i, '', %u, 0);",
uuid, task, task, time (NULL), status);
return sql_last_insert_id ();
}
/**
* @brief Create the current report for a task.
*
* @param[in] task The task.
* @param[out] report_id Report ID.
* @param[in] status Run status of scan associated with report.
*
* @return 0 success, -1 global_current_report is already set, -2 failed to
* generate ID.
*/
int
create_current_report (task_t task, char **report_id, task_status_t status)
{
char *id;
assert (global_current_report == (report_t) 0);
if (global_current_report) return -1;
if (report_id == NULL) report_id = &id;
/* Generate report UUID. */
*report_id = gvm_uuid_make ();
if (*report_id == NULL) return -2;
/* Create the report. */
global_current_report = make_report (task, *report_id, status);
set_report_scheduled (global_current_report);
return 0;
}
/**
* @brief Free a host detail.
*
* @param[in] detail Host detail.
*/
void
host_detail_free (host_detail_t *detail)
{
g_free (detail->ip);
g_free (detail->name);
g_free (detail->source_desc);
g_free (detail->source_name);
g_free (detail->source_type);
g_free (detail->value);
}
/**
* @brief Insert a host detail into a report.
*
* @param[in] report The detail's report.
* @param[in] host The detail's host.
* @param[in] s_type The detail's source type.
* @param[in] s_name The detail's source name.
* @param[in] s_desc The detail's source description.
* @param[in] name The detail's name.
* @param[in] value The detail's value.
*/
void
insert_report_host_detail (report_t report, const char *host,
const char *s_type, const char *s_name,
const char *s_desc, const char *name,
const char *value)
{
char *quoted_host, *quoted_source_name, *quoted_source_type;
char *quoted_source_desc, *quoted_name, *quoted_value;
quoted_host = sql_quote (host);
quoted_source_type = sql_quote (s_type);
quoted_source_name = sql_quote (s_name);
quoted_source_desc = sql_quote (s_desc);
quoted_name = sql_quote (name);
quoted_value = sql_quote (value);
sql ("INSERT INTO report_host_details"
" (report_host, source_type, source_name, source_description,"
" name, value)"
" VALUES"
" ((SELECT id FROM report_hosts"
" WHERE report = %llu AND host = '%s'),"
" '%s', '%s', '%s', '%s', '%s');",
report, quoted_host, quoted_source_type, quoted_source_name,
quoted_source_desc, quoted_name, quoted_value);
g_free (quoted_host);
g_free (quoted_source_type);
g_free (quoted_source_name);
g_free (quoted_source_desc);
g_free (quoted_name);
g_free (quoted_value);
}
/**
* @brief Maximum number of values per insert, when uploading report.
*/
#define CREATE_REPORT_INSERT_SIZE 300
/**
* @brief Number of results per transaction, when uploading report.
*/
#define CREATE_REPORT_CHUNK_SIZE 10
/**
* @brief Number of microseconds to sleep between insert chunks.
*/
#define CREATE_REPORT_CHUNK_SLEEP 1000
/**
* @brief Create a report from an array of results.
*
* @param[in] results Array of create_report_result_t pointers.
* @param[in] task_id UUID of container task, or NULL to create new one.
* @param[in] in_assets Whether to create assets from the report.
* @param[in] scan_start Scan start time text.
* @param[in] scan_end Scan end time text.
* @param[in] host_starts Array of create_report_result_t pointers. Host
* name in host, time in description.
* @param[in] host_ends Array of create_report_result_t pointers. Host
* name in host, time in description.
* @param[in] details Array of host_detail_t pointers.
* @param[out] report_id Report ID.
*
* @return 0 success, 99 permission denied, -1 error, -2 failed to generate ID,
* -3 task_id is NULL, -4 failed to find task, -5 task must be
* container, -6 permission to create assets denied.
*/
int
create_report (array_t *results, const char *task_id, const char *in_assets,
const char *scan_start, const char *scan_end,
array_t *host_starts, array_t *host_ends, array_t *details,
char **report_id)
{
int index, in_assets_int, count, insert_count, first, rc;
create_report_result_t *result, *end, *start;
report_t report;
user_t owner;
task_t task;
pid_t pid;
host_detail_t *detail;
GString *insert;
in_assets_int
= (in_assets && strcmp (in_assets, "") && strcmp (in_assets, "0"));
if (in_assets_int && acl_user_may ("create_asset") == 0)
return -6;
g_debug ("%s", __func__);
if (acl_user_may ("create_report") == 0)
return 99;
if (task_id == NULL)
return -3;
sql_begin_immediate ();
/* Find the task. */
rc = 0;
/* It's important that the task is not in the trash, because we
* are inserting results below. This find function will fail if
* the task is in the trash. */
if (find_task_with_permission (task_id, &task, "modify_task"))
rc = -1;
else if (task == 0)
rc = -4;
else if (task_target (task))
rc = -5;
if (rc)
{
sql_rollback ();
return rc;
}
/* Generate report UUID. */
*report_id = gvm_uuid_make ();
if (*report_id == NULL) return -2;
/* Create the report. */
report = make_report (task, *report_id, TASK_STATUS_RUNNING);
if (scan_start)
{
sql ("UPDATE reports SET start_time = %i WHERE id = %llu;",
parse_iso_time (scan_start),
report);
}
if (scan_end)
{
sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
parse_iso_time (scan_end),
report);
}
/* Show that the upload has started. */
set_task_run_status (task, TASK_STATUS_RUNNING);
sql ("UPDATE tasks SET upload_result_count = %llu WHERE id = %llu;",
results->len,
task);
sql_commit ();
/* Fork a child to import the results while the parent responds to the
* client. */
pid = fork ();
switch (pid)
{
case 0:
{
/* Child.
*
* Fork again so the parent can wait on the child, to prevent
* zombies. */
cleanup_manage_process (FALSE);
pid = fork ();
switch (pid)
{
case 0:
/* Grandchild. Reopen the database (required after fork) and carry on
* to import the reports, . */
reinit_manage_process ();
break;
case -1:
/* Grandchild's parent when error. */
g_warning ("%s: fork: %s", __func__, strerror (errno));
exit (EXIT_FAILURE);
break;
default:
/* Grandchild's parent. Exit, to close parent's wait. */
g_debug ("%s: %i forked %i", __func__, getpid (), pid);
exit (EXIT_SUCCESS);
break;
}
}
break;
case -1:
/* Parent when error. */
g_warning ("%s: fork: %s", __func__, strerror (errno));
global_current_report = report;
set_task_interrupted (task,
"Failed to fork child to import report."
" Setting task status to Interrupted.");
global_current_report = 0;
return -1;
break;
default:
{
int status;
/* Parent. Wait to prevent zombie, then return to respond to client. */
g_debug ("%s: %i forked %i", __func__, getpid (), pid);
while (waitpid (pid, &status, 0) < 0)
{
if (errno == ECHILD)
{
g_warning ("%s: Failed to get child exit status",
__func__);
return -1;
}
if (errno == EINTR)
continue;
g_warning ("%s: waitpid: %s",
__func__,
strerror (errno));
return -1;
}
return 0;
break;
}
}
proctitle_set ("gvmd: Importing results");
/* Add the results. */
if (sql_int64 (&owner,
"SELECT owner FROM tasks WHERE tasks.id = %llu",
task))
{
g_warning ("%s: failed to get owner of task", __func__);
return -1;
}
sql_begin_immediate ();
g_debug ("%s: add hosts", __func__);
index = 0;
while ((start = (create_report_result_t*) g_ptr_array_index (host_starts,
index++)))
if (start->host && start->description)
manage_report_host_add (report, start->host,
parse_iso_time (start->description),
0);
g_debug ("%s: add results", __func__);
insert = g_string_new ("");
index = 0;
first = 1;
insert_count = 0;
count = 0;
while ((result = (create_report_result_t*) g_ptr_array_index (results,
index++)))
{
gchar *quoted_host, *quoted_hostname, *quoted_port, *quoted_nvt_oid;
gchar *quoted_description, *quoted_scan_nvt_version, *quoted_severity;
gchar *quoted_qod, *quoted_qod_type;
g_debug ("%s: add results: index: %i", __func__, index);
quoted_host = sql_quote (result->host ? result->host : "");
quoted_hostname = sql_quote (result->hostname ? result->hostname : "");
quoted_port = sql_quote (result->port ? result->port : "");
quoted_nvt_oid = sql_quote (result->nvt_oid ? result->nvt_oid : "");
quoted_description = sql_quote (result->description
? result->description
: "");
quoted_scan_nvt_version = sql_quote (result->scan_nvt_version
? result->scan_nvt_version
: "");
quoted_severity = sql_quote (result->severity ? result->severity : "");
if (result->qod && strcmp (result->qod, "") && strcmp (result->qod, "0"))
quoted_qod = sql_quote (result->qod);
else
quoted_qod = g_strdup (G_STRINGIFY (QOD_DEFAULT));
quoted_qod_type = sql_quote (result->qod_type ? result->qod_type : "");
result_nvt_notice (quoted_nvt_oid);
if (first)
g_string_append (insert,
"INSERT INTO results"
" (uuid, owner, date, task, host, hostname, port,"
" nvt, type, description,"
" nvt_version, severity, qod, qod_type,"
" result_nvt, report)"
" VALUES");
else
g_string_append (insert, ", ");
first = 0;
g_string_append_printf (insert,
" (make_uuid (), %llu, m_now (), %llu, '%s',"
" '%s', '%s', '%s', '%s', '%s', '%s', '%s',"
" '%s', '%s',"
" (SELECT id FROM result_nvts WHERE nvt = '%s'),"
" %llu)",
owner,
task,
quoted_host,
quoted_hostname,
quoted_port,
quoted_nvt_oid,
result->threat
? threat_message_type (result->threat)
: "Log Message",
quoted_description,
quoted_scan_nvt_version,
quoted_severity,
quoted_qod,
quoted_qod_type,
quoted_nvt_oid,
report);
/* Limit the number of results inserted at a time. */
if (insert_count == CREATE_REPORT_INSERT_SIZE)
{
sql ("%s", insert->str);
g_string_truncate (insert, 0);
count++;
insert_count = 0;
first = 1;
if (count == CREATE_REPORT_CHUNK_SIZE)
{
report_cache_counts (report, 1, 1, NULL);
sql_commit ();
gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
sql_begin_immediate ();
count = 0;
}
}
insert_count++;
g_free (quoted_host);
g_free (quoted_hostname);
g_free (quoted_port);
g_free (quoted_nvt_oid);
g_free (quoted_description);
g_free (quoted_scan_nvt_version);
g_free (quoted_severity);
g_free (quoted_qod);
g_free (quoted_qod_type);
}
if (first == 0)
{
sql ("%s", insert->str);
report_cache_counts (report, 1, 1, NULL);
sql_commit ();
gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
sql_begin_immediate ();
}
sql ("INSERT INTO result_nvt_reports (result_nvt, report)"
" SELECT distinct result_nvt, %llu FROM results"
" WHERE results.report = %llu;",
report,
report);
g_debug ("%s: add host ends", __func__);
index = 0;
count = 0;
while ((end = (create_report_result_t*) g_ptr_array_index (host_ends,
index++)))
if (end->host)
{
gchar *quoted_host;
quoted_host = sql_quote (end->host);
if (end->description)
sql ("UPDATE report_hosts SET end_time = %i"
" WHERE report = %llu AND host = '%s';",
parse_iso_time (end->description),
report,
quoted_host);
else
sql ("UPDATE report_hosts SET end_time = NULL"
" WHERE report = %llu AND host = '%s';",
report,
quoted_host);
g_free (quoted_host);
count++;
if (count == CREATE_REPORT_CHUNK_SIZE)
{
sql_commit ();
gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
sql_begin_immediate ();
count = 0;
}
}
g_debug ("%s: add host details", __func__);
index = 0;
first = 1;
count = 0;
insert_count = 0;
g_string_truncate (insert, 0);
while ((detail = (host_detail_t*) g_ptr_array_index (details, index++)))
if (detail->ip && detail->name)
{
char *quoted_host, *quoted_source_name, *quoted_source_type;
char *quoted_source_desc, *quoted_name, *quoted_value;
quoted_host = sql_quote (detail->ip);
quoted_source_type = sql_quote (detail->source_type ?: "");
quoted_source_name = sql_quote (detail->source_name ?: "");
quoted_source_desc = sql_quote (detail->source_desc ?: "");
quoted_name = sql_quote (detail->name);
quoted_value = sql_quote (detail->value ?: "");
if (first)
g_string_append (insert,
"INSERT INTO report_host_details"
" (report_host, source_type, source_name,"
" source_description, name, value)"
" VALUES");
else
g_string_append (insert, ", ");
first = 0;
g_string_append_printf (insert,
" ((SELECT id FROM report_hosts"
" WHERE report = %llu AND host = '%s'),"
" '%s', '%s', '%s', '%s', '%s')",
report, quoted_host, quoted_source_type,
quoted_source_name, quoted_source_desc,
quoted_name, quoted_value);
g_free (quoted_host);
g_free (quoted_source_type);
g_free (quoted_source_name);
g_free (quoted_source_desc);
g_free (quoted_name);
g_free (quoted_value);
/* Limit the number of details inserted at a time. */
if (insert_count == CREATE_REPORT_INSERT_SIZE)
{
sql ("%s", insert->str);
g_string_truncate (insert, 0);
count++;
insert_count = 0;
first = 1;
if (count == CREATE_REPORT_CHUNK_SIZE)
{
sql_commit ();
gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
sql_begin_immediate ();
count = 0;
}
}
insert_count++;
}
sql_commit ();
index = 0;
sql_begin_immediate ();
while ((end = (create_report_result_t*) g_ptr_array_index (host_ends,
index++)))
if (end->host)
{
sql_commit ();
gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
add_assets_from_host_in_report (report, end->host);
sql_begin_immediate ();
}
if (first == 0)
sql ("%s", insert->str);
sql_commit ();
g_string_free (insert, TRUE);
current_scanner_task = task;
global_current_report = report;
set_task_run_status (task, TASK_STATUS_DONE);
current_scanner_task = 0;
global_current_report = 0;
if (in_assets_int)
{
create_asset_report (*report_id, "");
}
exit (EXIT_SUCCESS);
return 0;
}
/**
* @brief Return the UUID of a report.
*
* @param[in] report Report.
*
* @return Report UUID.
*/
char*
report_uuid (report_t report)
{
return sql_string ("SELECT uuid FROM reports WHERE id = %llu;",
report);
}
/**
* @brief Return the task of a report.
*
* @param[in] report A report.
* @param[out] task Task return, 0 if successfully failed to find task.
*
* @return FALSE on success (including if failed to find report), TRUE on error.
*/
gboolean
report_task (report_t report, task_t *task)
{
switch (sql_int64 (task,
"SELECT task FROM reports WHERE id = %llu;",
report))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*task = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return TRUE;
break;
}
return FALSE;
}
/**
* @brief Get compliance counts for a report.
*
* @param[in] report_id UUID of the report.
* @param[out] compliance_yes Number of "YES" results.
* @param[out] compliance_no Number of "NO" results.
* @param[out] compliance_incomplete Number of "INCOMPLETE" results.
*/
void
report_compliance_by_uuid (const char *report_id,
int *compliance_yes,
int *compliance_no,
int *compliance_incomplete)
{
report_t report;
gchar *quoted_uuid = sql_quote (report_id);
sql_int64 (&report,
"SELECT id FROM reports WHERE uuid = '%s';",
quoted_uuid);
if (compliance_yes)
{
*compliance_yes
= sql_int ("SELECT count(*) FROM results"
" WHERE report = %llu"
" AND description LIKE 'Compliant:%%YES%%';",
report);
}
if (compliance_no)
{
*compliance_no
= sql_int ("SELECT count(*) FROM results"
" WHERE report = %llu"
" AND description LIKE 'Compliant:%%NO%%';",
report);
}
if (compliance_incomplete)
{
*compliance_incomplete
= sql_int ("SELECT count(*) FROM results"
" WHERE report = %llu"
" AND description LIKE 'Compliant:%%INCOMPLETE%%';",
report);
}
g_free (quoted_uuid);
}
/**
* @brief Return the source interface of a report.
*
* @param[in] report Report.
*
* @return Source interface.
*/
static char*
report_source_iface (report_t report)
{
return sql_string ("SELECT source_iface FROM reports WHERE id = %llu;",
report);
}
/**
* @brief Add a result to a report.
*
* @param[in] report The report.
* @param[in] result The result.
*/
static void
report_add_result_for_buffer (report_t report, result_t result)
{
double severity, ov_severity;
int qod;
rowid_t rowid;
iterator_t cache_iterator;
user_t previous_user = 0;
assert (result);
if (report == 0)
return;
if (sql_int ("SELECT NOT EXISTS (SELECT * from result_nvt_reports"
" WHERE result_nvt = (SELECT result_nvt"
" FROM results"
" WHERE id = %llu)"
" AND report = %llu);",
result,
report))
sql ("INSERT INTO result_nvt_reports (result_nvt, report)"
" VALUES ((SELECT result_nvt FROM results WHERE id = %llu),"
" %llu);",
result,
report);
qod = sql_int ("SELECT qod FROM results WHERE id = %llu;",
result);
severity = sql_double ("SELECT severity FROM results WHERE id = %llu;",
result);
ov_severity = severity;
init_report_counts_build_iterator (&cache_iterator, report, qod, 1, NULL);
while (next (&cache_iterator))
{
int min_qod = report_counts_build_iterator_min_qod (&cache_iterator);
int override = report_counts_build_iterator_override (&cache_iterator);
user_t user = report_counts_build_iterator_user (&cache_iterator);
if (override && user != previous_user)
{
char *ov_severity_str;
gchar *owned_clause, *with_clause;
owned_clause = acl_where_owned_for_get ("override", NULL, NULL,
&with_clause);
ov_severity_str
= sql_string ("%s"
" SELECT coalesce (overrides.new_severity, %1.1f)"
" FROM overrides, results"
" WHERE results.id = %llu"
" AND overrides.nvt = results.nvt"
" AND %s"
" AND ((overrides.end_time = 0)"
" OR (overrides.end_time >= m_now ()))"
" AND (overrides.task ="
" (SELECT reports.task FROM reports"
" WHERE reports.id = %llu)"
" OR overrides.task = 0)"
" AND (overrides.result = results.id"
" OR overrides.result = 0)"
" AND (overrides.hosts is NULL"
" OR overrides.hosts = ''"
" OR hosts_contains (overrides.hosts,"
" results.host))"
" AND (overrides.port is NULL"
" OR overrides.port = ''"
" OR overrides.port = results.port)"
" AND severity_matches_ov (%1.1f,"
" overrides.severity)"
" ORDER BY overrides.result DESC,"
" overrides.task DESC, overrides.port DESC,"
" overrides.severity ASC,"
" overrides.creation_time DESC"
" LIMIT 1",
with_clause ? with_clause : "",
severity,
result,
owned_clause,
report,
severity);
g_free (with_clause);
g_free (owned_clause);
if (ov_severity_str == NULL
|| (sscanf (ov_severity_str, "%lf", &ov_severity) != 1))
ov_severity = severity;
free (ov_severity_str);
previous_user = user;
}
rowid = 0;
sql_int64 (&rowid,
"SELECT id FROM report_counts"
" WHERE report = %llu"
" AND \"user\" = %llu"
" AND override = %d"
" AND severity = %1.1f"
" AND min_qod = %d",
report, user, override,
override ? ov_severity : severity,
min_qod);
if (rowid)
sql ("UPDATE report_counts"
" SET count = count + 1"
" WHERE id = %llu;",
rowid);
else
sql ("INSERT INTO report_counts"
" (report, \"user\", override, min_qod, severity, count, end_time)"
" VALUES"
" (%llu, %llu, %d, %d, %1.1f, 1, 0);",
report, user, override,
override ? ov_severity : severity,
min_qod);
}
cleanup_iterator (&cache_iterator);
}
/**
* @brief Add a result to a report.
*
* @param[in] report The report.
* @param[in] result The result.
*/
void
report_add_result (report_t report, result_t result)
{
if (report == 0 || result == 0)
return;
sql ("UPDATE results SET report = %llu,"
" owner = (SELECT reports.owner"
" FROM reports WHERE id = %llu)"
" WHERE id = %llu;",
report, report, result);
report_add_result_for_buffer (report, result);
sql ("UPDATE report_counts"
" SET end_time = (SELECT coalesce(min(overrides.end_time), 0)"
" FROM overrides, results"
" WHERE overrides.nvt = results.nvt"
" AND results.report = %llu"
" AND overrides.end_time >= m_now ())"
" WHERE report = %llu AND override = 1;",
report, report);
}
/**
* @brief Add results from an array to a report.
*
* @param[in] report The report to add the results to.
* @param[in] results GArray containing the row ids of the results to add.
*/
void
report_add_results_array (report_t report, GArray *results)
{
GString *array_sql;
int index;
if (report == 0 || results == NULL || results->len == 0)
return;
array_sql = g_string_new ("(");
for (index = 0; index < results->len; index++)
{
result_t result;
result = g_array_index (results, result_t, index);
if (index)
g_string_append (array_sql, ", ");
g_string_append_printf (array_sql, "%llu", result);
}
g_string_append_c (array_sql, ')');
sql ("UPDATE results SET report = %llu,"
" owner = (SELECT reports.owner"
" FROM reports WHERE id = %llu)"
" WHERE id IN %s;",
report, report, array_sql->str);
for (index = 0; index < results->len; index++)
{
result_t result;
result = g_array_index (results, result_t, index);
report_add_result_for_buffer (report, result);
}
sql ("UPDATE report_counts"
" SET end_time = (SELECT coalesce(min(overrides.end_time), 0)"
" FROM overrides, results"
" WHERE overrides.nvt = results.nvt"
" AND results.report = %llu"
" AND overrides.end_time >= m_now ())"
" WHERE report = %llu AND override = 1;",
report, report);
g_string_free (array_sql, TRUE);
}
/**
* @brief Filter columns for report iterator.
*/
#define REPORT_ITERATOR_FILTER_COLUMNS \
{ ANON_GET_ITERATOR_FILTER_COLUMNS, "task_id", "name", "date", "status", \
"task", "severity", "false_positive", "log", "low", "medium", "high", \
"hosts", "result_hosts", "fp_per_host", "log_per_host", "low_per_host", \
"medium_per_host", "high_per_host", "duration", "duration_per_host", \
NULL }
/**
* @brief Report iterator columns.
*/
#define REPORT_ITERATOR_COLUMNS \
{ \
{ "id", NULL, KEYWORD_TYPE_INTEGER }, \
{ "uuid", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (start_time)", "name", KEYWORD_TYPE_STRING }, \
{ "''", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (start_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (end_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "start_time", "created", KEYWORD_TYPE_INTEGER }, \
{ "end_time", "modified", KEYWORD_TYPE_INTEGER }, \
{ "(SELECT name FROM users WHERE users.id = reports.owner)", \
"_owner", \
KEYWORD_TYPE_STRING }, \
{ "owner", NULL, KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Report iterator columns.
*/
#define REPORT_ITERATOR_WHERE_COLUMNS \
{ \
{ "run_status_name (scan_run_status)", "status", KEYWORD_TYPE_STRING }, \
{ \
"(SELECT uuid FROM tasks WHERE tasks.id = task)", \
"task_id", \
KEYWORD_TYPE_STRING \
}, \
{ "date", NULL, KEYWORD_TYPE_INTEGER }, \
{ "(SELECT name FROM tasks WHERE tasks.id = task)", "task" }, \
{ \
"report_severity (id, opts.override, opts.min_qod)", \
"severity", \
KEYWORD_TYPE_DOUBLE \
}, \
{ \
"report_severity_count (id, opts.override, opts.min_qod," \
" 'False Positive')", \
"false_positive", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"report_severity_count (id, opts.override, opts.min_qod, 'Log')", \
"log", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"report_severity_count (id, opts.override, opts.min_qod, 'Low')", \
"low", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"report_severity_count (id, opts.override, opts.min_qod, 'Medium')", \
"medium", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"report_severity_count (id, opts.override, opts.min_qod, 'High')", \
"high", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT name FROM users WHERE users.id = reports.owner)", \
"_owner", \
KEYWORD_TYPE_STRING \
}, \
{ \
"report_host_count (id)", \
"hosts", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"report_result_host_count (id, opts.min_qod)", \
"result_hosts", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"coalesce (report_severity_count (id, opts.override, opts.min_qod," \
" 'False Positive') * 1.0" \
" / nullif (report_result_host_count (id, opts.min_qod), 0),"\
" 0)", \
"fp_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"coalesce (report_severity_count (id, opts.override, opts.min_qod," \
" 'Log') * 1.0" \
" / nullif (report_result_host_count (id, opts.min_qod), 0),"\
" 0)", \
"log_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"coalesce (report_severity_count (id, opts.override, opts.min_qod," \
" 'Low') * 1.0" \
" / nullif (report_result_host_count (id, opts.min_qod), 0),"\
" 0)", \
"low_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"coalesce (report_severity_count (id, opts.override, opts.min_qod," \
" 'Medium') * 1.0" \
" / nullif (report_result_host_count (id, opts.min_qod), 0),"\
" 0)", \
"medium_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"coalesce (report_severity_count (id, opts.override, opts.min_qod," \
" 'High') * 1.0" \
" / nullif (report_result_host_count (id, opts.min_qod), 0),"\
" 0)", \
"high_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(CASE WHEN (start_time IS NULL or end_time IS NULL)" \
" THEN NULL ELSE end_time - start_time END)", \
"duration", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(CASE WHEN (start_time IS NULL or end_time IS NULL" \
" or report_result_host_count (id, opts.min_qod) = 0)" \
" THEN NULL" \
" ELSE (end_time - start_time)" \
" / report_result_host_count (id, opts.min_qod) END)", \
"duration_per_host", \
KEYWORD_TYPE_INTEGER \
}, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Generate the extra_tables string for a report iterator.
*
* @param[in] override Whether to apply overrides.
* @param[in] min_qod Minimum QoD of results to count.
*
* @return Newly allocated string with the extra_tables clause.
*/
static gchar*
report_iterator_opts_table (int override, int min_qod)
{
return g_strdup_printf (", (SELECT"
" %d AS override,"
" %d AS min_qod)"
" AS opts",
override,
min_qod);
}
/**
* @brief Count number of reports.
*
* @param[in] get GET params.
*
* @return Total number of reports in filtered set.
*/
int
report_count (const get_data_t *get)
{
static const char *filter_columns[] = REPORT_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = REPORT_ITERATOR_COLUMNS;
static column_t where_columns[] = REPORT_ITERATOR_WHERE_COLUMNS;
gchar *extra_tables;
int ret;
extra_tables = report_iterator_opts_table (0, MIN_QOD_DEFAULT);
ret = count2 ("report", get, columns, NULL, where_columns, NULL,
filter_columns, 0,
extra_tables,
get->trash
? " AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 2"
: " AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 0",
NULL,
TRUE);
g_free (extra_tables);
return ret;
}
/**
* @brief Initialise a report iterator, including observed reports.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find report, 2 failed to find filter,
* -1 error.
*/
int
init_report_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = REPORT_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = REPORT_ITERATOR_COLUMNS;
static column_t where_columns[] = REPORT_ITERATOR_WHERE_COLUMNS;
char *filter;
int overrides, min_qod;
gchar *extra_tables;
int ret;
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
overrides = filter_term_apply_overrides (filter ? filter : get->filter);
min_qod = filter_term_min_qod (filter ? filter : get->filter);
free (filter);
extra_tables = report_iterator_opts_table (overrides, min_qod);
ret = init_get_iterator2 (iterator,
"report",
get,
/* Columns. */
columns,
NULL,
/* Filterable columns not in SELECT columns. */
where_columns,
NULL,
filter_columns,
0,
extra_tables,
get->trash
? " AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 2"
: " AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 0",
NULL,
TRUE,
FALSE,
NULL);
g_free (extra_tables);
return ret;
}
/**
* @brief Initialise a report iterator.
*
* @param[in] iterator Iterator.
* @param[in] task Task whose reports the iterator loops over.
*/
void
init_report_iterator_task (iterator_t* iterator, task_t task)
{
assert (task);
init_iterator (iterator,
"SELECT id, uuid FROM reports WHERE task = %llu;",
task);
}
/**
* @brief Get the UUID from a report iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (report_iterator_uuid, 1);
/**
* @brief Read the next report from an iterator.
*
* @param[in] iterator Task iterator.
* @param[out] report Report.
*
* @return TRUE if there was a next task, else FALSE.
*/
gboolean
next_report (iterator_t* iterator, report_t* report)
{
if (next (iterator))
{
*report = iterator_int64 (iterator, 0);
return TRUE;
}
return FALSE;
}
/**
* @brief Return SQL WHERE for restricting a SELECT to levels.
*
* @param[in] levels String describing threat levels (message types)
* to include in report (for example, "hmlg" for
* High, Medium, Low and loG). All levels if NULL.
* @param[in] new_severity_sql SQL for new severity.
*
* @return WHERE clause for levels if one is required, else NULL.
*/
static GString *
where_levels_auto (const char *levels, const char *new_severity_sql)
{
int count;
GString *levels_sql;
/* Generate SQL for constraints on message type, according to levels. */
levels_sql = g_string_new ("");
if (levels == NULL || strlen (levels) == 0)
{
g_string_append_printf (levels_sql,
" AND %s != " G_STRINGIFY (SEVERITY_ERROR),
new_severity_sql);
return levels_sql;
}
count = 0;
g_string_append_printf (levels_sql, " AND severity_in_levels (%s", new_severity_sql);
if (strchr (levels, 'h'))
{
g_string_append (levels_sql, ", 'high'");
count++;
}
if (strchr (levels, 'm'))
{
g_string_append (levels_sql, ", 'medium'");
count++;
}
if (strchr (levels, 'l'))
{
g_string_append (levels_sql, ", 'low'");
count++;
}
if (strchr (levels, 'g'))
{
g_string_append (levels_sql, ", 'log'");
count++;
}
if (strchr (levels, 'f'))
{
g_string_append (levels_sql, ", 'false'");
count++;
}
if (count == 0)
{
g_string_free (levels_sql, TRUE);
return NULL;
}
g_string_append (levels_sql, ")");
if (count == 5)
{
/* All levels. */
g_string_free (levels_sql, TRUE);
levels_sql = g_string_new ("");
/* It's not possible to override from or to the error severity, so no
* need to use the overridden severity here (new_severity_sql). This
* helps with the default result counting performance because the
* overridden severity is complex. */
g_string_append_printf (levels_sql,
" AND severity != " G_STRINGIFY (SEVERITY_ERROR));
}
return levels_sql;
}
/**
* @brief Return SQL WHERE for restricting a SELECT to a minimum QoD.
*
* @param[in] min_qod Minimum value for QoD.
*
* @return WHERE clause if one is required, else an empty string.
*/
static gchar*
where_qod (int min_qod)
{
gchar *qod_sql;
if (min_qod <= 0)
qod_sql = g_strdup ("");
else
qod_sql = g_strdup_printf (" AND (results.qod >= CAST (%d AS INTEGER))",
min_qod);
return qod_sql;
}
/**
* @brief Filter columns for result iterator.
*/
#define RESULT_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "host", "location", "nvt", \
"type", "original_type", \
"description", "task", "report", "cvss_base", "nvt_version", \
"severity", "original_severity", "vulnerability", "date", "report_id", \
"solution_type", "qod", "qod_type", "task_id", "cve", "hostname", \
"path", NULL }
// TODO Combine with RESULT_ITERATOR_COLUMNS.
/**
* @brief Result iterator filterable columns, for severity only version .
*/
#define BASE_RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE \
{ "id", NULL, KEYWORD_TYPE_INTEGER }, \
{ "uuid", NULL, KEYWORD_TYPE_STRING }, \
{ "(SELECT name FROM nvts WHERE nvts.oid = nvt)", \
"name", \
KEYWORD_TYPE_STRING }, \
{ "''", "comment", KEYWORD_TYPE_STRING }, \
{ " iso_time (date, opts.user_zone)", \
"creation_time", \
KEYWORD_TYPE_STRING }, \
{ " iso_time (date, opts.user_zone)", \
"modification_time", \
KEYWORD_TYPE_STRING }, \
{ "date", "created", KEYWORD_TYPE_INTEGER }, \
{ "date", "modified", KEYWORD_TYPE_INTEGER }, \
{ "(SELECT name FROM users WHERE users.id = results.owner)", \
"_owner", \
KEYWORD_TYPE_STRING }, \
{ "owner", NULL, KEYWORD_TYPE_INTEGER }, \
/* Result specific columns. */ \
{ "host", NULL, KEYWORD_TYPE_STRING }, \
{ "port", "location", KEYWORD_TYPE_STRING }, \
{ "nvt", NULL, KEYWORD_TYPE_STRING }, \
{ "severity_to_type (severity)", "original_type", KEYWORD_TYPE_STRING }, \
{ "'Log Message'", /* Adjusted by init_result_get_iterator_severity. */ \
"type", \
KEYWORD_TYPE_STRING }, \
{ "description", NULL, KEYWORD_TYPE_STRING }, \
{ "task", NULL, KEYWORD_TYPE_INTEGER }, \
{ "report", "report_rowid", KEYWORD_TYPE_INTEGER }, \
{ "(SELECT cvss_base FROM nvts WHERE nvts.oid = nvt)", \
"cvss_base", \
KEYWORD_TYPE_DOUBLE }, \
{ "nvt_version", NULL, KEYWORD_TYPE_STRING }, \
{ "severity", "original_severity", KEYWORD_TYPE_DOUBLE }, \
{ "(SELECT name FROM nvts WHERE nvts.oid = nvt)", \
"vulnerability", \
KEYWORD_TYPE_STRING }, \
{ "date" , NULL, KEYWORD_TYPE_INTEGER }, \
{ "(SELECT uuid FROM reports WHERE id = report)", \
"report_id", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT solution_type FROM nvts WHERE nvts.oid = nvt)", \
"solution_type", \
KEYWORD_TYPE_STRING }, \
{ "qod", NULL, KEYWORD_TYPE_INTEGER }, \
{ "qod_type", NULL, KEYWORD_TYPE_STRING }, \
{ "(CASE WHEN (hostname IS NULL) OR (hostname = '')" \
" THEN (SELECT value FROM report_host_details" \
" WHERE name = 'hostname'" \
" AND report_host = (SELECT id FROM report_hosts" \
" WHERE report_hosts.host=results.host" \
" AND report_hosts.report = results.report)" \
" LIMIT 1)" \
" ELSE hostname" \
" END)", \
"hostname", \
KEYWORD_TYPE_STRING \
}, \
{ "(SELECT uuid FROM tasks WHERE id = task)", \
"task_id", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT cve FROM nvts WHERE oid = nvt)", "cve", KEYWORD_TYPE_STRING }, \
{ "path", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT CASE WHEN host IS NULL" \
" THEN NULL" \
" ELSE (SELECT uuid FROM hosts" \
" WHERE id = (SELECT host FROM host_identifiers" \
" WHERE source_type = 'Report Host'" \
" AND name = 'ip'" \
" AND source_id" \
" = (SELECT uuid" \
" FROM reports" \
" WHERE id = results.report)" \
" AND value = results.host" \
" LIMIT 1))" \
" END)", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT CASE" \
" WHEN EXISTS (SELECT * FROM notes" \
" WHERE (result = results.id" \
" OR (result = 0 AND nvt = results.nvt))" \
" AND (task = 0 OR task = results.task))" \
" THEN 1" \
" ELSE 0" \
" END)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "(SELECT CASE" \
" WHEN EXISTS (SELECT * FROM overrides" \
" WHERE (result = results.id" \
" OR (result = 0 AND nvt = results.nvt))" \
" AND (task = 0 OR task = results.task))" \
" THEN 1" \
" ELSE 0" \
" END)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ TICKET_SQL_RESULT_MAY_HAVE_TICKETS, \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "(SELECT name FROM tasks WHERE tasks.id = task)", \
"task", \
KEYWORD_TYPE_STRING },
/**
* @brief Result iterator columns.
*/
#define RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE \
{ \
BASE_RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Result iterator columns, when CERT db is not loaded.
*/
#define RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE_NO_CERT \
{ \
BASE_RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE \
{ "0", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "0", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Result iterator columns.
*/
#define PRE_BASE_RESULT_ITERATOR_COLUMNS(new_severity_sql) \
{ "results.id", NULL, KEYWORD_TYPE_INTEGER }, \
/* ^ 0 */ \
{ "results.uuid", NULL, KEYWORD_TYPE_STRING }, \
{ "nvts.name", \
"name", \
KEYWORD_TYPE_STRING }, \
{ "''", "comment", KEYWORD_TYPE_STRING }, \
{ " iso_time (date, opts.user_zone)", \
"creation_time", \
KEYWORD_TYPE_STRING }, \
{ " iso_time (date, opts.user_zone)", \
"modification_time", \
KEYWORD_TYPE_STRING }, \
{ "date", "created", KEYWORD_TYPE_INTEGER }, \
{ "date", "modified", KEYWORD_TYPE_INTEGER }, \
{ "(SELECT name FROM users WHERE users.id = results.owner)", \
"_owner", \
KEYWORD_TYPE_STRING }, \
{ "results.owner", NULL, KEYWORD_TYPE_INTEGER }, \
/* ^ 9 */ \
/* Result specific columns. */ \
{ "host", NULL, KEYWORD_TYPE_STRING }, \
/* ^ 10 = 0 */ \
{ "port", "location", KEYWORD_TYPE_STRING }, \
{ "nvt", NULL, KEYWORD_TYPE_STRING }, \
{ "severity_to_type (results.severity)", \
"original_type", \
KEYWORD_TYPE_STRING }, \
{ "severity_to_type (" new_severity_sql ")", \
"type", \
KEYWORD_TYPE_STRING }, \
{ "description", NULL, KEYWORD_TYPE_STRING }, \
{ "task", NULL, KEYWORD_TYPE_INTEGER }, \
{ "report", "report_rowid", KEYWORD_TYPE_INTEGER }, \
{ "nvts.cvss_base", \
"cvss_base", \
KEYWORD_TYPE_DOUBLE }, \
{ "nvt_version", NULL, KEYWORD_TYPE_STRING }, \
{ "results.severity", "original_severity", KEYWORD_TYPE_DOUBLE }, \
/* ^ 20 = 10 */ \
{ new_severity_sql, \
"severity", \
KEYWORD_TYPE_DOUBLE }, \
{ "nvts.name", \
"vulnerability", \
KEYWORD_TYPE_STRING }, \
{ "date" , NULL, KEYWORD_TYPE_INTEGER }, \
{ "(SELECT uuid FROM reports WHERE id = report)", \
"report_id", \
KEYWORD_TYPE_STRING }, \
{ "nvts.solution_type", \
"solution_type", \
KEYWORD_TYPE_STRING }, \
{ "results.qod", "qod", KEYWORD_TYPE_INTEGER }, \
{ "results.qod_type", NULL, KEYWORD_TYPE_STRING }, \
{ "(CASE WHEN (hostname IS NULL) OR (hostname = '')" \
" THEN (SELECT value FROM report_host_details" \
" WHERE name = 'hostname'" \
" AND report_host = (SELECT id FROM report_hosts" \
" WHERE report_hosts.host=results.host" \
" AND report_hosts.report = results.report)" \
" LIMIT 1)" \
" ELSE hostname" \
" END)", \
"hostname", \
KEYWORD_TYPE_STRING \
}, \
{ "(SELECT uuid FROM tasks WHERE id = task)", \
"task_id", \
KEYWORD_TYPE_STRING }, \
{ "nvts.cve", "cve", KEYWORD_TYPE_STRING }, \
/* ^ 30 = 20 */ \
{ "path", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT CASE WHEN host IS NULL" \
" THEN NULL" \
" ELSE (SELECT uuid FROM hosts" \
" WHERE id = (SELECT host FROM host_identifiers" \
" WHERE source_type = 'Report Host'" \
" AND name = 'ip'" \
" AND source_id" \
" = (SELECT uuid" \
" FROM reports" \
" WHERE id = results.report)" \
" AND value = results.host" \
" LIMIT 1))" \
" END)", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT CASE" \
" WHEN EXISTS (SELECT * FROM notes" \
" WHERE (result = results.id" \
" OR (result = 0 AND nvt = results.nvt))" \
" AND (task = 0 OR task = results.task))" \
" THEN 1" \
" ELSE 0" \
" END)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "(SELECT CASE" \
" WHEN EXISTS (SELECT * FROM overrides" \
" WHERE (result = results.id" \
" OR (result = 0 AND nvt = results.nvt))" \
" AND (task = 0 OR task = results.task))" \
" THEN 1" \
" ELSE 0" \
" END)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ TICKET_SQL_RESULT_MAY_HAVE_TICKETS, \
NULL, \
KEYWORD_TYPE_INTEGER }, \
/* ^ 35 = 25 */ \
{ "(SELECT name FROM tasks WHERE tasks.id = task)", \
"task", \
KEYWORD_TYPE_STRING }, \
{ "nvts.summary", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "nvts.insight", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "nvts.affected", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "nvts.impact", \
NULL, \
KEYWORD_TYPE_STRING }, \
/* ^ 40 = 30 */ \
{ "nvts.solution", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "nvts.detection", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "nvts.family", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "nvts.tag", \
NULL, \
KEYWORD_TYPE_STRING }, \
/**
* @brief Result iterator columns.
*/
#define BASE_RESULT_ITERATOR_COLUMNS \
PRE_BASE_RESULT_ITERATOR_COLUMNS("lateral_new_severity.new_severity")
/**
* @brief Result iterator columns.
*/
#define RESULT_ITERATOR_COLUMNS \
{ \
BASE_RESULT_ITERATOR_COLUMNS \
{ SECINFO_SQL_RESULT_CERT_BUNDS, \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ SECINFO_SQL_RESULT_DFN_CERTS, \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Result iterator columns, when CERT db is not loaded.
*/
#define RESULT_ITERATOR_COLUMNS_NO_CERT \
{ \
BASE_RESULT_ITERATOR_COLUMNS \
{ "0", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "0", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Generate the extra_tables string for a result iterator.
*
* @param[in] override Whether to apply overrides.
* @param[in] dynamic Whether to use dynamic severity scores.
*
* @return Newly allocated string with the extra_tables clause.
*/
static gchar*
result_iterator_opts_table (int override, int dynamic)
{
user_t user_id;
gchar *user_zone, *quoted_user_zone, *ret;
if (current_credentials.uuid)
{
user_id = sql_int64_0 ("SELECT id FROM users WHERE uuid = '%s';",
current_credentials.uuid);
if (user_id > 0)
user_zone = sql_string ("SELECT"
" coalesce ((SELECT current_setting"
" ('gvmd.tz_override')),"
" (SELECT timezone FROM users"
" WHERE id = %llu));",
user_id);
else
user_zone = g_strdup ("UTC");
}
else
{
user_id = 0;
user_zone = sql_string ("SELECT"
" coalesce ((SELECT current_setting"
" ('gvmd.tz_override')),"
" 'UTC');");
}
quoted_user_zone = sql_quote ("user_zone");
g_free (user_zone);
ret = g_strdup_printf
(", (SELECT"
" '%s'::text AS user_zone,"
" %llu AS user_id,"
" %d AS override,"
" %d AS dynamic) AS opts",
quoted_user_zone,
user_id,
override,
dynamic);
g_free (quoted_user_zone);
return ret;
}
/**
* @brief Get new severity clause.
*
* @param[in] apply_overrides Whether to apply overrides.
* @param[in] dynamic_severity Whether to use dynamic severity.
*
* @return Newly allocated clause.
*/
static gchar*
new_severity_clause (int apply_overrides, int dynamic_severity)
{
if (apply_overrides)
{
if (dynamic_severity)
/* Overrides, dynamic. */
return g_strdup_printf ("(SELECT new_severity FROM result_new_severities_dynamic"
" WHERE result_new_severities_dynamic.result = results.id"
" AND result_new_severities_dynamic.user"
" = (SELECT id FROM users WHERE uuid = '%s')"
" LIMIT 1)",
current_credentials.uuid);
/* Overrides, no dynamic. */
return g_strdup_printf ("(SELECT new_severity FROM result_new_severities_static"
" WHERE result_new_severities_static.result = results.id"
" AND result_new_severities_static.user"
" = (SELECT id FROM users WHERE uuid = '%s')"
" LIMIT 1)",
current_credentials.uuid);
}
if (dynamic_severity)
/* Dynamic, no overrides. */
return g_strdup ("current_severity (results.severity,"
" results.nvt)");
/* No dynamic, no overrides. */
return g_strdup ("results.severity");
}
/**
* @brief Get extra_where string for a result iterator or count.
*
* @param[in] trash Whether to get results from trashcan.
* @param[in] report Report to restrict returned results to.
* @param[in] host Host to restrict returned results to.
* @param[in] apply_overrides Whether to apply overrides.
* @param[in] dynamic_severity Whether to use dynamic severity.
* @param[in] filter Filter string.
* @param[in] given_new_severity_sql SQL for new severity, or NULL.
*
* @return Newly allocated extra_where string.
*/
static gchar*
results_extra_where (int trash, report_t report, const gchar* host,
int apply_overrides, int dynamic_severity,
const gchar *filter, const gchar *given_new_severity_sql)
{
gchar *extra_where;
int min_qod;
gchar *levels;
gchar *report_clause, *host_clause, *min_qod_clause;
GString *levels_clause;
gchar *new_severity_sql;
// Get filter values
min_qod = filter_term_min_qod (filter);
levels = filter_term_value (filter, "levels");
if (levels == NULL)
levels = g_strdup ("hmlgdf");
// Build clause fragments
if (given_new_severity_sql)
new_severity_sql = NULL;
else
new_severity_sql = new_severity_clause (apply_overrides, dynamic_severity);
// Build filter clauses
report_clause = report ? g_strdup_printf (" AND (report = %llu) ", report)
: NULL;
if (host)
{
gchar *quoted_host = sql_quote (host);
host_clause = g_strdup_printf (" AND (host = '%s') ", quoted_host);
g_free (quoted_host);
}
else
host_clause = NULL;
min_qod_clause = where_qod (min_qod);
levels_clause = where_levels_auto (levels ? levels : "hmlgdf",
given_new_severity_sql
? given_new_severity_sql
: new_severity_sql);
g_free (levels);
g_free (new_severity_sql);
extra_where = g_strdup_printf("%s%s%s%s",
report_clause ? report_clause : "",
host_clause ? host_clause : "",
levels_clause->str,
min_qod_clause ? min_qod_clause : "");
g_free (min_qod_clause);
g_string_free (levels_clause, TRUE);
g_free (report_clause);
g_free (host_clause);
return extra_where;
}
/**
* @brief Initialise the severity-only result iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
* @param[in] report Report to restrict returned results to.
* @param[in] host Host to limit results to.
* @param[in] extra_order Extra text for ORDER term in SQL.
*
* @return 0 success, 1 failed to find result, 2 failed to find filter (filt_id),
* -1 error.
*/
static int
init_result_get_iterator_severity (iterator_t* iterator, const get_data_t *get,
report_t report, const char* host,
const gchar *extra_order)
{
column_t columns[2];
static column_t static_filterable_columns[]
= RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE;
static column_t static_filterable_columns_no_cert[]
= RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE_NO_CERT;
static const char *filter_columns[] = RESULT_ITERATOR_FILTER_COLUMNS;
column_t *filterable_columns;
int ret;
gchar *filter;
int apply_overrides, dynamic_severity;
gchar *extra_tables, *extra_where, *extra_where_single, *opts, *with_clause;
const gchar *lateral;
assert (report);
dynamic_severity = setting_dynamic_severity_int ();
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
apply_overrides
= filter_term_apply_overrides (filter ? filter : get->filter);
if (manage_cert_loaded ())
filterable_columns = column_array_copy (static_filterable_columns);
else
filterable_columns = column_array_copy (static_filterable_columns_no_cert);
column_array_set
(filterable_columns,
"type",
apply_overrides
? (dynamic_severity
/* Overrides, dynamic. */
? g_strdup_printf ("severity_to_type"
" ((SELECT new_severity FROM result_new_severities_dynamic"
" WHERE result_new_severities_dynamic.result = results.id"
" AND result_new_severities_dynamic.user = opts.user_id"
" LIMIT 1))")
/* Overrides, no dynamic. */
: g_strdup_printf ("severity_to_type"
" ((SELECT new_severity FROM result_new_severities_static"
" WHERE result_new_severities_static.result = results.id"
" AND result_new_severities_static.user = opts.user_id"
" LIMIT 1))"))
: (dynamic_severity
/* Dynamic, no overrides. */
? g_strdup ("severity_to_type (current_severity (results.severity,"
" results.nvt))")
/* No dynamic, no overrides. */
: g_strdup ("severity_to_type (results.severity)")));
if (dynamic_severity)
{
if (apply_overrides)
lateral
= "coalesce ((SELECT new_severity FROM valid_overrides"
" WHERE valid_overrides.result_nvt"
" = results.result_nvt"
" AND (valid_overrides.result = 0"
" OR valid_overrides.result"
" = results.id)"
" AND (valid_overrides.hosts is NULL"
" OR valid_overrides.hosts = ''"
" OR hosts_contains"
" (valid_overrides.hosts,"
" results.host))"
" AND (valid_overrides.port is NULL"
" OR valid_overrides.port = ''"
" OR valid_overrides.port"
" = results.port)"
" AND severity_matches_ov"
" (coalesce"
" ((CASE WHEN results.severity"
" > " G_STRINGIFY
(SEVERITY_LOG)
" THEN CAST (nvts.cvss_base"
" AS double precision)"
" ELSE results.severity"
" END),"
" results.severity),"
" valid_overrides.severity)"
" LIMIT 1),"
" coalesce ((CASE WHEN results.severity"
" > " G_STRINGIFY
(SEVERITY_LOG)
" THEN CAST (nvts.cvss_base"
" AS double precision)"
" ELSE results.severity"
" END),"
" results.severity))";
else
lateral
= "coalesce ((CASE WHEN results.severity"
" > " G_STRINGIFY (SEVERITY_LOG)
" THEN CAST (nvts.cvss_base"
" AS double precision)"
" ELSE results.severity"
" END),"
" results.severity)";
}
else
{
if (apply_overrides)
lateral
= "coalesce ((SELECT new_severity FROM valid_overrides"
" WHERE valid_overrides.result_nvt"
" = results.result_nvt"
" AND (valid_overrides.result = 0"
" OR valid_overrides.result"
" = results.id)"
" AND (valid_overrides.hosts is NULL"
" OR valid_overrides.hosts = ''"
" OR hosts_contains"
" (valid_overrides.hosts,"
" results.host))"
" AND (valid_overrides.port is NULL"
" OR valid_overrides.port = ''"
" OR valid_overrides.port"
" = results.port)"
" AND severity_matches_ov"
" (results.severity,"
" valid_overrides.severity)"
" LIMIT 1),"
" results.severity)";
else
lateral
/* coalesce because results.severity gives syntax error. */
= "coalesce (results.severity, results.severity)";
}
columns[0].select = "lateral_severity";
columns[0].filter = "severity";
columns[0].type = KEYWORD_TYPE_DOUBLE;
columns[1].select = NULL;
columns[1].filter = NULL;
columns[1].type = KEYWORD_TYPE_UNKNOWN;
opts = result_iterator_opts_table (apply_overrides,
dynamic_severity);
if (dynamic_severity)
extra_tables = g_strdup_printf (" LEFT OUTER JOIN nvts"
" ON results.nvt = nvts.oid,"
" LATERAL %s AS lateral_severity%s",
lateral, opts);
else
extra_tables = g_strdup_printf (", LATERAL %s AS lateral_severity%s",
lateral, opts);
g_free (opts);
extra_where = results_extra_where (get->trash, report, host,
apply_overrides, dynamic_severity,
filter ? filter : get->filter,
"lateral_severity");
extra_where_single = results_extra_where (get->trash, report, host,
apply_overrides,
dynamic_severity,
"min_qod=0",
"lateral_severity");
free (filter);
if (apply_overrides)
{
gchar *owned_clause, *overrides_with;
char *user_id;
user_id = sql_string ("SELECT id FROM users WHERE uuid = '%s';",
current_credentials.uuid);
// Do not get ACL with_clause as it will be added by
// init_get_iterator2_with.
owned_clause = acl_where_owned_for_get ("override", user_id,
"valid_overrides_",
&overrides_with);
free (user_id);
with_clause = g_strdup_printf
(" %s,"
" valid_overrides"
" AS (SELECT result_nvt, hosts, new_severity, port,"
" severity, result"
" FROM overrides"
" WHERE %s"
/* Only use if override's NVT is in report. */
" AND EXISTS (SELECT * FROM result_nvt_reports"
" WHERE report = %llu"
" AND result_nvt"
" = overrides.result_nvt)"
" AND (task = 0"
" OR task = (SELECT reports.task"
" FROM reports"
" WHERE reports.id = %llu))"
" AND ((end_time = 0) OR (end_time >= m_now ()))"
" ORDER BY result DESC, task DESC, port DESC,"
" severity ASC, creation_time DESC)"
" ",
overrides_with + strlen ("WITH "),
owned_clause,
report,
report);
g_free (overrides_with);
g_free (owned_clause);
}
else
with_clause = NULL;
table_order_if_sort_not_specified = 1;
ret = init_get_iterator2_with (iterator,
"result",
get,
/* SELECT columns. */
columns,
NULL,
/* Filterable columns not in SELECT columns. */
filterable_columns,
NULL,
filter_columns,
0,
extra_tables,
extra_where,
extra_where_single,
TRUE,
report ? TRUE : FALSE,
extra_order,
with_clause,
1,
1);
table_order_if_sort_not_specified = 0;
column_array_free (filterable_columns);
g_free (with_clause);
g_free (extra_tables);
g_free (extra_where);
g_free (extra_where_single);
return ret;
}
/**
* @brief SQL for getting current severity.
*/
#define CURRENT_SEVERITY_SQL \
"coalesce ((CASE WHEN results.severity > " G_STRINGIFY (SEVERITY_LOG) \
" THEN CAST (nvts.cvss_base AS double precision)" \
" ELSE results.severity" \
" END)," \
" results.severity)"
/**
* @brief Get LATERAL clause for result iterator.
*
* @param[in] apply_overrides Whether to apply overrides.
* @param[in] dynamic_severity Whether to use dynamic severity.
*
* @return SQL clause for FROM.
*/
static const gchar *
result_iterator_lateral (int apply_overrides, int dynamic_severity)
{
if (apply_overrides && dynamic_severity)
/* Overrides, dynamic. */
return "(WITH curr AS (SELECT " CURRENT_SEVERITY_SQL " AS curr_severity)"
" SELECT coalesce ((SELECT ov_new_severity FROM result_overrides"
" WHERE result = results.id"
" AND result_overrides.user = opts.user_id"
" AND severity_matches_ov"
" ((SELECT curr_severity FROM curr LIMIT 1),"
" ov_old_severity)"
" LIMIT 1),"
" (SELECT curr_severity FROM curr LIMIT 1))"
" AS new_severity)";
if (apply_overrides)
/* Overrides, no dynamic. */
return "(SELECT new_severity"
" FROM result_new_severities_static"
" WHERE result_new_severities_static.result = results.id"
" AND result_new_severities_static.user = opts.user_id"
" LIMIT 1)";
if (dynamic_severity)
/* No overrides, dynamic. */
return "(SELECT " CURRENT_SEVERITY_SQL " AS new_severity)";
/* No overrides, no dynamic.
*
* SELECT because results.severity gives syntax error. */
return "(SELECT results.severity AS new_severity)";
}
/**
* @brief Initialise a result iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
* @param[in] report Report to restrict returned results to.
* @param[in] host Host to limit results to.
* @param[in] extra_order Extra text for ORDER term in SQL.
*
* @return 0 success, 1 failed to find result, 2 failed to find filter (filt_id),
* -1 error.
*/
int
init_result_get_iterator (iterator_t* iterator, const get_data_t *get,
report_t report, const char* host,
const gchar *extra_order)
{
static const char *filter_columns[] = RESULT_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = RESULT_ITERATOR_COLUMNS;
static column_t columns_no_cert[] = RESULT_ITERATOR_COLUMNS_NO_CERT;
int ret;
gchar *filter, *extra_tables, *extra_where, *extra_where_single, *opts_tables;
int apply_overrides, dynamic_severity;
column_t *actual_columns;
g_debug ("%s", __func__);
if (report == -1)
{
init_iterator (iterator, "SELECT NULL WHERE false;");
return 0;
}
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
apply_overrides
= filter_term_apply_overrides (filter ? filter : get->filter);
dynamic_severity = setting_dynamic_severity_int ();
if (manage_cert_loaded ())
actual_columns = columns;
else
actual_columns = columns_no_cert;
opts_tables = result_iterator_opts_table (apply_overrides, dynamic_severity);
extra_tables = g_strdup_printf (" LEFT OUTER JOIN nvts"
" ON results.nvt = nvts.oid %s,"
" LATERAL %s AS lateral_new_severity",
opts_tables,
result_iterator_lateral (apply_overrides,
dynamic_severity));
g_free (opts_tables);
extra_where = results_extra_where (get->trash, report, host,
apply_overrides, dynamic_severity,
filter ? filter : get->filter,
NULL);
extra_where_single = results_extra_where (get->trash, report, host,
apply_overrides,
dynamic_severity,
"min_qod=0",
NULL);
free (filter);
ret = init_get_iterator2 (iterator,
"result",
get,
/* SELECT columns. */
actual_columns,
NULL,
/* Filterable columns not in SELECT columns. */
NULL,
NULL,
filter_columns,
0,
extra_tables,
extra_where,
extra_where_single,
TRUE,
report ? TRUE : FALSE,
extra_order);
g_free (extra_tables);
g_free (extra_where);
g_free (extra_where_single);
g_debug ("%s: done", __func__);
return ret;
}
/**
* @brief Count the number of results.
*
* @param[in] get GET params.
* @param[in] report Report to limit results to.
* @param[in] host Host to limit results to.
*
* @return Total number of results in filtered set.
*/
int
result_count (const get_data_t *get, report_t report, const char* host)
{
static const char *filter_columns[] = RESULT_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = RESULT_ITERATOR_COLUMNS;
static column_t columns_no_cert[] = RESULT_ITERATOR_COLUMNS_NO_CERT;
int ret;
gchar *filter, *extra_tables, *extra_where, *opts_tables;
int apply_overrides, dynamic_severity;
if (report == -1)
return 0;
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
apply_overrides
= filter_term_apply_overrides (filter ? filter : get->filter);
dynamic_severity = setting_dynamic_severity_int ();
opts_tables = result_iterator_opts_table (apply_overrides, dynamic_severity);
extra_tables = g_strdup_printf (" LEFT OUTER JOIN nvts"
" ON results.nvt = nvts.oid %s,"
" LATERAL %s AS lateral_new_severity",
opts_tables,
result_iterator_lateral (apply_overrides,
dynamic_severity));
g_free (opts_tables);
extra_where = results_extra_where (get->trash, report, host,
apply_overrides, dynamic_severity,
filter ? filter : get->filter,
NULL);
ret = count ("result", get,
manage_cert_loaded () ? columns : columns_no_cert,
manage_cert_loaded () ? columns : columns_no_cert,
filter_columns, 0,
extra_tables,
extra_where,
TRUE);
g_free (extra_tables);
g_free (extra_where);
return ret;
}
/**
* @brief Get the result from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The result.
*/
result_t
result_iterator_result (iterator_t* iterator)
{
if (iterator->done) return 0;
return (result_t) iterator_int64 (iterator, 0);
}
/**
* @brief Get the host from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The host of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_host, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the port from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The port of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_port, GET_ITERATOR_COLUMN_COUNT + 1);
/**
* @brief Get the NVT OID from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The NVT OID of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_nvt_oid, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get the original type from a result iterator.
*
* This is the column 'type'.
*
* @param[in] iterator Iterator.
*
* @return The original type of the result. Caller must only use before calling
* cleanup_iterator.
*/
static
DEF_ACCESS (result_iterator_original_type, GET_ITERATOR_COLUMN_COUNT + 3);
/**
* @brief Get the type from a result iterator.
*
* This is the overridden type.
*
* @param[in] iterator Iterator.
*
* @return The type of the result. Caller must only use before calling
* cleanup_iterator.
*/
static const char*
result_iterator_type (iterator_t *iterator)
{
if (iterator->done) return NULL;
/* new_type */
return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
}
/**
* @brief Get the descr from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The descr of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_descr, GET_ITERATOR_COLUMN_COUNT + 5);
/**
* @brief Get the task from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The task associated with the result, or 0 on error.
*/
task_t
result_iterator_task (iterator_t* iterator)
{
if (iterator->done) return 0;
return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
}
/**
* @brief Get the report from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The report associated with the result, or 0 on error.
*/
report_t
result_iterator_report (iterator_t* iterator)
{
if (iterator->done) return 0;
return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
}
/**
* @brief Get the NVT CVSS base value from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The CVSS base of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_cvss_base, GET_ITERATOR_COLUMN_COUNT + 8);
/**
* @brief Get the NVT version used during the scan from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The version of NVT used by the scan that produced the result.
* Caller must only use before calling cleanup_iterator.
*/
const char*
result_iterator_scan_nvt_version (iterator_t *iterator)
{
const char* ret;
if (iterator->done)
return NULL;
/* nvt_version */
ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
return ret ? ret : "";
}
/**
* @brief Get the original severity from a result iterator.
*
* This is the original severity without overrides.
*
* @param[in] iterator Iterator.
*
* @return The original severity of the result. Caller must only use before
* calling cleanup_iterator.
*/
const char*
result_iterator_original_severity (iterator_t *iterator)
{
const char* ret;
if (iterator->done)
return NULL;
/* severity */
ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 10);
return ret ? ret : "";
}
/**
* @brief Get the original severity/threat level from a result iterator.
*
* This is the original level without overrides.
*
* @param[in] iterator Iterator.
*
* @return The original threat level of the result. Caller must only use before
* calling cleanup_iterator.
*/
const char*
result_iterator_original_level (iterator_t *iterator)
{
double severity;
const char* ret;
if (iterator->done)
return NULL;
if (iterator_null (iterator, GET_ITERATOR_COLUMN_COUNT + 10))
return NULL;
/* severity */
severity = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 10);
ret = severity_to_level (severity, 0);
return ret ? ret : "";
}
/**
* @brief Get the severity from a result iterator.
*
* This is the the overridden severity.
*
* @param[in] iterator Iterator.
*
* @return The severity of the result. Caller must only use before calling
* cleanup_iterator.
*/
const char*
result_iterator_severity (iterator_t *iterator)
{
const char* ret;
if (iterator->done)
return NULL;
/* new_severity */
ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
return ret ? ret : "";
}
/**
* @brief Get the severity from a result iterator as double.
*
* This is the the overridden severity.
*
* @param[in] iterator Iterator.
*
* @return The severity of the result. Caller must only use before calling
* cleanup_iterator.
*/
double
result_iterator_severity_double (iterator_t *iterator)
{
if (iterator->done)
return 0.0;
return iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
}
/**
* @brief Get the severity/threat level from a result iterator.
*
* This is the the overridden level.
*
* @param[in] iterator Iterator.
*
* @return The threat level of the result. Caller must only use before
* calling cleanup_iterator.
*/
const char*
result_iterator_level (iterator_t *iterator)
{
double severity;
const char* ret;
if (iterator->done)
return "";
/* new_severity */
if (iterator_null (iterator, GET_ITERATOR_COLUMN_COUNT + 11))
return "";
severity = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
ret = severity_to_level (severity, 0);
return ret ? ret : "";
}
/**
* @brief Get the solution type from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The solution type of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_solution_type, GET_ITERATOR_COLUMN_COUNT + 15);
/**
* @brief Get the qod from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The qod of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_qod, GET_ITERATOR_COLUMN_COUNT + 16);
/**
* @brief Get the qod_type from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The qod type of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_qod_type, GET_ITERATOR_COLUMN_COUNT + 17);
/**
* @brief Get the host from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The host of the result. Caller must only use before calling
* cleanup_iterator.
*/
DEF_ACCESS (result_iterator_hostname, GET_ITERATOR_COLUMN_COUNT + 18);
/**
* @brief Get the path from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The path of the result. Caller must only use before
* calling cleanup_iterator.
*/
DEF_ACCESS (result_iterator_path, GET_ITERATOR_COLUMN_COUNT + 21);
/**
* @brief Get the asset host ID from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The ID of the asset host. Caller must only use before
* calling cleanup_iterator.
*/
DEF_ACCESS (result_iterator_asset_host_id, GET_ITERATOR_COLUMN_COUNT + 22);
/**
* @brief Get whether notes may exist from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if notes may exist, else 0.
*/
int
result_iterator_may_have_notes (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 23);
}
/**
* @brief Get whether overrides may exist from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if overrides may exist, else 0.
*/
int
result_iterator_may_have_overrides (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 24);
}
/**
* @brief Get whether tickets may exist from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if notes may exist, else 0.
*/
int
result_iterator_may_have_tickets (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 25);
}
/**
* @brief Get the NVT summary from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The summary of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_summary, GET_ITERATOR_COLUMN_COUNT + 27);
/**
* @brief Get the NVT insight from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The insight of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_insight, GET_ITERATOR_COLUMN_COUNT + 28);
/**
* @brief Get the NVT affected from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The affected of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_affected, GET_ITERATOR_COLUMN_COUNT + 29);
/**
* @brief Get the NVT impact from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return Impact text of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_impact, GET_ITERATOR_COLUMN_COUNT + 30);
/**
* @brief Get the NVT solution from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The solution of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_solution, GET_ITERATOR_COLUMN_COUNT + 31);
/**
* @brief Get the NVT detection from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The detection of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_detection, GET_ITERATOR_COLUMN_COUNT + 32);
/**
* @brief Get the NVT family from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The family of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_family, GET_ITERATOR_COLUMN_COUNT + 33);
/**
* @brief Get the NVT tags from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The tags of the NVT that produced the result, or NULL on error.
*/
DEF_ACCESS (result_iterator_nvt_tag, GET_ITERATOR_COLUMN_COUNT + 34);
/**
* @brief Get CERT-BUNDs from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return CERT-BUND names if any, else NULL.
*/
gchar **
result_iterator_cert_bunds (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_array (iterator, GET_ITERATOR_COLUMN_COUNT + 35);
}
/**
* @brief Get DFN-CERTs from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return DFN-CERT names if any, else NULL.
*/
gchar **
result_iterator_dfn_certs (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_array (iterator, GET_ITERATOR_COLUMN_COUNT + 36);
}
/**
* @brief Get the NVT name from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the NVT that produced the result, or NULL on error.
*/
const char*
result_iterator_nvt_name (iterator_t *iterator)
{
return get_iterator_name (iterator);
}
/**
* @brief Get the NVT solution_type from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The solution_type of the NVT that produced the result,
* or NULL on error.
*/
const char*
result_iterator_nvt_solution_type (iterator_t *iterator)
{
return result_iterator_solution_type (iterator);
}
/**
* @brief Get the NVT solution_method from a result iterator.
*
* @param[in] iterator Iterator.
*
* @return The solution_method of the NVT that produced the result,
* or NULL on error.
*/
const char*
result_iterator_nvt_solution_method (iterator_t *iterator)
{
/* When we used a cache this was never added to the cache. */
return NULL;
}
/**
* @brief Append an NVT's references to an XML string buffer.
*
* @param[in] xml The buffer where to append to.
* @param[in] oid The oid of the nvti object from where to collect the refs.
* @param[in] first Marker for first element.
*/
void
xml_append_nvt_refs (GString *xml, const char *oid, int *first)
{
nvti_t *nvti = lookup_nvti (oid);
int i;
if (!nvti)
return;
for (i = 0; i < nvti_vtref_len (nvti); i++)
{
vtref_t *ref;
if (first && *first)
{
xml_string_append (xml, "");
*first = 0;
}
ref = nvti_vtref (nvti, i);
xml_string_append (xml, "[", vtref_type (ref), vtref_id (ref));
}
}
/**
* @brief Check if the result_nvts are assigned to result
*
* @return 0 success, -1 error
*/
int
cleanup_result_nvts ()
{
iterator_t affected_iter;
GArray *affected;
int index;
g_debug ("%s: Cleaning up results with wrong nvt ids", __func__);
sql ("UPDATE results"
" SET nvt = (SELECT oid FROM nvts WHERE name = nvt),"
" result_nvt = NULL"
" WHERE nvt IN (SELECT name FROM nvts WHERE name != oid);");
g_debug ("%s: Cleaning up result_nvts entries with wrong nvt ids",
__func__);
sql ("DELETE FROM result_nvts"
" WHERE nvt IN (SELECT name FROM nvts WHERE name != oid);");
g_debug ("%s: Creating missing result_nvts entries", __func__);
sql ("INSERT INTO result_nvts (nvt)"
" SELECT DISTINCT nvt FROM results ON CONFLICT (nvt) DO NOTHING;");
// Get affected reports with overrides
affected = g_array_new (TRUE, TRUE, sizeof (report_t));
init_iterator (&affected_iter,
"SELECT DISTINCT report FROM results"
" WHERE (result_nvt IS NULL"
" OR report NOT IN"
" (SELECT report FROM result_nvt_reports"
" WHERE result_nvt IS NOT NULL))"
" AND nvt IN (SELECT nvt FROM overrides);");
while (next (&affected_iter))
{
report_t report;
report = iterator_int64 (&affected_iter, 0);
g_array_append_val (affected, report);
}
cleanup_iterator(&affected_iter);
g_debug ("%s: Adding missing result_nvt values to results", __func__);
sql ("UPDATE results"
" SET result_nvt"
" = (SELECT id FROM result_nvts"
" WHERE result_nvts.nvt = results.nvt)"
" WHERE result_nvt IS NULL");
g_debug ("%s: Cleaning up NULL result_nvt_reports entries", __func__);
sql ("DELETE FROM result_nvt_reports WHERE result_nvt IS NULL;");
g_debug ("%s: Adding missing result_nvt_reports entries", __func__);
sql ("INSERT INTO result_nvt_reports (report, result_nvt)"
" SELECT DISTINCT report, result_nvts.id FROM results"
" JOIN result_nvts ON result_nvts.nvt = results.nvt"
" WHERE report NOT IN (SELECT report FROM result_nvt_reports"
" WHERE result_nvt IS NOT NULL)");
// Re-cache affected reports with overrides
for (index = 0; index < affected->len; index++)
{
report_t report;
report = g_array_index (affected, report_t, index);
g_debug ("%s: Updating cache of affected report %llu",
__func__, report);
report_cache_counts (report, 0, 1, NULL);
}
g_array_free (affected, TRUE);
return 0;
}
/**
* @brief Initialise a host iterator.
*
* @param[in] iterator Iterator.
* @param[in] report Report whose hosts the iterator loops over.
* @param[in] host Single host to iterate over. All hosts if NULL.
* @param[in] report_host Single report host to iterate over. All if 0.
*/
void
init_report_host_iterator (iterator_t* iterator, report_t report, const char *host,
report_host_t report_host)
{
if (report)
{
if (report_host)
init_iterator (iterator,
"SELECT id, host, iso_time (start_time),"
" iso_time (end_time), current_port, max_port, report,"
" (SELECT uuid FROM reports WHERE id = report),"
" (SELECT uuid FROM hosts"
" WHERE id = (SELECT host FROM host_identifiers"
" WHERE source_type = 'Report Host'"
" AND name = 'ip'"
" AND source_id = (SELECT uuid"
" FROM reports"
" WHERE id = report)"
" AND value = report_hosts.host"
" LIMIT 1))"
" FROM report_hosts WHERE id = %llu"
" AND report = %llu"
"%s%s%s"
" ORDER BY order_inet (host);",
report_host,
report,
host ? " AND host = '" : "",
host ? host : "",
host ? "'" : "");
else
init_iterator (iterator,
"SELECT id, host, iso_time (start_time),"
" iso_time (end_time), current_port, max_port, report,"
" (SELECT uuid FROM reports WHERE id = report),"
" (SELECT uuid FROM hosts"
" WHERE id = (SELECT host FROM host_identifiers"
" WHERE source_type = 'Report Host'"
" AND name = 'ip'"
" AND source_id = (SELECT uuid"
" FROM reports"
" WHERE id = report)"
" AND value = report_hosts.host"
" LIMIT 1))"
" FROM report_hosts WHERE report = %llu"
"%s%s%s"
" ORDER BY order_inet (host);",
report,
host ? " AND host = '" : "",
host ? host : "",
host ? "'" : "");
}
else
{
if (report_host)
init_iterator (iterator,
"SELECT id, host, iso_time (start_time),"
" iso_time (end_time), current_port, max_port, report,"
" (SELECT uuid FROM reports WHERE id = report),"
" ''"
" FROM report_hosts WHERE id = %llu"
"%s%s%s"
" ORDER BY order_inet (host);",
report_host,
host ? " AND host = '" : "",
host ? host : "",
host ? "'" : "");
else
init_iterator (iterator,
"SELECT id, host, iso_time (start_time),"
" iso_time (end_time), current_port, max_port, report,"
" (SELECT uuid FROM reports WHERE id = report),"
" ''"
" FROM report_hosts"
"%s%s%s"
" ORDER BY order_inet (host);",
host ? " WHERE host = '" : "",
host ? host : "",
host ? "'" : "");
}
}
/**
* @brief Get the report host from a host iterator.
*
* @param[in] iterator Iterator.
*
* @return Report host.
*/
static report_host_t
host_iterator_report_host (iterator_t* iterator)
{
if (iterator->done) return 0;
return (report_host_t) iterator_int64 (iterator, 0);
}
/**
* @brief Get the host from a host iterator.
*
* @param[in] iterator Iterator.
*
* @return The host of the host. Caller must use only before calling
* cleanup_iterator.
*/
DEF_ACCESS (host_iterator_host, 1);
/**
* @brief Get the start time from a host iterator.
*
* @param[in] iterator Iterator.
*
* @return The start time of the host. Caller must use only before calling
* cleanup_iterator.
*/
DEF_ACCESS (host_iterator_start_time, 2);
/**
* @brief Get the end time from a host iterator.
*
* @param[in] iterator Iterator.
*
* @return The end time of the host. Caller must use only before calling
* cleanup_iterator.
*/
DEF_ACCESS (host_iterator_end_time, 3);
/**
* @brief Get the current port from a host iterator.
*
* @param[in] iterator Iterator.
*
* @return Current port.
*/
int
host_iterator_current_port (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, 4);
return ret;
}
/**
* @brief Get the max port from a host iterator.
*
* @param[in] iterator Iterator.
*
* @return Current port.
*/
int
host_iterator_max_port (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, 5);
return ret;
}
/**
* @brief Get the asset UUID from a host iterator.
*
* @param[in] iterator Iterator.
*
* @return The UUID of the assset associate with the host. Caller must use
* only before calling cleanup_iterator.
*/
static
DEF_ACCESS (host_iterator_asset_uuid, 8);
/**
* @brief Initialise a report errors iterator.
*
* @param[in] iterator Iterator.
* @param[in] report The report.
*/
void
init_report_errors_iterator (iterator_t* iterator, report_t report)
{
if (report)
init_iterator (iterator,
"SELECT results.host, results.port, results.nvt,"
" results.description,"
" coalesce((SELECT name FROM nvts"
" WHERE nvts.oid = results.nvt), ''),"
" coalesce((SELECT cvss_base FROM nvts"
" WHERE nvts.oid = results.nvt), ''),"
" results.nvt_version, results.severity,"
" results.id"
" FROM results"
" WHERE results.type = 'Error Message'"
" AND results.report = %llu",
report);
}
/**
* @brief Get the host from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The host of the report error message. Caller must use only before
* calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_host, 0);
/**
* @brief Get the port from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The port of the report error message. Caller must use only before
* calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_port, 1);
/**
* @brief Get the nvt oid from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The nvt of the report error message. Caller must use only before
* calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_nvt_oid, 2);
/**
* @brief Get the description from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The description of the report error message. Caller must use only
* before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_desc, 3);
/**
* @brief Get the nvt name from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The nvt of the report error message. Caller must use only before
* calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_nvt_name, 4);
/**
* @brief Get the nvt cvss base from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The nvt cvss base of the report error message. Caller must use only
* before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_nvt_cvss, 5);
/**
* @brief Get the nvt cvss base from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The nvt version at scan time of the report error message.
* Caller must use only before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_scan_nvt_version, 6);
/**
* @brief Get the nvt cvss base from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity at scan time of the report error message.
* Caller must use only before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_errors_iterator_severity, 7);
/**
* @brief Get the result from a report error messages iterator.
*
* @param[in] iterator Iterator.
*
* @return Result.
*/
static result_t
report_errors_iterator_result (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, 8);
}
/**
* @brief Initialise a report host details iterator.
*
* @param[in] iterator Iterator.
* @param[in] report_host Report host whose details the iterator loops over.
* All report_hosts if NULL.
*/
static void
init_report_host_details_iterator (iterator_t* iterator,
report_host_t report_host)
{
/* The 'detected_at' and 'detected_by' entries are filtered out of the final
* reports as they are only used internally for product detection. */
init_iterator (iterator,
"SELECT id, name, value, source_type, source_name,"
" source_description, NULL"
" FROM report_host_details WHERE report_host = %llu"
" AND NOT name IN ('detected_at', 'detected_by')"
" AND NOT name LIKE 'detected_by@%%'"
" UNION SELECT 0, 'Closed CVE', cve, 'openvasmd', oid,"
" nvts.name, cvss_base"
" FROM nvts, report_host_details"
" WHERE cve != ''"
" AND family IN (" LSC_FAMILY_LIST ")"
" AND nvts.oid = report_host_details.source_name"
" AND report_host = %llu"
" AND report_host_details.name = 'EXIT_CODE'"
" AND report_host_details.value = 'EXIT_NOTVULN';",
report_host,
report_host);
}
/**
* @brief Get the name from a report host details iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the report host detail. Caller must use only before
* calling cleanup_iterator.
*/
static
DEF_ACCESS (report_host_details_iterator_name, 1);
/**
* @brief Get the value from a report host details iterator.
*
* @param[in] iterator Iterator.
*
* @return The value of the report host detail. Caller must use only before
* calling cleanup_iterator.
*/
static
DEF_ACCESS (report_host_details_iterator_value, 2);
/**
* @brief Get the source type from a report host details iterator.
*
* @param[in] iterator Iterator.
*
* @return The source type of the report host detail. Caller must use only
* before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_host_details_iterator_source_type, 3);
/**
* @brief Get the source name from a report host details iterator.
*
* @param[in] iterator Iterator.
*
* @return The source name of the report host detail. Caller must use only
* before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_host_details_iterator_source_name, 4);
/**
* @brief Get the source description from a report host details iterator.
*
* @param[in] iterator Iterator.
*
* @return The source description of the report host detail. Caller must use
* only before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_host_details_iterator_source_desc, 5);
/**
* @brief Get the extra info from a report host details iterator.
*
* @param[in] iterator Iterator.
*
* @return Extra info of the report host detail. Caller must use
* only before calling cleanup_iterator.
*/
static
DEF_ACCESS (report_host_details_iterator_extra, 6);
/**
* @brief Set the end time of a task.
*
* @param[in] task Task.
* @param[in] time New time. Freed before return. If NULL, clear end time.
*/
void
set_task_end_time (task_t task, char* time)
{
if (time)
{
sql ("UPDATE tasks SET end_time = %i WHERE id = %llu;",
parse_iso_time (time),
task);
free (time);
}
else
sql ("UPDATE tasks SET end_time = NULL WHERE id = %llu;",
task);
}
/**
* @brief Set the end time of a task.
*
* @param[in] task Task.
* @param[in] time New time. Freed before return. If NULL, clear end time.
*/
void
set_task_end_time_epoch (task_t task, time_t time)
{
if (time)
sql ("UPDATE tasks SET end_time = %i WHERE id = %llu;", time, task);
else
sql ("UPDATE tasks SET end_time = NULL WHERE id = %llu;", task);
}
/**
* @brief Get the start time of a scan.
*
* @param[in] report The report associated with the scan.
*
* @return Start time of scan, in a newly allocated string.
*/
static char*
scan_start_time (report_t report)
{
char *time = sql_string ("SELECT iso_time (start_time)"
" FROM reports WHERE id = %llu;",
report);
return time ? time : g_strdup ("");
}
/**
* @brief Get the start time of a scan, in seconds since the epoch.
*
* @param[in] report The report associated with the scan.
*
* @return Start time of scan, in seconds.
*/
int
scan_start_time_epoch (report_t report)
{
return sql_int ("SELECT start_time FROM reports WHERE id = %llu;",
report);
}
/**
* @brief Get the start time of a scan.
*
* @param[in] uuid The report associated with the scan.
*
* @return Start time of scan, in a newly allocated string.
*/
char*
scan_start_time_uuid (const char *uuid)
{
char *time, *quoted_uuid;
quoted_uuid = sql_quote (uuid);
time = sql_string ("SELECT iso_time (start_time)"
" FROM reports WHERE uuid = '%s';",
quoted_uuid);
return time ? time : g_strdup ("");
}
/**
* @brief Set the start time of a scan.
*
* @param[in] report The report associated with the scan.
* @param[in] timestamp Start time. Epoch format.
*/
void
set_scan_start_time_epoch (report_t report, time_t timestamp)
{
sql ("UPDATE reports SET start_time = %i WHERE id = %llu;",
timestamp, report);
}
/**
* @brief Set the start time of a scan.
*
* @param[in] report The report associated with the scan.
* @param[in] timestamp Start time. In UTC ctime format.
*/
void
set_scan_start_time_ctime (report_t report, const char* timestamp)
{
sql ("UPDATE reports SET start_time = %i WHERE id = %llu;",
parse_utc_ctime (timestamp),
report);
}
/**
* @brief Get the end time of a scan.
*
* @param[in] report The report associated with the scan.
*
* @return End time of scan, in a newly allocated string.
*/
static char*
scan_end_time (report_t report)
{
char *time = sql_string ("SELECT iso_time (end_time)"
" FROM reports WHERE id = %llu;",
report);
return time ? time : g_strdup ("");
}
/**
* @brief Get the end time of a scan.
*
* @param[in] uuid The report associated with the scan.
*
* @return End time of scan, in a newly allocated string.
*/
char*
scan_end_time_uuid (const char *uuid)
{
char *time, *quoted_uuid;
quoted_uuid = sql_quote (uuid);
time = sql_string ("SELECT iso_time (end_time)"
" FROM reports WHERE uuid = '%s';",
quoted_uuid);
return time ? time : g_strdup ("");
}
/**
* @brief Set the end time of a scan.
*
* @param[in] report The report associated with the scan.
* @param[in] timestamp End time. Epoch format.
*/
void
set_scan_end_time_epoch (report_t report, time_t timestamp)
{
if (timestamp)
sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
timestamp, report);
}
/**
* @brief Set the end time of a scan.
*
* @param[in] report The report associated with the scan.
* @param[in] timestamp End time. ISO format. If NULL, clear end time.
*/
void
set_scan_end_time (report_t report, const char* timestamp)
{
if (timestamp)
sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
parse_iso_time (timestamp), report);
else
sql ("UPDATE reports SET end_time = NULL WHERE id = %llu;",
report);
}
/**
* @brief Set the end time of a scan.
*
* @param[in] report The report associated with the scan.
* @param[in] timestamp End time. In UTC ctime format. If NULL, clear end
* time.
*/
void
set_scan_end_time_ctime (report_t report, const char* timestamp)
{
if (timestamp)
sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
parse_utc_ctime (timestamp), report);
else
sql ("UPDATE reports SET end_time = NULL WHERE id = %llu;",
report);
}
/**
* @brief Get the end time of a scanned host.
*
* @param[in] report Report associated with the scan.
* @param[in] host Host.
*
* @return End time.
*/
int
scan_host_end_time (report_t report, const char* host)
{
gchar *quoted_host;
int ret;
quoted_host = sql_quote (host);
ret = sql_int ("SELECT end_time FROM report_hosts"
" WHERE report = %llu AND host = '%s';",
report, quoted_host);
g_free (quoted_host);
return ret;
}
/**
* @brief Set the end time of a scanned host.
*
* @param[in] report Report associated with the scan.
* @param[in] host Host.
* @param[in] timestamp End time. ISO format.
*/
void
set_scan_host_end_time (report_t report, const char* host,
const char* timestamp)
{
gchar *quoted_host;
quoted_host = sql_quote (host);
if (sql_int ("SELECT COUNT(*) FROM report_hosts"
" WHERE report = %llu AND host = '%s';",
report, quoted_host))
sql ("UPDATE report_hosts SET end_time = %i"
" WHERE report = %llu AND host = '%s';",
parse_iso_time (timestamp), report, quoted_host);
else
manage_report_host_add (report, host, 0, parse_iso_time (timestamp));
g_free (quoted_host);
}
/**
* @brief Set the end time of a scanned host.
*
* @param[in] report Report associated with the scan.
* @param[in] host Host.
* @param[in] timestamp End time. In UTC ctime format.
*/
void
set_scan_host_end_time_ctime (report_t report, const char* host,
const char* timestamp)
{
gchar *quoted_host;
quoted_host = sql_quote (host);
if (sql_int ("SELECT COUNT(*) FROM report_hosts"
" WHERE report = %llu AND host = '%s';",
report, quoted_host))
sql ("UPDATE report_hosts SET end_time = %i"
" WHERE report = %llu AND host = '%s';",
parse_utc_ctime (timestamp), report, quoted_host);
else
manage_report_host_add (report, host, 0, parse_utc_ctime (timestamp));
g_free (quoted_host);
}
/**
* @brief Set the start time of a scanned host.
*
* @param[in] report Report associated with the scan.
* @param[in] host Host.
* @param[in] timestamp Start time. In UTC ctime format.
*/
void
set_scan_host_start_time_ctime (report_t report, const char* host,
const char* timestamp)
{
gchar *quoted_host;
quoted_host = sql_quote (host);
if (sql_int ("SELECT COUNT(*) FROM report_hosts"
" WHERE report = %llu AND host = '%s';",
report, quoted_host))
sql ("UPDATE report_hosts SET start_time = %i"
" WHERE report = %llu AND host = '%s';",
parse_utc_ctime (timestamp), report, quoted_host);
else
manage_report_host_add (report, host, parse_utc_ctime (timestamp), 0);
g_free (quoted_host);
}
/**
* @brief Get the timestamp of a report.
*
* @todo Lacks permission check. Caller contexts all have permission
* checks before calling this so it's safe. Rework callers so
* they pass report_t instead of UUID string.
*
* @param[in] report_id UUID of report.
* @param[out] timestamp Timestamp on success. Caller must free.
*
* @return 0 on success, -1 on error.
*/
int
report_timestamp (const char* report_id, gchar** timestamp)
{
const char* stamp;
time_t time = sql_int ("SELECT date FROM reports where uuid = '%s';",
report_id);
stamp = iso_time (&time);
if (stamp == NULL) return -1;
*timestamp = g_strdup (stamp);
return 0;
}
/**
* @brief Return the run status of the scan associated with a report.
*
* @param[in] report Report.
* @param[out] status Scan run status.
*
* @return 0 on success, -1 on error.
*/
static int
report_scan_run_status (report_t report, task_status_t* status)
{
*status = sql_int ("SELECT scan_run_status FROM reports"
" WHERE reports.id = %llu;",
report);
return 0;
}
/**
* @brief Return the run status of the scan associated with a report.
*
* @param[in] report Report.
* @param[out] status Scan run status.
*
* @return 0 on success, -1 on error.
*/
int
set_report_scan_run_status (report_t report, task_status_t status)
{
sql ("UPDATE reports SET scan_run_status = %u WHERE id = %llu;",
status,
report);
if (setting_auto_cache_rebuild_int ())
report_cache_counts (report, 0, 0, NULL);
return 0;
}
/**
* @brief Get the result severity counts for a report.
*
* @param[in] report Report.
* @param[in] host Host to which to limit the count. NULL to allow all.
* @param[in] get Report "get" data to retrieve filter info from.
* @param[out] severity_data The severity data struct to store counts in.
* @param[out] filtered_severity_data The severity data struct to store counts in.
*/
static void
report_severity_data (report_t report, const char *host,
const get_data_t* get,
severity_data_t* severity_data,
severity_data_t* filtered_severity_data)
{
iterator_t results;
gchar *filter;
int apply_overrides;
if (report == 0)
return;
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
}
else
filter = NULL;
apply_overrides
= filter_term_apply_overrides (filter ? filter : get->filter);
if (severity_data)
{
get_data_t *get_all;
get_all = report_results_get_data (1, -1, apply_overrides, 0);
ignore_max_rows_per_page = 1;
init_result_get_iterator_severity (&results, get_all, report, host, NULL);
ignore_max_rows_per_page = 0;
while (next (&results))
{
double severity;
if (results.done)
severity = 0.0;
else
severity = iterator_double (&results, 0);
severity_data_add (severity_data, severity);
}
cleanup_iterator (&results);
get_data_reset (get_all);
free (get_all);
}
if (filtered_severity_data)
{
get_data_t get_filtered;
memset (&get_filtered, 0, sizeof (get_data_t));
get_filtered.filt_id = get->filt_id;
get_filtered.filter = get->filter;
get_filtered.type = get->type;
get_filtered.ignore_pagination = 1;
ignore_max_rows_per_page = 1;
init_result_get_iterator_severity (&results, &get_filtered, report, host,
NULL);
ignore_max_rows_per_page = 0;
while (next (&results))
{
double severity;
if (results.done)
severity = 0.0;
else
severity = iterator_double (&results, 0);
severity_data_add (filtered_severity_data, severity);
}
cleanup_iterator (&results);
}
}
/**
* @brief Get the message counts for a report given the UUID.
*
* @todo Lacks permission check. Caller contexts all have permission
* checks before calling this so it's safe. Rework callers to
* use report_counts_id instead.
*
* @param[in] report_id ID of report.
* @param[out] holes Number of hole messages.
* @param[out] infos Number of info messages.
* @param[out] logs Number of log messages.
* @param[out] warnings Number of warning messages.
* @param[out] false_positives Number of false positives.
* @param[out] severity Maximum severity score.
* @param[in] override Whether to override the threat.
* @param[in] min_qod Min QOD.
*
* @return 0 on success, -1 on error.
*/
int
report_counts (const char* report_id, int* holes, int* infos,
int* logs, int* warnings, int* false_positives, double* severity,
int override, int min_qod)
{
report_t report;
int ret;
get_data_t *get;
// TODO Wrap in transaction.
if (find_report_with_permission (report_id, &report, "get_reports"))
return -1;
// TODO Check if report was found.
get = report_results_get_data (1, -1, override, min_qod);
ret = report_counts_id (report, holes, infos, logs, warnings,
false_positives, severity, get, NULL);
get_data_reset (get);
free (get);
return ret;
}
/**
* @brief Test if a counts cache exists for a report and the current user.
* @param[in] report The report to check.
* @param[in] override Whether to check for overridden results.
* @param[in] min_qod Minimum QoD of results to count.
*
* @return 1 if cache exists, 0 otherwise.
*/
static int
report_counts_cache_exists (report_t report, int override, int min_qod)
{
return sql_int ("SELECT EXISTS (SELECT * FROM report_counts"
" WHERE report = %llu"
" AND override = %d"
" AND \"user\" = (SELECT id FROM users"
" WHERE users.uuid = '%s')"
" AND min_qod = %d"
" AND (end_time = 0 OR end_time >= m_now ()));",
report, override, current_credentials.uuid, min_qod);
}
/**
* @brief Get cached result counts for a report and the current user.
*
* @param[in] report The report to get counts from.
* @param[in] override Whether to get overridden results.
* @param[in] min_qod Minimum QoD of results to count.
* @param[out] data The severity_data_t to save counts in.
*/
static void
report_counts_from_cache (report_t report, int override, int min_qod,
severity_data_t* data)
{
iterator_t iterator;
init_iterator (&iterator,
"SELECT severity, count FROM report_counts"
" WHERE report = %llu"
" AND override = %i"
" AND \"user\" = (SELECT id FROM users"
" WHERE users.uuid = '%s')"
" AND min_qod = %d"
" AND (end_time = 0 OR end_time >= m_now ());",
report, override, current_credentials.uuid, min_qod);
while (next (&iterator))
{
severity_data_add_count (data,
iterator_double (&iterator, 0),
iterator_int (&iterator, 1));
}
cleanup_iterator (&iterator);
}
/**
* @brief Cache the message counts for a report.
*
* @param[in] report Report.
* @param[in] override Whether overrides were applied to the results.
* @param[in] min_qod The minimum QoD of the results.
* @param[in] data Severity data struct containing the message counts.
*
* @return 0 if successful, 1 gave up, -1 error (see sql_giveup).
*/
static int
cache_report_counts (report_t report, int override, int min_qod,
severity_data_t* data)
{
int i, ret;
double severity;
int end_time;
/* Try cache results. */
ret = sql_giveup ("DELETE FROM report_counts"
" WHERE report = %llu"
" AND override = %i"
" AND min_qod = %i"
" AND \"user\" = (SELECT id FROM users"
" WHERE users.uuid = '%s');",
report, override, min_qod, current_credentials.uuid);
if (ret)
{
return ret;
}
if (data->total == 0)
{
/* Create dummy entry for empty reports */
ret = sql_giveup ("INSERT INTO report_counts"
" (report, \"user\", override, min_qod, severity,"
" count, end_time)"
" VALUES (%llu,"
" (SELECT id FROM users"
" WHERE users.uuid = '%s'),"
" %d, %d, " G_STRINGIFY (SEVERITY_MISSING) ","
" 0, 0);",
report, current_credentials.uuid, override, min_qod);
if (ret)
{
return ret;
}
}
else
{
GString *insert;
int first;
i = 0;
if (override)
end_time = sql_int ("SELECT coalesce(min(end_time), 0)"
" FROM overrides, results"
" WHERE overrides.nvt = results.nvt"
" AND results.report = %llu"
" AND overrides.end_time >= m_now ();",
report);
else
end_time = 0;
severity = severity_data_value (i);
insert = g_string_new ("INSERT INTO report_counts"
" (report, \"user\", override, min_qod,"
" severity, count, end_time)"
" VALUES");
first = 1;
while (severity <= (data->max + (1.0
/ SEVERITY_SUBDIVISIONS
/ SEVERITY_SUBDIVISIONS))
&& severity != SEVERITY_MISSING)
{
if (data->counts[i] > 0)
{
g_string_append_printf (insert,
"%s (%llu,"
" (SELECT id FROM users"
" WHERE users.uuid = '%s'),"
" %d, %d, %1.1f, %d, %d)",
first == 1 ? "" : ",",
report, current_credentials.uuid,
override, min_qod, severity,
data->counts[i], end_time);
first = 0;
}
i++;
severity = severity_data_value (i);
}
if (i)
{
g_string_append_printf (insert, ";");
ret = sql_giveup (insert->str);
if (ret)
{
g_string_free (insert, TRUE);
return ret;
}
}
g_string_free (insert, TRUE);
}
return 0;
}
/**
* @brief Get the message counts for a report.
*
* @param[in] report Report.
* @param[out] holes Number of hole messages.
* @param[out] infos Number of info messages.
* @param[out] logs Number of log messages.
* @param[out] warnings Number of warning messages.
* @param[out] false_positives Number of false positive messages.
* @param[out] severity Maximum severity of the report.
* @param[in] get Get data.
* @param[in] host Host to which to limit the count.
* @param[out] filtered_holes Number of hole messages after filtering.
* @param[out] filtered_infos Number of info messages after filtering.
* @param[out] filtered_logs Number of log messages after filtering.
* @param[out] filtered_warnings Number of warning messages after filtering.
* @param[out] filtered_false_positives Number of false positive messages after
* filtering.
* @param[out] filtered_severity Maximum severity after filtering.
*
* @return 0 on success, -1 on error.
*/
static int
report_counts_id_full (report_t report, int* holes, int* infos,
int* logs, int* warnings, int* false_positives,
double* severity,
const get_data_t* get, const char* host,
int* filtered_holes,
int* filtered_infos, int* filtered_logs,
int* filtered_warnings, int* filtered_false_positives,
double* filtered_severity)
{
const char *filter;
keyword_t **point;
array_t *split;
int filter_cacheable, unfiltered_requested, filtered_requested, cache_exists;
int override, min_qod_int;
severity_data_t severity_data, filtered_severity_data;
unfiltered_requested = (holes || warnings || infos || logs || false_positives
|| severity);
filtered_requested = (filtered_holes || filtered_warnings || filtered_infos
|| filtered_logs || filtered_false_positives
|| filtered_severity);
if (current_credentials.uuid == NULL
|| strcmp (current_credentials.uuid, "") == 0)
g_warning ("%s: called by NULL or dummy user", __func__);
if (get->filt_id && strlen (get->filt_id)
&& strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
{
return -1;
}
}
else
{
filter = get->filter;
}
filter_cacheable = TRUE;
override = 0;
min_qod_int = MIN_QOD_DEFAULT;
if (filter == NULL)
filter = "";
split = split_filter (filter);
point = (keyword_t**) split->pdata;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column == NULL)
{
filter_cacheable = FALSE;
}
else if (strcasecmp (keyword->column, "first") == 0
|| strcasecmp (keyword->column, "rows") == 0
|| strcasecmp (keyword->column, "sort") == 0
|| strcasecmp (keyword->column, "sort-reverse") == 0
|| strcasecmp (keyword->column, "notes") == 0
|| strcasecmp (keyword->column, "overrides") == 0)
{
// ignore
}
else if (strcasecmp (keyword->column, "apply_overrides") == 0)
{
if (keyword->string
&& strcmp (keyword->string, "")
&& strcmp (keyword->string, "0"))
override = 1;
}
else if (strcasecmp (keyword->column, "min_qod") == 0)
{
if (keyword->string == NULL
|| sscanf (keyword->string, "%d", &min_qod_int) != 1)
min_qod_int = MIN_QOD_DEFAULT;
}
else
{
filter_cacheable = FALSE;
}
point++;
}
filter_free (split);
cache_exists = filter_cacheable
&& report_counts_cache_exists (report, override, min_qod_int);
init_severity_data (&severity_data);
init_severity_data (&filtered_severity_data);
if (cache_exists && filter_cacheable)
{
/* Get unfiltered counts from cache. */
if (unfiltered_requested)
report_counts_from_cache (report, override, min_qod_int,
&severity_data);
if (filtered_requested)
report_counts_from_cache (report, override, min_qod_int,
&filtered_severity_data);
}
else
{
/* Recalculate. */
report_severity_data (report, host, get,
unfiltered_requested
? &severity_data : NULL,
filtered_requested
? &filtered_severity_data : NULL);
}
severity_data_level_counts (&severity_data,
NULL, false_positives,
logs, infos, warnings, holes);
severity_data_level_counts (&filtered_severity_data,
NULL, filtered_false_positives,
filtered_logs, filtered_infos,
filtered_warnings, filtered_holes);
if (severity)
*severity = severity_data.max;
if (filtered_severity && filtered_requested)
*filtered_severity = filtered_severity_data.max;
if (filter_cacheable && !cache_exists)
{
if (unfiltered_requested)
cache_report_counts (report, override, 0, &severity_data);
if (filtered_requested)
cache_report_counts (report, override, min_qod_int,
&filtered_severity_data);
}
cleanup_severity_data (&severity_data);
cleanup_severity_data (&filtered_severity_data);
return 0;
}
/**
* @brief Get only the filtered message counts for a report.
*
* @param[in] report Report.
* @param[out] holes Number of hole messages.
* @param[out] infos Number of info messages.
* @param[out] logs Number of log messages.
* @param[out] warnings Number of warning messages.
* @param[out] false_positives Number of false positive messages.
* @param[out] severity Maximum severity score.
* @param[in] get Get data.
* @param[in] host Host to which to limit the count. NULL to allow all.
*
* @return 0 on success, -1 on error.
*/
int
report_counts_id (report_t report, int* holes, int* infos,
int* logs, int* warnings, int* false_positives,
double* severity, const get_data_t *get, const char *host)
{
int ret;
ret = report_counts_id_full (report, NULL, NULL, NULL, NULL, NULL, NULL,
get, host, holes, infos, logs, warnings,
false_positives, severity);
return ret;
}
/**
* @brief Get the maximum severity of a report.
*
* @param[in] report Report.
* @param[in] overrides Whether to apply overrides.
* @param[in] min_qod Minimum QoD of results to count.
*
* @return Severity score of the report.
*/
double
report_severity (report_t report, int overrides, int min_qod)
{
double severity;
iterator_t iterator;
init_iterator (&iterator,
"SELECT max(severity)"
" FROM report_counts"
" WHERE report = %llu"
" AND override = %d"
" AND \"user\" = (SELECT id FROM users WHERE uuid = '%s')"
" AND min_qod = %d"
" AND (end_time = 0 or end_time >= m_now ());",
report, overrides, current_credentials.uuid, min_qod);
if (next (&iterator)
&& (iterator_null (&iterator, 0) == 0))
{
g_debug ("%s: max(severity)=%s", __func__,
iterator_string (&iterator, 0));
severity = iterator_double (&iterator, 0);
}
else
{
g_debug ("%s: could not get max from cache", __func__);
get_data_t *get = report_results_get_data (1, -1, overrides, min_qod);
report_counts_id (report, NULL, NULL, NULL, NULL,
NULL, &severity, get, NULL);
get_data_reset (get);
free (get);
}
cleanup_iterator (&iterator);
return severity;
}
/**
* @brief Delete a report.
*
* It's up to the caller to provide the transaction.
*
* @param[in] report Report.
*
* @return 0 success, 2 report is in use, -1 error.
*/
int
delete_report_internal (report_t report)
{
task_t task;
if (sql_int ("SELECT count(*) FROM reports WHERE id = %llu"
" AND (scan_run_status = %u OR scan_run_status = %u"
" OR scan_run_status = %u OR scan_run_status = %u"
" OR scan_run_status = %u);",
report,
TASK_STATUS_RUNNING,
TASK_STATUS_QUEUED,
TASK_STATUS_REQUESTED,
TASK_STATUS_DELETE_REQUESTED,
TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
TASK_STATUS_STOP_REQUESTED,
TASK_STATUS_STOP_WAITING))
return 2;
/* This needs to have exclusive access to reports because otherwise at this
* point another process (like a RESUME_TASK handler) could store the report
* ID and then start trying to access that report after we've deleted it. */
if (report_task (report, &task))
return -1;
/* Remove the report data. */
sql ("DELETE FROM report_host_details WHERE report_host IN"
" (SELECT id FROM report_hosts WHERE report = %llu);",
report);
sql ("DELETE FROM report_hosts WHERE report = %llu;", report);
sql ("DELETE FROM tag_resources"
" WHERE resource_type = 'result'"
" AND resource IN"
" (SELECT id FROM results WHERE report = %llu);",
report);
sql ("DELETE FROM tag_resources_trash"
" WHERE resource_type = 'result'"
" AND resource IN"
" (SELECT id FROM results WHERE report = %llu);",
report);
sql ("DELETE FROM results WHERE report = %llu;", report);
sql ("DELETE FROM results_trash WHERE report = %llu;", report);
sql ("DELETE FROM tag_resources"
" WHERE resource_type = 'report'"
" AND resource = %llu;",
report);
sql ("DELETE FROM tag_resources_trash"
" WHERE resource_type = 'report'"
" AND resource = %llu;",
report);
sql ("DELETE FROM report_counts WHERE report = %llu;", report);
sql ("DELETE FROM result_nvt_reports WHERE report = %llu;", report);
sql ("DELETE FROM reports WHERE id = %llu;", report);
/* Adjust permissions. */
permissions_set_orphans ("report", report, LOCATION_TABLE);
tags_remove_resource ("report", report, LOCATION_TABLE);
tickets_remove_report (report);
/* Update the task state. */
switch (sql_int64 (&report,
"SELECT max (id) FROM reports WHERE task = %llu",
task))
{
case 0:
if (report)
{
task_status_t status;
if (report_scan_run_status (report, &status))
return -1;
sql ("UPDATE tasks SET run_status = %u WHERE id = %llu;",
status,
task);
}
else
sql ("UPDATE tasks SET run_status = %u WHERE id = %llu;",
TASK_STATUS_NEW,
task);
break;
case 1: /* Too few rows in result of query. */
break;
default: /* Programming error. */
assert (0);
case -1:
return -1;
break;
}
return 0;
}
/**
* @brief Delete a report.
*
* @param[in] report_id UUID of report.
* @param[in] dummy Dummy arg to match other delete functions.
*
* @return 0 success, 2 failed to find report, 99 permission denied, -1 error.
*/
int
delete_report (const char *report_id, int dummy)
{
report_t report;
int ret, lock_ret, lock_retries;
sql_begin_immediate ();
/* This prevents other processes (in particular a RESUME_TASK) from getting
* a reference to the report ID, and then using that reference to try access
* the deleted report.
*
* If the report is running already then delete_report_internal will
* ROLLBACK. */
lock_retries = LOCK_RETRIES;
lock_ret = sql_int ("SELECT try_exclusive_lock('reports');");
while ((lock_ret == 0) && (lock_retries > 0))
{
sleep(LOCK_RETRY_DELAY);
lock_ret = sql_int ("SELECT try_exclusive_lock('reports');");
lock_retries--;
}
if (lock_ret == 0)
{
sql_rollback ();
return -1;
}
if (acl_user_may ("delete_report") == 0)
{
sql_rollback ();
return 99;
}
report = 0;
if (find_report_with_permission (report_id, &report, "delete_report"))
{
sql_rollback ();
return -1;
}
if (report == 0)
{
if (find_trash_report_with_permission (report_id, &report, "delete_report"))
{
sql_rollback ();
return -1;
}
if (report == 0)
{
sql_rollback ();
return 2;
}
}
ret = delete_report_internal (report);
if (ret)
{
sql_rollback ();
return ret;
}
sql_commit ();
return 0;
}
/**
* @brief Return the slave progress of a report.
*
* @param[in] report Report.
*
* @return Number of reports.
*/
static int
report_slave_progress (report_t report)
{
return sql_int ("SELECT slave_progress FROM reports WHERE id = %llu;",
report);
}
/**
* @brief Set slave progress of a report.
*
* @param[in] report The report.
* @param[in] progress The new progress value.
*
* @return 0 success.
*/
int
set_report_slave_progress (report_t report, int progress)
{
sql ("UPDATE reports SET slave_progress = %i WHERE id = %llu;",
progress,
report);
return 0;
}
/**
* @brief Prepare a partial report for restarting the scan from the beginning.
*
* @param[in] report The report.
*/
void
trim_report (report_t report)
{
/* Remove results for all hosts. */
sql ("DELETE FROM results WHERE id IN"
" (SELECT results.id FROM results"
" WHERE results.report = %llu);",
report);
/* Remove all hosts and host details. */
sql ("DELETE FROM report_host_details WHERE report_host IN"
" (SELECT id FROM report_hosts WHERE report = %llu);",
report);
sql ("DELETE FROM report_hosts"
" WHERE report = %llu;",
report);
/* Clear and rebuild counts cache */
if (setting_auto_cache_rebuild_int ())
report_cache_counts (report, 1, 1, NULL);
else
report_clear_count_cache (report, 1, 1, NULL);
}
/**
* @brief Prepare a partial report for resumption of the scan.
*
* @param[in] report The report.
*/
void
trim_partial_report (report_t report)
{
/* Remove results for partial hosts. */
sql ("DELETE FROM results WHERE id IN"
" (SELECT results.id FROM results, report_hosts"
" WHERE results.report = %llu"
" AND report_hosts.report = %llu"
" AND results.host = report_hosts.host"
" AND report_hosts.end_time = 0);",
report,
report);
/* Remove partial hosts and host details. */
sql ("DELETE FROM report_host_details WHERE report_host IN"
" (SELECT report_hosts.id FROM report_hosts"
" WHERE report_hosts.report = %llu"
" AND report_hosts.end_time = 0);",
report);
sql ("DELETE FROM report_hosts"
" WHERE report = %llu"
" AND end_time is NULL;",
report);
/* Clear and rebuild counts cache */
if (setting_auto_cache_rebuild_int ())
report_cache_counts (report, 1, 1, NULL);
else
report_clear_count_cache (report, 1, 1, NULL);
}
/**
* @brief Compares two textual port representations, sorting descending
* @brief by severity
*
* @param[in] arg_one First threat level.
* @param[in] arg_two Second threat level.
*
* @return 1, 0 or -1 if first given severity is less than, equal to or greater
* than second.
*/
static gint
compare_severity_desc (gconstpointer arg_one, gconstpointer arg_two)
{
double one_severity, two_severity;
gchar *one = *((gchar**) arg_one);
gchar *two = *((gchar**) arg_two);
gint host;
one += strlen (one) + 1;
two += strlen (two) + 1;
one_severity = g_strtod (one, NULL);
two_severity = g_strtod (two, NULL);
one += strlen (one) + 1;
two += strlen (two) + 1;
host = strcmp (one, two);
if (host == 0)
{
if (one_severity > two_severity)
return -1;
else if (one_severity < two_severity)
return 1;
else
{
one = *((gchar**) arg_one);
two = *((gchar**) arg_two);
return strcmp (two, one);
}
}
return host;
}
/**
* @brief Compares two textual port representations, sorting descending
* @brief by severity
*
* @param[in] arg_one First port.
* @param[in] arg_two Second port.
*
* @return -1, 0 or 1 if first given severity is less than, equal to or greater
* than second.
*/
static gint
compare_severity_asc (gconstpointer arg_one, gconstpointer arg_two)
{
double one_severity, two_severity;
gchar *one = *((gchar**) arg_one);
gchar *two = *((gchar**) arg_two);
gint host;
one += strlen (one) + 1;
two += strlen (two) + 1;
one_severity = g_strtod (one, NULL);
two_severity = g_strtod (two, NULL);
one += strlen (one) + 1;
two += strlen (two) + 1;
host = strcmp (one, two);
if (host == 0)
{
if (one_severity < two_severity)
return -1;
else if (one_severity > two_severity)
return 1;
else
{
one = *((gchar**) arg_one);
two = *((gchar**) arg_two);
return strcmp (one, two);
}
}
return host;
}
/**
* @brief Some result info, for sorting.
*/
struct result_buffer
{
gchar *host; ///< Host.
gchar *port; ///< Port.
gchar *severity; ///< Severity.
double severity_double; ///< Severity.
};
/**
* @brief Buffer host type.
*/
typedef struct result_buffer result_buffer_t;
/**
* @brief Create a result buffer.
*
* @param[in] host Host.
* @param[in] port Port.
* @param[in] severity Severity.
* @param[in] severity_double Severity.
*
* @return Freshly allocated result buffer.
*/
static result_buffer_t*
result_buffer_new (const gchar *host, const gchar *port, const gchar *severity,
double severity_double)
{
result_buffer_t *result_buffer;
result_buffer = g_malloc (sizeof (result_buffer_t));
result_buffer->host = g_strdup (host);
result_buffer->port = g_strdup (port);
result_buffer->severity = g_strdup (severity);
result_buffer->severity_double = severity_double;
return result_buffer;
}
/**
* @brief Free a result buffer.
*
* @param[in] result_buffer Result buffer.
*/
static void
result_buffer_free (result_buffer_t *result_buffer)
{
g_free (result_buffer->host);
g_free (result_buffer->port);
g_free (result_buffer->severity);
g_free (result_buffer);
}
/**
* @brief Compares two buffered results, sorting by host, port then severity.
*
* @param[in] arg_one First result.
* @param[in] arg_two Second result.
*
* @return -1, 0 or 1 if first given result is less than, equal to or greater
* than second.
*/
static gint
compare_port_severity (gconstpointer arg_one, gconstpointer arg_two)
{
int host;
result_buffer_t *one, *two;
one = *((result_buffer_t**) arg_one);
two = *((result_buffer_t**) arg_two);
host = strcmp (one->host, two->host);
if (host == 0)
{
double severity_cmp;
int port;
port = strcmp (one->port, two->port);
if (port != 0)
return port;
severity_cmp = two->severity_double - one->severity_double;
if (severity_cmp > 0)
return 1;
else if (severity_cmp < 0)
return -1;
else
return 0;
}
return host;
}
/** @todo Defined in gmp.c! */
void buffer_results_xml (GString *, iterator_t *, task_t, int, int, int,
int, int, int, int, const char *, iterator_t *,
int, int, int);
/**
* @brief Comparison returns.
*/
typedef enum
{
COMPARE_RESULTS_CHANGED,
COMPARE_RESULTS_ERROR,
COMPARE_RESULTS_GONE,
COMPARE_RESULTS_NEW,
COMPARE_RESULTS_SAME
} compare_results_t;
/**
* @brief Return the sort order of two results.
*
* @param[in] results Iterator containing first result.
* @param[in] delta_results Iterator containing second result.
* @param[in] sort_order Whether to sort ascending or descending.
* @param[in] sort_field Field to sort on, or NULL for "type".
*
* @return < 0 if first comes before second, 0 if equal, > 0 if first comes
* after second.
*/
static compare_results_t
result_cmp (iterator_t *results, iterator_t *delta_results, int sort_order,
const char* sort_field)
{
const char *host, *delta_host, *port, *delta_port;
const char *nvt, *delta_nvt, *name, *delta_name, *descr, *delta_descr;
int ret;
double severity, delta_severity;
if (sort_field == NULL)
sort_field = "type";
g_debug (" delta: %s: sort_order: %i", __func__, sort_order);
g_debug (" delta: %s: sort_field: %s", __func__, sort_field);
host = result_iterator_host (results);
delta_host = result_iterator_host (delta_results);
port = result_iterator_port (results);
delta_port = result_iterator_port (delta_results);
severity = result_iterator_severity_double (results);
delta_severity = result_iterator_severity_double (delta_results);
nvt = result_iterator_nvt_oid (results);
delta_nvt = result_iterator_nvt_oid (delta_results);
name = result_iterator_nvt_name (results);
delta_name = result_iterator_nvt_name (delta_results);
descr = result_iterator_descr (results);
delta_descr = result_iterator_descr (delta_results);
/* For delta reports to work correctly, the order must be the same as in
* init_delta_iterators, except that description should not be checked
* unless it is the sort_field.
*
* If description is not the sort_field it is checked after the result_cmp
* in compare_results. */
/* Check sort_field first, also using sort_order (0 is descending). */
if (strcmp (sort_field, "host") == 0)
{
ret = collate_ip (NULL,
strlen (host), host, strlen (delta_host), delta_host);
if (sort_order == 0)
ret = -ret;
g_debug (" delta: %s: host (%s): %s VS %s (%i)",
__func__, sort_order ? "desc" : "asc",
host, delta_host, ret);
if (ret)
return ret;
}
else if (strcmp (sort_field, "port") == 0
|| strcmp (sort_field, "location") == 0)
{
ret = strcmp (port, delta_port);
if (sort_order == 0)
ret = -ret;
g_debug (" delta: %s: port (%s): %s VS %s (%i)",
__func__, sort_order ? "desc" : "asc",
port, delta_port, ret);
if (ret)
return ret;
}
else if (strcmp (sort_field, "severity") == 0)
{
if (severity > delta_severity)
ret = sort_order ? 1 : -1;
else if (severity < delta_severity)
ret = sort_order ? -1 : 1;
else
ret = 0;
g_debug (" delta: %s: severity (%s): %f VS %f (%i)",
__func__, sort_order ? "desc" : "asc",
severity, delta_severity, ret);
if (ret)
return ret;
}
/* NVT OID, not name/vulnerability. */
else if (strcmp (sort_field, "nvt") == 0)
{
ret = strcmp (nvt, delta_nvt);
if (sort_order)
ret = -ret;
g_debug (" delta: %s: nvt (%s): %s VS %s (%i)",
__func__, sort_order ? "desc" : "asc",
nvt, delta_nvt, ret);
if (ret)
return ret;
}
else if (strcmp (sort_field, "description") == 0)
{
ret = strcmp (descr, delta_descr);
if (sort_order == 0)
ret = -ret;
g_debug (" delta: %s: description (%s): %s VS %s (%i)",
__func__, sort_order ? "desc" : "asc",
descr, delta_descr, ret);
if (ret)
return ret;
}
else if (strcmp (sort_field, "type") == 0)
{
const char *type, *delta_type;
type = result_iterator_type (results);
delta_type = result_iterator_type (delta_results);
ret = strcmp (type, delta_type);
if (sort_order == 0)
ret = -ret;
g_debug (" delta: %s: type (%s): %s VS %s (%i)",
__func__, sort_order ? "desc" : "asc",
descr, delta_descr, ret);
if (ret)
return ret;
}
else if (strcmp (sort_field, "original_type") == 0)
{
const char *type, *delta_type;
type = result_iterator_original_type (results);
delta_type = result_iterator_original_type (delta_results);
ret = strcmp (type, delta_type);
if (sort_order == 0)
ret = -ret;
g_debug (" delta: %s: original_type (%s): %s VS %s (%i)",
__func__, sort_order ? "desc" : "asc",
descr, delta_descr, ret);
if (ret)
return ret;
}
else
{
/* Default to "vulnerability" (a.k.a "name") for unknown sort fields.
*
* Also done in print_report_xml_start, so this is just a safety check. */
ret = strcmp (name ? name : "", delta_name ? delta_name : "");
if (sort_order == 0)
ret = -ret;
if (ret)
return ret;
}
/* Check remaining fields */
if (strcmp (sort_field, "host"))
{
ret = collate_ip (NULL,
strlen (host), host, strlen (delta_host), delta_host);
g_debug (" delta: %s: host: %s VS %s (%i)",
__func__, host, delta_host, ret);
if (ret)
return ret;
}
if (strcmp (sort_field, "port")
&& strcmp (sort_field, "location"))
{
ret = strcmp (port, delta_port);
g_debug (" delta: %s: port: %s VS %s (%i)",
__func__, port, delta_port, ret);
if (ret)
return ret;
}
if (strcmp (sort_field, "severity"))
{
if (severity > delta_severity)
ret = 1;
else if (severity < delta_severity)
ret = -1;
else
ret = 0;
g_debug (" delta: %s: severity: %f VS %f (%i)",
__func__, severity, delta_severity, ret);
if (ret)
return ret;
}
if (strcmp (sort_field, "nvt"))
{
ret = strcmp (nvt, delta_nvt);
g_debug (" delta: %s: nvt: %s VS %s (%i)",
__func__, nvt, delta_nvt, ret);
if (ret)
return ret;
}
return 0;
}
/**
* @brief Test if two strings are equal, ignoring whitespace.
*
* @param[in] one First string.
* @param[in] two Second string.
*
* @return 1 if equal, else 0.
*/
static int
streq_ignore_ws (const gchar *one, const gchar *two)
{
if (one == NULL)
return two == NULL;
if (two == NULL)
return 0;
while (1)
{
/* Skip space in both. */
while (isspace (*one))
one++;
while (isspace (*two))
two++;
/* Check for end. */
if (*one == '\0')
break;
if (*two == '\0')
return 0;
/* Compare. */
if (*one != *two)
return 0;
/* Next. */
one++;
two++;
}
if (*two)
return 0;
return 1;
}
/**
* @brief Compare two results.
*
* @param[in] results Iterator containing first result.
* @param[in] delta_results Iterator containing second result.
* @param[in] sort_order Whether to sort ascending or descending.
* @param[in] sort_field Field to sort on, or NULL for "type".
*
* @return Result of comparison.
*/
static compare_results_t
compare_results (iterator_t *results, iterator_t *delta_results, int sort_order,
const char* sort_field)
{
int ret;
const char *descr, *delta_descr;
g_debug (" delta: %s", __func__);
ret = result_cmp (results, delta_results, sort_order, sort_field);
if (ret > 0)
/* The delta result sorts first, so it is new. */
return COMPARE_RESULTS_NEW;
if (ret < 0)
/* The 'results' result sorts first, so it has gone. */
return COMPARE_RESULTS_GONE;
descr = result_iterator_descr (results);
delta_descr = result_iterator_descr (delta_results);
g_debug (" delta: %s: descr: %s VS %s (%i)",
__func__,
descr ? descr : "NULL",
delta_descr ? delta_descr : "NULL",
(descr && delta_descr) ? strcmp (descr, delta_descr) : 0);
/* This comparison ignores whitespace to match the diff output created by
* strdiff in gmp.c. The down side of this is that the comparison may be
* affected by the locale.
*
* An alternate would be to use the strdiff result as the comparison, but
* strdiff is only called for the results on the page (and not for the
* rest of the results, which must also be compared for the counts).
* Using strdiff for all results could also be slow, because it's running
* the diff command. */
if (descr && delta_descr && (streq_ignore_ws (descr, delta_descr) == 0))
return COMPARE_RESULTS_CHANGED;
return COMPARE_RESULTS_SAME;
}
/**
* @brief Compare two results, optionally writing associated XML to a buffer.
*
* This is called with buffer NULL to compare results after the page limit
* (filter keyword "max") is reached. These results need to be compared to be
* included in the counts.
*
* @param[in] buffer Buffer. NULL to skip writing to buffer.
* @param[in] results Iterator containing first result.
* @param[in] delta_results Iterator containing second result.
* @param[in] task Task associated with report.
* @param[in] notes Whether to include notes.
* @param[in] notes_details If notes, Whether to include details.
* @param[in] overrides Whether to include overrides.
* @param[in] overrides_details If overrides, Whether to include details.
* @param[in] sort_order Whether to sort ascending or descending.
* @param[in] sort_field Field to sort on, or NULL for "type".
* @param[in] changed Whether to include changed results.
* @param[in] gone Whether to include gone results.
* @param[in] new Whether to include new results.
* @param[in] same Whether to include same results.
* @param[in] max_results Value to decrement if result is buffered.
* @param[in] first_result Skip result and decrement if positive.
* @param[in] used 0 if used, 1 if skipped.
* @param[in] would_use 0 if would use (first_result aside), 1 if skipped.
*
* @return Result of comparison.
*/
static compare_results_t
compare_and_buffer_results (GString *buffer, iterator_t *results,
iterator_t *delta_results, task_t task, int notes,
int notes_details, int overrides,
int overrides_details, int sort_order,
const char* sort_field, int changed, int gone,
int new, int same, int *max_results,
int *first_result, int *used, int *would_use)
{
compare_results_t state;
state = compare_results (results, delta_results, sort_order, sort_field);
*used = 0;
*would_use = 0;
switch (state)
{
case COMPARE_RESULTS_CHANGED:
if (changed)
{
*would_use = 1;
if (*first_result)
{
g_debug (" delta: skip");
(*first_result)--;
break;
}
*used = 1;
(*max_results)--;
if (buffer)
buffer_results_xml (buffer,
results,
task,
notes,
notes_details,
overrides,
overrides_details,
0,
0,
0,
"changed",
delta_results,
/* This is the only case that uses 1. */
1, /* Whether result is "changed". */
-1,
0);
}
break;
case COMPARE_RESULTS_GONE:
if (gone)
{
*would_use = 1;
if (*first_result)
{
g_debug (" delta: skip");
(*first_result)--;
break;
}
*used = 1;
(*max_results)--;
if (buffer)
buffer_results_xml (buffer,
results,
task,
notes,
notes_details,
overrides,
overrides_details,
0,
0,
0,
"gone",
delta_results,
0,
-1,
0);
}
break;
case COMPARE_RESULTS_NEW:
if (new)
{
*would_use = 1;
if (*first_result)
{
g_debug (" delta: skip");
(*first_result)--;
break;
}
*used = 1;
(*max_results)--;
if (buffer)
buffer_results_xml (buffer,
delta_results,
task,
notes,
notes_details,
overrides,
overrides_details,
0,
0,
0,
"new",
delta_results,
0,
-1,
0);
}
break;
case COMPARE_RESULTS_SAME:
if (same)
{
*would_use = 1;
if (*first_result)
{
g_debug (" delta: skip");
(*first_result)--;
break;
}
*used = 1;
(*max_results)--;
if (buffer)
buffer_results_xml (buffer,
results,
task,
notes,
notes_details,
overrides,
overrides_details,
0,
0,
0,
"same",
delta_results,
0,
-1,
0);
}
break;
default:
return COMPARE_RESULTS_ERROR;
}
return state;
}
/**
* @brief Write XML to a file or close stream and return.
*
* @param[in] stream Stream to write to.
* @param[in] xml XML.
*/
#define PRINT_XML(stream, xml) \
do \
{ \
if (fprintf (stream, "%s", xml) < 0) \
{ \
fclose (stream); \
return -1; \
} \
} \
while (0)
/**
* @brief Add a port to a port tree.
*
* @param[in] ports The tree.
* @param[in] results Result iterator on result whose port to add.
*/
static void
add_port (GTree *ports, iterator_t *results)
{
const char *port, *host;
double *old_severity, *severity;
GTree *host_ports;
/* Ensure there's an inner tree for the host. */
host = result_iterator_host (results);
host_ports = g_tree_lookup (ports, host);
if (host_ports == NULL)
{
host_ports = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, g_free,
g_free);
g_tree_insert (ports, g_strdup (host), host_ports);
}
/* Ensure the highest threat is recorded for the port in the inner tree. */
port = result_iterator_port (results);
severity = g_malloc (sizeof (double));
*severity = result_iterator_severity_double (results);
old_severity = g_tree_lookup (host_ports, port);
g_debug (" delta: %s: adding %s severity %1.1f on host %s", __func__,
port, *severity, host);
if (old_severity == NULL)
g_tree_insert (host_ports, g_strdup (port), severity);
else if (severity > old_severity)
{
*old_severity = *severity;
g_free (severity);
}
else
{
g_free (severity);
}
}
/**
* @brief Print delta host ports.
*
* @param[in] key Port.
* @param[in] value Threat.
* @param[in] data Host and stream.
*
* @return Always FALSE.
*/
static gboolean
print_host_port (gpointer key, gpointer value, gpointer data)
{
gpointer *host_and_stream;
host_and_stream = (gpointer*) data;
g_debug (" delta: %s: host %s port %s", __func__,
(gchar*) host_and_stream[0], (gchar*) key);
fprintf ((FILE*) host_and_stream[1],
""
"%s"
"%s"
"%1.1f"
"%s"
"",
(gchar*) host_and_stream[0],
(gchar*) key,
*((double*) value),
severity_to_level (*((double*) value), 0));
return FALSE;
}
/**
* @brief Print delta ports.
*
* @param[in] key Host.
* @param[in] value Port tree.
* @param[in] stream Stream.
*
* @return Always FALSE.
*/
static gboolean
print_host_ports (gpointer key, gpointer value, gpointer stream)
{
gpointer host_and_stream[2];
host_and_stream[0] = key;
host_and_stream[1] = stream;
g_debug (" delta: %s: host %s", __func__, (gchar*) key);
g_tree_foreach ((GTree*) value, print_host_port, host_and_stream);
return FALSE;
}
/**
* @brief Add port to ports array.
*
* @param[in] key Port.
* @param[in] value Threat.
* @param[in] ports Ports array.
*
* @return Always FALSE.
*/
static gboolean
array_add_port (gpointer key, gpointer value, gpointer ports)
{
gpointer *port_threat;
port_threat = g_malloc (2 * sizeof (gpointer));
port_threat[0] = key;
port_threat[1] = value;
array_add ((array_t*) ports, port_threat);
return FALSE;
}
/**
* @brief Print delta ports, in descending order.
*
* @param[in] key Host.
* @param[in] value Port tree.
* @param[in] stream Stream.
*
* @return Always FALSE.
*/
static gboolean
print_host_ports_desc (gpointer key, gpointer value, gpointer stream)
{
guint index;
array_t *ports;
g_debug (" delta: %s: host %s", __func__, (gchar*) key);
/* Convert tree to array. */
ports = make_array ();
g_tree_foreach ((GTree*) value, array_add_port, ports);
/* Print the array backwards. */
index = ports->len;
while (index--)
{
gpointer *port_threat;
port_threat = g_ptr_array_index (ports, index);
fprintf ((FILE*) stream,
""
"%s"
"%s"
"%1.1f"
"%s"
"",
(gchar*) key,
(gchar*) port_threat[0],
*((double*) port_threat[1]),
severity_to_level (*((double*) port_threat[1]), 0));
}
array_free (ports);
return FALSE;
}
/**
* @brief Compare port severities, ascending.
*
* @param[in] one First.
* @param[in] two Second.
*
* @return 1 one greater, -1 two greater, 0 equal.
*/
static gint
compare_ports_severity (gconstpointer one, gconstpointer two)
{
gpointer *port_threat_one, *port_threat_two;
port_threat_one = *((gpointer**) one);
port_threat_two = *((gpointer**) two);
if (*((double*) port_threat_one[1]) > *((double*) port_threat_two[1]))
return 1;
else if (*((double*) port_threat_one[1]) < *((double*) port_threat_two[1]))
return -1;
else
return 0;
}
/**
* @brief Compare port severities, descending.
*
* @param[in] one First.
* @param[in] two Second.
*
* @return 1 one less, -1 two less, 0 equal.
*/
static gint
compare_ports_severity_desc (gconstpointer one, gconstpointer two)
{
gpointer *port_threat_one, *port_threat_two;
port_threat_one = *((gpointer**) one);
port_threat_two = *((gpointer**) two);
if (*((double*) port_threat_one[1]) < *((double*) port_threat_two[1]))
return 1;
else if (*((double*) port_threat_one[1]) > *((double*) port_threat_two[1]))
return -1;
else
return 0;
}
/**
* @brief Print delta ports, ordering by severity.
*
* @param[in] key Host.
* @param[in] value Port tree.
* @param[in] stream Stream.
* @param[in] ascending Ascending or descending.
*
* @return Always FALSE.
*/
static gboolean
print_host_ports_by_severity (gpointer key, gpointer value, gpointer stream,
int ascending)
{
guint index, len;
array_t *ports;
g_debug (" delta: %s: host %s", __func__, (gchar*) key);
/* Convert tree to array. */
ports = make_array ();
g_tree_foreach ((GTree*) value, array_add_port, ports);
/* Sort the array. */
if (ascending)
g_ptr_array_sort (ports, compare_ports_severity);
else
g_ptr_array_sort (ports, compare_ports_severity_desc);
/* Print the sorted array. */
index = 0;
len = ports->len;
while (index < len)
{
gpointer *port_threat;
port_threat = g_ptr_array_index (ports, index);
fprintf ((FILE*) stream,
""
"%s"
"%s"
"%1.1f"
"%s"
"",
(gchar*) key,
(gchar*) port_threat[0],
*((double*) port_threat[1]),
severity_to_level (*((double*) port_threat[1]), 0));
index++;
}
array_free (ports);
return FALSE;
}
/**
* @brief Print delta ports, ordering by severity descending.
*
* @param[in] key Host.
* @param[in] value Port tree.
* @param[in] stream Stream.
*
* @return Always FALSE.
*/
static gboolean
print_host_ports_by_severity_desc (gpointer key, gpointer value,
gpointer stream)
{
return print_host_ports_by_severity (key, value, stream, 0);
}
/**
* @brief Print delta ports, ordering by severity ascending.
*
* @param[in] key Host.
* @param[in] value Port tree.
* @param[in] stream Stream.
*
* @return Always FALSE.
*/
static gboolean
print_host_ports_by_severity_asc (gpointer key, gpointer value,
gpointer stream)
{
return print_host_ports_by_severity (key, value, stream, 1);
}
/**
* @brief Free delta host ports.
*
* @param[in] host_ports Ports.
* @param[in] dummy Dummy.
*
* @return Always FALSE.
*/
static gboolean
free_host_ports (GTree *host_ports, gpointer dummy)
{
g_tree_destroy (host_ports);
return FALSE;
}
/**
* @brief Get N'th last report_host given a host.
*
* The last report_host is at position 1, the second last at position 2, and
* so on.
*
* @param[in] host Host.
* @param[in] report_host Report host.
* @param[in] position Position from end.
*
* @return N'th last report_host.
*/
gboolean
host_nthlast_report_host (const char *host, report_host_t *report_host,
int position)
{
gchar *quoted_host;
assert (current_credentials.uuid);
if (position == 0)
position = 1;
quoted_host = sql_quote (host);
switch (sql_int64 (report_host,
"SELECT id FROM report_hosts WHERE host = '%s'"
" AND user_owns ('task',"
" (SELECT reports.task FROM reports"
" WHERE reports.id"
" = report_hosts.report))"
" AND (SELECT tasks.hidden FROM tasks, reports"
" WHERE reports.task = tasks.id"
" AND reports.id = report_hosts.report)"
" = 0"
" AND (SELECT value FROM task_preferences, tasks,"
" reports"
" WHERE reports.task = tasks.id"
" AND reports.id = report_hosts.report"
" AND task_preferences.task = tasks.id"
" AND task_preferences.name = 'in_assets')"
" = 'yes'"
" AND report_hosts.end_time > 0"
" AND NOT EXISTS (SELECT * FROM report_host_details"
" WHERE report_host = report_hosts.id"
" AND name = 'CVE Scan')"
" ORDER BY id DESC LIMIT 1 OFFSET %i;",
quoted_host,
position - 1))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*report_host = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return TRUE;
break;
}
g_free (quoted_host);
return FALSE;
}
/**
* @brief Count a report's total number of hosts.
*
* @param[in] report Report.
*
* @return Host count.
*/
int
report_host_count (report_t report)
{
return sql_int ("SELECT count (DISTINCT id) FROM report_hosts"
" WHERE report = %llu;",
report);
}
/**
* @brief Count a report's total number of hosts with results.
*
* @param[in] report Report.
* @param[in] min_qod Minimum QoD of results to count.
*
* @return The number of hosts with results
*/
int
report_result_host_count (report_t report, int min_qod)
{
return sql_int ("SELECT count (DISTINCT id) FROM report_hosts"
" WHERE report_hosts.report = %llu"
" AND EXISTS (SELECT * FROM results"
" WHERE results.host = report_hosts.host"
" AND results.qod >= %d)",
report,
min_qod);
}
/**
* @brief Count a report's total number of tcp/ip ports.
*
* Ignores port entries in "general/..." form.
*
* @param[in] report Report.
*
* @return Ports count.
*/
static int
report_port_count (report_t report)
{
return sql_int ("SELECT count (DISTINCT port) FROM results"
" WHERE report = %llu AND port != ''"
" AND port NOT %s 'general/%%';",
report,
sql_ilike_op ());
}
/**
* @brief Count a report's total number of closed cves.
*
* @param[in] report Report.
*
* @return Closed CVE count.
*/
static int
report_closed_cve_count (report_t report)
{
return sql_int (" SELECT count(id) FROM nvts"
" WHERE cve != ''"
" AND family IN (" LSC_FAMILY_LIST ")"
" AND oid IN"
" (SELECT source_name FROM report_host_details"
" WHERE report_host IN "
" (SELECT id FROM report_hosts WHERE report = %llu)"
" AND name = 'EXIT_CODE'"
" AND value = 'EXIT_NOTVULN');",
report);
}
/**
* @brief Count a report's total number of vulnerabilities.
*
* @param[in] report Report.
*
* @return Vulnerabilities count.
*/
static int
report_vuln_count (report_t report)
{
return sql_int ("SELECT count (DISTINCT nvt) FROM results"
" WHERE report = %llu"
" AND severity != " G_STRINGIFY (SEVERITY_ERROR) ";",
report);
}
/**
* @brief Count a report's total number of detected Operating Systems.
*
* @param[in] report Report.
*
* @return OS count.
*/
static int
report_os_count (report_t report)
{
return sql_int ("SELECT count (DISTINCT value) FROM report_host_details"
" WHERE report_host IN"
" (SELECT id from report_hosts WHERE report = %llu)"
" AND name = 'best_os_cpe';",
report);
}
/**
* @brief Count a report's total number of detected Apps.
*
* @param[in] report Report.
*
* @return App count.
*/
static int
report_app_count (report_t report)
{
return sql_int ("SELECT count (DISTINCT value) FROM report_host_details"
" WHERE report_host IN"
" (SELECT id from report_hosts WHERE report = %llu)"
" AND name = 'App';",
report);
}
/**
* @brief Count a report's total number of found SSL Certificates.
*
* @param[in] report Report.
*
* @return SSL Certificates count.
*/
static int
report_ssl_cert_count (report_t report)
{
return sql_int ("SELECT count (DISTINCT id) FROM report_host_details"
" WHERE report_host IN"
" (SELECT id from report_hosts WHERE report = %llu)"
" AND name = 'SSLInfo';",
report);
}
/**
* @brief Count a report's total number of error messages.
*
* @param[in] report Report.
*
* @return Error Messages count.
*/
static int
report_error_count (report_t report)
{
return sql_int ("SELECT count (id) FROM results"
" WHERE report = %llu and type = 'Error Message';",
report);
}
/**
* @brief Get a list string of finished hosts in a report.
*
* @param[in] report The report to get the finished hosts from.
*
* @return String containing finished hosts as comma separated list.
*/
char *
report_finished_hosts_str (report_t report)
{
char *ret;
ret = sql_string ("SELECT string_agg (host, ',' ORDER BY host)"
" FROM report_hosts"
" WHERE report = %llu"
" AND end_time != 0;",
report);
return ret;
}
/**
* @brief Write report host detail to file stream.
*
* On error close stream.
*
* @param[in] stream Stream to write to.
* @param[in] details Report host details iterator.
* @param[in] lean Whether to return reduced info.
*
* @return 0 success, -1 error.
*/
static int
print_report_host_detail (FILE *stream, iterator_t *details, int lean)
{
const char *name, *value;
name = report_host_details_iterator_name (details);
value = report_host_details_iterator_value (details);
if (lean)
{
/* Skip certain host details. */
if (strcmp (name, "EXIT_CODE") == 0
&& strcmp (value, "EXIT_NOTVULN") == 0)
return 0;
if (strcmp (name, "scanned_with_scanner") == 0)
return 0;
if (strcmp (name, "scanned_with_feedtype") == 0)
return 0;
if (strcmp (name, "scanned_with_feedversion") == 0)
return 0;
if (strcmp (name, "OS") == 0)
return 0;
if (strcmp (name, "traceroute") == 0)
return 0;
}
PRINT (stream,
""
"%s"
"%s"
"");
if (report_host_details_iterator_extra (details)
&& strlen (report_host_details_iterator_extra (details)))
PRINT (stream,
"%s",
report_host_details_iterator_extra (details));
else if (lean == 0)
PRINT (stream,
"");
PRINT (stream,
"");
return 0;
}
/**
* @brief Print the XML for a report's host details to a file stream.
* @param[in] report_host The report host.
* @param[in] stream File stream to write to.
* @param[in] lean Report host details iterator.
*
* @return 0 on success, -1 error.
*/
static int
print_report_host_details_xml (report_host_t report_host, FILE *stream,
int lean)
{
iterator_t details;
init_report_host_details_iterator
(&details, report_host);
while (next (&details))
if (print_report_host_detail (stream, &details, lean))
return -1;
cleanup_iterator (&details);
return 0;
}
/**
* @brief Write report error message to file stream.
*
* @param[in] stream Stream to write to.
* @param[in] errors Pointer to report error messages iterator.
* @param[in] asset_id Asset ID.
*/
#define PRINT_REPORT_ERROR(stream, errors, asset_id) \
do \
{ \
PRINT (stream, \
"" \
"" \
"%s" \
"" \
"" \
"%s" \
"%s" \
"" \
"nvt" \
"%s" \
"%s" \
"" \
"%s" \
"%s" \
"", \
report_errors_iterator_host (errors) ?: "", \
asset_id ? asset_id : "", \
report_errors_iterator_port (errors), \
report_errors_iterator_desc (errors), \
report_errors_iterator_nvt_oid (errors), \
report_errors_iterator_nvt_name (errors), \
report_errors_iterator_nvt_cvss (errors), \
report_errors_iterator_scan_nvt_version (errors), \
report_errors_iterator_severity (errors)); \
} \
while (0)
/**
* @brief Print the XML for a report's error messages to a file stream.
* @param[in] report The report.
* @param[in] stream File stream to write to.
*
* @return 0 on success, -1 error.
*/
static int
print_report_errors_xml (report_t report, FILE *stream)
{
iterator_t errors;
init_report_errors_iterator
(&errors, report);
PRINT (stream, "%i", report_error_count (report));
while (next (&errors))
{
char *asset_id;
asset_id = result_host_asset_id (report_errors_iterator_host (&errors),
report_errors_iterator_result (&errors));
PRINT_REPORT_ERROR (stream, &errors, asset_id);
free (asset_id);
}
cleanup_iterator (&errors);
PRINT (stream, "");
return 0;
}
/**
* @brief Print the XML for a report port summary to a file.
*
* @param[in] report The report.
* @param[in] out File stream.
* @param[in] get Result get data.
* @param[in] first_result The result to start from. The results are 0
* indexed.
* @param[in] max_results The maximum number of results returned.
* @param[in] sort_order Whether to sort ascending or descending.
* @param[in] sort_field Field to sort on.
* @param[out] host_ports Hash table for counting ports per host.
* @param[in,out] results Result iterator. For caller to reuse.
*
* @return 0 on success, -1 error.
*/
static int
print_report_port_xml (report_t report, FILE *out, const get_data_t *get,
int first_result, int max_results,
int sort_order, const char *sort_field,
GHashTable *host_ports, iterator_t *results)
{
result_buffer_t *last_item;
GArray *ports = g_array_new (TRUE, FALSE, sizeof (gchar*));
init_result_get_iterator (results, get, report, NULL, NULL);
/* Buffer the results, removing duplicates. */
last_item = NULL;
while (next (results))
{
const char *port = result_iterator_port (results);
const char *host = result_iterator_host (results);
double cvss_double;
cvss_double = result_iterator_severity_double (results);
if (last_item
&& strcmp (port, last_item->port) == 0
&& strcmp (host, last_item->host) == 0
&& last_item->severity_double <= cvss_double)
{
last_item->severity_double = cvss_double;
g_free (last_item->severity);
last_item->severity = g_strdup (result_iterator_severity (results));
}
else
{
const char *cvss;
result_buffer_t *item;
cvss = result_iterator_severity (results);
if (cvss == NULL)
{
cvss_double = 0.0;
cvss = "0.0";
}
item = result_buffer_new (host, port, cvss, cvss_double);
g_array_append_val (ports, item);
last_item = item;
}
}
/* Handle sorting by threat and ROWID. */
if (sort_field == NULL || strcmp (sort_field, "port"))
{
int index, length;
/** @todo Sort by ROWID if was requested. */
/* Sort by port then severity. */
g_array_sort (ports, compare_port_severity);
/* Remove duplicates. */
last_item = NULL;
for (index = 0, length = ports->len; index < length; index++)
{
result_buffer_t *item;
item = g_array_index (ports, result_buffer_t*, index);
if (last_item
&& (strcmp (item->port, last_item->port) == 0)
&& (strcmp (item->host, last_item->host) == 0))
{
if (item->severity_double > last_item->severity_double)
{
gchar *severity;
severity = last_item->severity;
last_item->severity = item->severity;
item->severity = severity;
last_item->severity_double = item->severity_double;
}
g_array_remove_index (ports, index);
length = ports->len;
index--;
}
else
last_item = item;
}
/* Sort by severity. */
if (sort_order)
g_array_sort (ports, compare_severity_asc);
else
g_array_sort (ports, compare_severity_desc);
}
/* Write to file from the buffer. */
PRINT (out,
""
"%i",
/* Add 1 for 1 indexing. */
first_result + 1,
max_results,
report_port_count (report));
{
result_buffer_t *item;
int index = 0;
while ((item = g_array_index (ports, result_buffer_t*, index++)))
{
int host_port_count
= GPOINTER_TO_INT (g_hash_table_lookup (host_ports, item->host));
PRINT (out,
""
"%s"
"%s"
"%1.1f"
"%s"
"",
item->host,
item->port,
item->severity_double,
severity_to_level (g_strtod (item->severity, NULL), 0));
if (g_str_has_prefix(item->port, "general/") == FALSE)
{
g_hash_table_replace (host_ports,
g_strdup (item->host),
GINT_TO_POINTER (host_port_count + 1));
}
result_buffer_free (item);
}
g_array_free (ports, TRUE);
}
PRINT (out, "");
return 0;
}
/**
* @brief Calculate the progress of a report.
*
* @param[in] report Report.
*
* @return Progress.
*/
int
report_progress (report_t report)
{
if (report == 0)
return -1;
return report_slave_progress (report);
}
/**
* @brief Restore original TZ.
*
* @param[in] zone Only revert if this is at least one character.
* Freed here always.
* @param[in] tz Original TZ. Freed here if revert occurs.
* @param[in] old_tz_override Original tz_override. Freed here on revert.
*
* @return 0 success, -1 error.
*/
static int
tz_revert (gchar *zone, char *tz, char *old_tz_override)
{
if (zone && strlen (zone))
{
gchar *quoted_old_tz_override;
/* Revert to stored TZ. */
if (tz)
{
if (setenv ("TZ", tz, 1) == -1)
{
g_warning ("%s: Failed to switch to original TZ", __func__);
g_free (tz);
g_free (zone);
free (old_tz_override);
return -1;
}
}
else
unsetenv ("TZ");
quoted_old_tz_override = sql_insert (old_tz_override);
sql ("SET SESSION \"gvmd.tz_override\" = %s;",
quoted_old_tz_override);
g_free (quoted_old_tz_override);
free (old_tz_override);
g_free (tz);
}
g_free (zone);
return 0;
}
/**
* @brief Print the XML for a report to a file.
*
* @param[in] host_summary_buffer Summary.
* @param[in] host Host.
* @param[in] start_iso Start time, in ISO format.
* @param[in] end_iso End time, in ISO format.
*/
static void
host_summary_append (GString *host_summary_buffer, const char *host,
const char *start_iso, const char *end_iso)
{
if (host_summary_buffer)
{
char start[200], end[200];
if (start_iso)
{
struct tm start_tm;
memset (&start_tm, 0, sizeof (struct tm));
#if !defined(__GLIBC__)
if (strptime (start_iso, "%Y-%m-%dT%H:%M:%S", &start_tm) == NULL)
#else
if (strptime (start_iso, "%FT%H:%M:%S", &start_tm) == NULL)
#endif
{
g_warning ("%s: Failed to parse start", __func__);
return;
}
if (strftime (start, 200, "%b %d, %H:%M:%S", &start_tm) == 0)
{
g_warning ("%s: Failed to format start", __func__);
return;
}
}
else
strcpy (start, "(not started)");
if (end_iso)
{
struct tm end_tm;
memset (&end_tm, 0, sizeof (struct tm));
#if !defined(__GLIBC__)
if (strptime (end_iso, "%Y-%m-%dT%H:%M:%S", &end_tm) == NULL)
#else
if (strptime (end_iso, "%FT%H:%M:%S", &end_tm) == NULL)
#endif
{
g_warning ("%s: Failed to parse end", __func__);
return;
}
if (strftime (end, 200, "%b %d, %H:%M:%S", &end_tm) == 0)
{
g_warning ("%s: Failed to format end", __func__);
return;
}
}
else
strcpy (end, "(not finished)");
g_string_append_printf (host_summary_buffer,
" %-15s %-16s %s\n",
host,
start,
end);
}
}
/**
* @brief Init delta iterators for print_report_xml.
*
* @param[in] report The report.
* @param[in] results Report result iterator.
* @param[in] delta Delta report.
* @param[in] delta_results Delta report result iterator.
* @param[in] get GET command data.
* @param[in] term Filter term.
* @param[out] sort_field Sort field.
*
* @return 0 on success, -1 error.
*/
static int
init_delta_iterators (report_t report, iterator_t *results, report_t delta,
iterator_t *delta_results, const get_data_t *get,
const char *term, const char *sort_field)
{
int res;
gchar *order;
get_data_t delta_get;
/*
* Order must be the same as in result_cmp, except for description
* which isn't checked there.
*/
if ((strcmp (sort_field, "name") == 0)
|| (strcmp (sort_field, "vulnerability") == 0))
order = g_strdup (", host, port, severity, nvt, description");
else if (strcmp (sort_field, "host") == 0)
order = g_strdup (", port, severity, nvt, description");
else if ((strcmp (sort_field, "port") == 0)
|| (strcmp (sort_field, "location") == 0))
order = g_strdup (", host, severity, nvt, description");
else if (strcmp (sort_field, "severity") == 0)
order = g_strdup (", host, port, nvt, description");
else if (strcmp (sort_field, "nvt") == 0)
order = g_strdup (", host, port, severity, description");
else
order = g_strdup (", host, port, severity, nvt, description");
delta_get = *get;
delta_get.filt_id = NULL;
delta_get.filter = g_strdup_printf ("rows=-1 first=1 sort=%s %s",
sort_field, term);
ignore_max_rows_per_page = 1;
#if 0
/* For debugging. */
iterator_t results2;
res = init_result_get_iterator (results, &delta_get, report, NULL, order);
if (res)
return -1;
res = init_result_get_iterator (&results2, &delta_get, delta, NULL, order);
if (res)
return -1;
g_debug (" delta: %s: REPORT 1:", __func__);
while (next (results))
g_debug (" delta: %s: %s %s %s %s %.30s",
__func__,
result_iterator_nvt_name (results),
result_iterator_host (results),
result_iterator_type (results),
result_iterator_port (results),
result_iterator_descr (results));
cleanup_iterator (results);
g_debug (" delta: %s: REPORT 1 END", __func__);
g_debug (" delta: %s: REPORT 2:", __func__);
while (next (&results2))
g_debug (" delta: %s: %s %s %s %s %.30s",
__func__,
result_iterator_nvt_name (&results2),
result_iterator_host (&results2),
result_iterator_type (&results2),
result_iterator_port (&results2),
result_iterator_descr (&results2));
cleanup_iterator (&results2);
g_debug (" delta: %s: REPORT 2 END", __func__);
#endif
res = init_result_get_iterator (results, &delta_get, report, NULL, order);
if (res)
{
ignore_max_rows_per_page = 0;
g_free (order);
return -1;
}
res = init_result_get_iterator (delta_results, &delta_get, delta, NULL, order);
if (res)
{
ignore_max_rows_per_page = 0;
g_free (order);
return -1;
}
g_free (delta_get.filter);
ignore_max_rows_per_page = 0;
g_free (order);
return 0;
}
/**
* @brief Print delta results for print_report_xml.
*
* @param[in] out File stream to write to.
* @param[in] results Report result iterator.
* @param[in] delta_results Delta report result iterator.
* @param[in] delta_states String describing delta states to include in count
* (for example, "sngc" Same, New, Gone and Changed).
* All levels if NULL.
* @param[in] first_result First result.
* @param[in] max_results Max results.
* @param[in] task The task.
* @param[in] notes Whether to include notes.
* @param[in] notes_details Whether to include note details.
* @param[in] overrides Whether to include overrides.
* @param[in] overrides_details Whether to include override details.
* @param[in] sort_order Sort order.
* @param[in] sort_field Sort field.
* @param[in] result_hosts_only Whether to only include hosts with results.
* @param[in] orig_filtered_result_count Result count.
* @param[in] filtered_result_count Result count.
* @param[in] orig_f_holes Result count.
* @param[in] f_holes Result count.
* @param[in] orig_f_infos Result count.
* @param[in] f_infos Result count.
* @param[in] orig_f_logs Result count.
* @param[in] f_logs Result count.
* @param[in] orig_f_warnings Result count.
* @param[in] f_warnings Result count.
* @param[in] orig_f_false_positives Result count.
* @param[in] f_false_positives Result count.
* @param[in] result_hosts Result hosts.
*
* @return 0 on success, -1 error.
*/
static int
print_report_delta_xml (FILE *out, iterator_t *results,
iterator_t *delta_results, const char *delta_states,
int first_result, int max_results, task_t task,
int notes, int notes_details, int overrides,
int overrides_details, int sort_order,
const char *sort_field, int result_hosts_only,
int *orig_filtered_result_count,
int *filtered_result_count,
int *orig_f_holes, int *f_holes,
int *orig_f_infos, int *f_infos,
int *orig_f_logs, int *f_logs,
int *orig_f_warnings, int *f_warnings,
int *orig_f_false_positives, int *f_false_positives,
array_t *result_hosts)
{
gboolean done, delta_done;
int changed, gone, new, same;
/* A tree of host, tree pairs, where the inner tree is a sorted tree
* of port, threat pairs. */
GTree *ports;
gchar *msg;
*orig_f_holes = *f_holes;
*orig_f_infos = *f_infos;
*orig_f_logs = *f_logs;
*orig_f_warnings = *f_warnings;
*orig_f_false_positives = *f_false_positives;
*orig_filtered_result_count = *filtered_result_count;
changed = (strchr (delta_states, 'c') != NULL);
gone = (strchr (delta_states, 'g') != NULL);
new = (strchr (delta_states, 'n') != NULL);
same = (strchr (delta_states, 's') != NULL);
ports = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, g_free,
(GDestroyNotify) free_host_ports);
/* Compare the results in the two iterators, which are sorted. */
g_debug (" delta: %s: start", __func__);
g_debug (" delta: %s: sort_field: %s", __func__, sort_field);
g_debug (" delta: %s: sort_order: %i", __func__, sort_order);
g_debug (" delta: %s: max_results: %i", __func__, max_results);
done = !next (results);
delta_done = !next (delta_results);
while (1)
{
GString *buffer;
compare_results_t state;
int used, would_use;
if (max_results == 0)
break;
if (done)
{
if (delta_done)
break;
if (new)
/* Extra results in 'delta_results'. */
do
{
const char *level;
g_debug (" delta: %s: extra from report 2: %s",
__func__,
result_iterator_nvt_oid (results));
if (first_result)
{
g_debug (" delta: skip");
first_result--;
continue;
}
/* Increase the result count. */
level = result_iterator_level (delta_results);
(*orig_filtered_result_count)++;
(*filtered_result_count)++;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)++;
(*f_holes)++;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)++;
(*f_warnings)++;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)++;
(*f_infos)++;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)++;
(*f_logs)++;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)++;
(*f_false_positives)++;
}
g_debug (" delta: %s: extra from report 2: %s",
__func__,
result_iterator_nvt_oid (delta_results));
buffer = g_string_new ("");
buffer_results_xml (buffer,
delta_results,
task,
notes,
notes_details,
overrides,
overrides_details,
0,
0,
0,
"new",
NULL,
0,
-1,
0);
if (fprintf (out, "%s", buffer->str) < 0)
return -1;
g_string_free (buffer, TRUE);
if (result_hosts_only)
array_add_new_string (result_hosts,
result_iterator_host (delta_results));
add_port (ports, delta_results);
max_results--;
if (max_results == 0)
break;
}
while (next (delta_results));
delta_done = TRUE;
break;
}
if (delta_done)
{
/* Extra results in 'results'. */
if (gone)
do
{
g_debug (" delta: %s: extra from report 1: %s",
__func__,
result_iterator_nvt_oid (results));
if (first_result)
{
g_debug (" delta: skip");
first_result--;
continue;
}
buffer = g_string_new ("");
buffer_results_xml (buffer,
results,
task,
notes,
notes_details,
overrides,
overrides_details,
0,
0,
0,
"gone",
NULL,
0,
-1,
0);
if (fprintf (out, "%s", buffer->str) < 0)
return -1;
g_string_free (buffer, TRUE);
if (result_hosts_only)
array_add_new_string (result_hosts,
result_iterator_host (results));
add_port (ports, results);
max_results--;
if (max_results == 0)
break;
}
while (next (results));
else
do
{
const char *level;
/* Decrease the result count. */
level = result_iterator_level (results);
(*orig_filtered_result_count)--;
(*filtered_result_count)--;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)--;
(*f_holes)--;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)--;
(*f_warnings)--;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)--;
(*f_infos)--;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)--;
(*f_logs)--;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)--;
(*f_false_positives)--;
}
}
while (next (results));
done = TRUE;
break;
}
/* Compare the two results. */
buffer = g_string_new ("");
state = compare_and_buffer_results (buffer,
results,
delta_results,
task,
notes,
notes_details,
overrides,
overrides_details,
sort_order,
sort_field,
changed,
gone,
new,
same,
&max_results,
&first_result,
&used,
&would_use);
if (state == COMPARE_RESULTS_ERROR)
{
g_warning ("%s: compare_and_buffer_results failed",
__func__);
return -1;
}
if (fprintf (out, "%s", buffer->str) < 0)
return -1;
g_string_free (buffer, TRUE);
if ((used == 0)
&& ((state == COMPARE_RESULTS_GONE)
|| (state == COMPARE_RESULTS_SAME)
|| (state == COMPARE_RESULTS_CHANGED)))
{
const char *level;
/* Decrease the result count. */
level = result_iterator_level (results);
(*filtered_result_count)--;
if (strcmp (level, "High") == 0)
{
(*f_holes)--;
}
else if (strcmp (level, "Medium") == 0)
{
(*f_warnings)--;
}
else if (strcmp (level, "Low") == 0)
{
(*f_infos)--;
}
else if (strcmp (level, "Log") == 0)
{
(*f_logs)--;
}
else if (strcmp (level, "False Positive") == 0)
{
(*f_false_positives)--;
}
}
if ((would_use == 0)
&& ((state == COMPARE_RESULTS_GONE)
|| (state == COMPARE_RESULTS_SAME)
|| (state == COMPARE_RESULTS_CHANGED)))
{
const char *level;
/* Decrease the result count. */
level = result_iterator_level (results);
(*orig_filtered_result_count)--;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)--;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)--;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)--;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)--;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)--;
}
}
/* Move on to the next. */
if (state == COMPARE_RESULTS_GONE)
{
/* "Used" just the 'results' result. */
if (used)
{
if (result_hosts_only)
array_add_new_string (result_hosts,
result_iterator_host (results));
add_port (ports, results);
}
done = !next (results);
}
else if ((state == COMPARE_RESULTS_SAME)
|| (state == COMPARE_RESULTS_CHANGED))
{
/* "Used" both results. */
if (used)
{
if (result_hosts_only)
array_add_new_string (result_hosts,
result_iterator_host (results));
add_port (ports, results);
}
done = !next (results);
delta_done = !next (delta_results);
}
else if (state == COMPARE_RESULTS_NEW)
{
if (would_use)
{
const char *level;
/* Would have "used" just the 'delta_results' result, on
* an earlier page. */
/* Increase the result count. */
level = result_iterator_level (delta_results);
(*orig_filtered_result_count)++;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)++;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)++;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)++;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)++;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)++;
}
}
if (used)
{
const char *level;
/* "Used" just the 'delta_results' result. */
/* Increase the result count. */
level = result_iterator_level (delta_results);
(*filtered_result_count)++;
if (strcmp (level, "High") == 0)
{
(*f_holes)++;
}
else if (strcmp (level, "Medium") == 0)
{
(*f_warnings)++;
}
else if (strcmp (level, "Low") == 0)
{
(*f_infos)++;
}
else if (strcmp (level, "Log") == 0)
{
(*f_logs)++;
}
else if (strcmp (level, "False Positive") == 0)
{
(*f_false_positives)++;
}
if (result_hosts_only)
array_add_new_string (result_hosts,
result_iterator_host
(delta_results));
add_port (ports, delta_results);
}
delta_done = !next (delta_results);
}
else
assert (0);
}
/* Compare remaining results, for the filtered report counts. */
g_debug (" delta: %s: counting rest", __func__);
while (1)
{
compare_results_t state;
int used, would_use;
if (done)
{
if (delta_done)
break;
if (new)
/* Extra results in 'delta_results'. */
do
{
const char *level;
g_debug (" delta: %s: extra from report 2: %s",
__func__,
result_iterator_nvt_oid (delta_results));
/* Increase the result count. */
level = result_iterator_level (delta_results);
(*orig_filtered_result_count)++;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)++;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)++;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)++;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)++;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)++;
}
}
while (next (delta_results));
break;
}
if (delta_done)
{
/* Extra results in 'results'. */
if (gone)
do
{
g_debug (" delta: %s: extra from report 1: %s",
__func__,
result_iterator_nvt_oid (results));
/* It's in the count already. */
}
while (next (results));
else
do
{
const char *level;
/* Decrease the result count. */
level = result_iterator_level (results);
(*orig_filtered_result_count)--;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)--;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)--;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)--;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)--;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)--;
}
}
while (next (results));
break;
}
/* Compare the two results. */
state = compare_and_buffer_results (NULL,
results,
delta_results,
task,
notes,
notes_details,
overrides,
overrides_details,
sort_order,
sort_field,
changed,
gone,
new,
same,
&max_results,
&first_result,
&used,
&would_use);
if (state == COMPARE_RESULTS_ERROR)
{
g_warning ("%s: compare_and_buffer_results failed",
__func__);
return -1;
}
if (state == COMPARE_RESULTS_NEW)
{
if (used)
{
const char *level;
/* "Used" just the 'delta_results' result. */
/* Increase the result count. */
level = result_iterator_level (delta_results);
(*orig_filtered_result_count)++;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)++;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)++;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)++;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)++;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)++;
}
}
}
else if (used)
{
/* It's in the count already. */
}
else
{
const char *level;
/* Decrease the result count. */
level = result_iterator_level (results);
(*orig_filtered_result_count)--;
if (strcmp (level, "High") == 0)
{
(*orig_f_holes)--;
}
else if (strcmp (level, "Medium") == 0)
{
(*orig_f_warnings)--;
}
else if (strcmp (level, "Low") == 0)
{
(*orig_f_infos)--;
}
else if (strcmp (level, "Log") == 0)
{
(*orig_f_logs)--;
}
else if (strcmp (level, "False Positive") == 0)
{
(*orig_f_false_positives)--;
}
}
/* Move on to the next. */
if (state == COMPARE_RESULTS_GONE)
{
/* "Used" just the 'results' result. */
done = !next (results);
}
else if ((state == COMPARE_RESULTS_SAME)
|| (state == COMPARE_RESULTS_CHANGED))
{
/* "Used" both results. */
done = !next (results);
delta_done = !next (delta_results);
}
else if (state == COMPARE_RESULTS_NEW)
{
/* "Used" just the 'delta_results' result. */
delta_done = !next (delta_results);
}
else
assert (0);
}
msg = g_markup_printf_escaped ("");
if (fprintf (out, "%s", msg) < 0)
{
g_free (msg);
fclose (out);
return -1;
}
g_free (msg);
/* Write ports to file. */
msg = g_markup_printf_escaped ("",
/* Add 1 for 1 indexing. */
first_result + 1,
max_results);
if (fprintf (out, "%s", msg) < 0)
{
g_free (msg);
fclose (out);
return -1;
}
g_free (msg);
if (sort_field == NULL || strcmp (sort_field, "port"))
{
if (sort_order)
g_tree_foreach (ports, print_host_ports_by_severity_asc, out);
else
g_tree_foreach (ports, print_host_ports_by_severity_desc, out);
}
else if (sort_order)
g_tree_foreach (ports, print_host_ports, out);
else
g_tree_foreach (ports, print_host_ports_desc, out);
g_tree_destroy (ports);
msg = g_markup_printf_escaped ("");
if (fprintf (out, "%s", msg) < 0)
{
g_free (msg);
fclose (out);
return -1;
}
g_free (msg);
return 0;
}
/**
* @brief Print the main XML content for a report to a file.
*
* @param[in] report The report.
* @param[in] delta Report to compare with the report.
* @param[in] task Task associated with report.
* @param[in] xml_start File name.
* @param[in] get GET command data.
* @param[in] notes_details If notes, Whether to include details.
* @param[in] overrides_details If overrides, Whether to include details.
* @param[in] result_tags Whether to include tags in results.
* @param[in] ignore_pagination Whether to ignore pagination data.
* @param[in] lean Whether to return lean report.
* @param[out] filter_term_return Filter term used in report.
* @param[out] zone_return Actual timezone used in report.
* @param[out] host_summary Summary of results per host.
*
* @return 0 on success, -1 error, 2 failed to find filter (before any printing).
*/
static int
print_report_xml_start (report_t report, report_t delta, task_t task,
gchar* xml_start, const get_data_t *get,
int notes_details, int overrides_details,
int result_tags, int ignore_pagination, int lean,
gchar **filter_term_return, gchar **zone_return,
gchar **host_summary)
{
int result_hosts_only;
int notes, overrides;
int first_result, max_results, sort_order;
FILE *out;
gchar *clean, *term, *sort_field, *levels, *search_phrase;
gchar *min_qod;
gchar *delta_states, *timestamp;
int min_qod_int;
char *uuid, *tsk_uuid = NULL, *start_time, *end_time;
int total_result_count, filtered_result_count;
array_t *result_hosts;
int reuse_result_iterator;
iterator_t results, delta_results;
int holes, infos, logs, warnings, false_positives;
int f_holes, f_infos, f_logs, f_warnings, f_false_positives;
int orig_f_holes, orig_f_infos, orig_f_logs;
int orig_f_warnings, orig_f_false_positives, orig_filtered_result_count;
int search_phrase_exact, apply_overrides, count_filtered;
double severity, f_severity;
gchar *tz, *zone;
char *old_tz_override;
GString *filters_buffer, *filters_extra_buffer, *host_summary_buffer;
gchar *term_value;
GHashTable *f_host_ports;
GHashTable *f_host_holes, *f_host_warnings, *f_host_infos;
GHashTable *f_host_logs, *f_host_false_positives;
task_status_t run_status;
/* Init some vars to prevent warnings from older compilers. */
max_results = -1;
levels = NULL;
zone = NULL;
delta_states = NULL;
min_qod = NULL;
search_phrase = NULL;
total_result_count = filtered_result_count = 0;
orig_filtered_result_count = 0;
orig_f_false_positives = orig_f_warnings = orig_f_logs = orig_f_infos = 0;
orig_f_holes = 0;
f_host_ports = NULL;
f_host_holes = NULL;
f_host_warnings = NULL;
f_host_infos = NULL;
f_host_logs = NULL;
f_host_false_positives = NULL;
/** @todo Leaks on error in PRINT and PRINT_XML. The process normally exits
* then anyway. */
/* run_status is set by report_scan_run_status when either of "delta" and
* "report" are true. run_status is only used by run_status_name, only when
* either of "delta" and "report" are true, and only after a
* report_scan_run_status call. Still GCC 4.4.5 (Debian 4.4.5-8) gives a
* "may be used uninitialized" warning, so init it here to quiet the
* warning. */
run_status = TASK_STATUS_INTERRUPTED;
if (report == 0)
{
assert (0);
return -1;
}
out = fopen (xml_start, "w");
if (out == NULL)
{
g_warning ("%s: fopen failed: %s",
__func__,
strerror (errno));
return -1;
}
assert (get);
if ((get->filt_id && strlen (get->filt_id)
&& strcmp (get->filt_id, FILT_ID_NONE))
|| (get->filter && strlen (get->filter)))
{
term = NULL;
if (get->filt_id && strlen (get->filt_id)
&& strcmp (get->filt_id, FILT_ID_NONE))
{
term = filter_term (get->filt_id);
if (term == NULL)
{
fclose (out);
return 2;
}
}
/* Set the filter parameters from the filter term. */
manage_report_filter_controls (term ? term : get->filter,
&first_result, &max_results, &sort_field,
&sort_order, &result_hosts_only,
&min_qod, &levels, &delta_states,
&search_phrase, &search_phrase_exact,
¬es, &overrides,
&apply_overrides, &zone);
}
else
{
term = g_strdup ("");
/* Set the filter parameters to defaults */
manage_report_filter_controls (term,
&first_result, &max_results, &sort_field,
&sort_order, &result_hosts_only,
&min_qod, &levels, &delta_states,
&search_phrase, &search_phrase_exact,
¬es, &overrides,
&apply_overrides, &zone);
}
max_results = manage_max_rows (max_results);
levels = levels ? levels : g_strdup ("hmlgd");
if (task && task_uuid (task, &tsk_uuid))
{
fclose (out);
g_free (term);
g_free (levels);
g_free (search_phrase);
g_free (min_qod);
g_free (delta_states);
return -1;
}
if (zone && strlen (zone))
{
gchar *quoted_zone;
/* Store current TZ. */
tz = getenv ("TZ") ? g_strdup (getenv ("TZ")) : NULL;
if (setenv ("TZ", zone, 1) == -1)
{
g_warning ("%s: Failed to switch to timezone", __func__);
if (tz != NULL)
setenv ("TZ", tz, 1);
g_free (tz);
g_free (zone);
return -1;
}
old_tz_override = sql_string ("SELECT current_setting"
" ('gvmd.tz_override');");
quoted_zone = sql_insert (zone);
sql ("SET SESSION \"gvmd.tz_override\" = %s;", quoted_zone);
g_free (quoted_zone);
tzset ();
}
else
{
/* Keep compiler quiet. */
tz = NULL;
old_tz_override = NULL;
}
if (delta && report)
{
uuid = report_uuid (report);
PRINT (out, "", uuid);
free (uuid);
}
else
{
uuid = report_uuid (report);
PRINT (out, "", uuid);
free (uuid);
}
PRINT (out, "%s", GMP_VERSION);
if (delta)
{
delta_states = delta_states ? delta_states : g_strdup ("cgns");
report_scan_run_status (delta, &run_status);
uuid = report_uuid (delta);
PRINT (out,
""
""
"%s",
uuid,
run_status_name (run_status
? run_status
: TASK_STATUS_INTERRUPTED));
if (report_timestamp (uuid, ×tamp))
{
free (uuid);
g_free (levels);
g_free (search_phrase);
g_free (min_qod);
g_free (delta_states);
tz_revert (zone, tz, old_tz_override);
return -1;
}
PRINT (out,
"%s",
timestamp);
g_free (timestamp);
start_time = scan_start_time (delta);
PRINT (out,
"%s",
start_time);
free (start_time);
end_time = scan_end_time (delta);
PRINT (out,
"%s",
end_time);
free (end_time);
PRINT (out,
""
"");
}
count_filtered = (delta == 0 && ignore_pagination && get->details);
if (report)
{
/* Get total counts of full results. */
if (delta == 0)
{
int total_holes, total_infos, total_logs;
int total_warnings, total_false_positives;
get_data_t *all_results_get;
all_results_get = report_results_get_data (1, -1, 0, 0);
report_counts_id (report, &total_holes, &total_infos,
&total_logs, &total_warnings,
&total_false_positives, NULL, all_results_get,
NULL);
total_result_count = total_holes + total_infos
+ total_logs + total_warnings
+ total_false_positives;
get_data_reset (all_results_get);
free (all_results_get);
}
/* Get total counts of filtered results. */
if (count_filtered)
{
/* We're getting all the filtered results, so we can count them as we
* print them, to save time. */
filtered_result_count = 0;
}
else
{
/* Beware, we're using the full variables temporarily here, but
* report_counts_id counts the filtered results. */
report_counts_id (report, &holes, &infos, &logs, &warnings,
&false_positives, NULL, get, NULL);
filtered_result_count = holes + infos + logs + warnings
+ false_positives;
}
/* Get report run status. */
report_scan_run_status (report, &run_status);
}
clean = manage_clean_filter (term
? term
: (get->filter ? get->filter : ""));
term_value = filter_term_value (clean, "min_qod");
if (term_value == NULL)
{
gchar *new_filter;
new_filter = g_strdup_printf ("min_qod=%i %s",
MIN_QOD_DEFAULT,
clean);
g_free (clean);
clean = new_filter;
}
g_free (term_value);
term_value = filter_term_value (clean, "apply_overrides");
if (term_value == NULL)
{
gchar *new_filter;
new_filter = g_strdup_printf ("apply_overrides=%i %s",
APPLY_OVERRIDES_DEFAULT,
clean);
g_free (clean);
clean = new_filter;
}
g_free (term_value);
g_free (term);
term = clean;
if (delta
&& sort_field
/* These are all checked in result_cmp. */
&& strcmp (sort_field, "name")
&& strcmp (sort_field, "vulnerability")
&& strcmp (sort_field, "host")
&& strcmp (sort_field, "port")
&& strcmp (sort_field, "location")
&& strcmp (sort_field, "severity")
&& strcmp (sort_field, "nvt")
&& strcmp (sort_field, "description")
&& strcmp (sort_field, "type")
&& strcmp (sort_field, "original_type"))
{
gchar *new_term;
if ((strcmp (sort_field, "task") == 0)
|| (strcmp (sort_field, "task_id") == 0)
|| (strcmp (sort_field, "report_id") == 0))
{
/* These don't affect delta report, so sort by vulnerability. */
g_free (sort_field);
sort_field = g_strdup ("vulnerability");
}
else
{
/* The remaining filterable fields for the result iterator, all of
* which may be used as a sort field. These could be added to
* result_cmp. For now sort by vulnerability. */
#if 0
"uuid", "comment", "created", "modified", "_owner",
"cvss_base", "nvt_version", "original_severity", "date",
"solution_type", "qod", "qod_type", "cve", "hostname", "path"
#endif
g_free (sort_field);
sort_field = g_strdup ("vulnerability");
}
/* Adjust "term" to match sort_field, because "term" will be used in the
* REPORT XML FILTERS (sent by buffer_get_filter_xml below). */
new_term = g_strdup_printf ("sort=%s %s",
sort_field,
term);
g_free (term);
term = new_term;
/* Similarly, the order will now be ascending. */
sort_order = 1;
}
if (filter_term_return)
*filter_term_return = g_strdup (term);
PRINT
(out,
"%s%s",
sort_field ? sort_field : "type",
sort_order ? "ascending" : "descending");
filters_extra_buffer = g_string_new ("");
if (strchr (levels, 'h'))
g_string_append (filters_extra_buffer, "High");
if (strchr (levels, 'm'))
g_string_append (filters_extra_buffer, "Medium");
if (strchr (levels, 'l'))
g_string_append (filters_extra_buffer, "Low");
if (strchr (levels, 'g'))
g_string_append (filters_extra_buffer, "Log");
if (strchr (levels, 'f'))
g_string_append (filters_extra_buffer, "False Positive");
if (delta)
{
gchar *escaped_delta_states = g_markup_escape_text (delta_states, -1);
g_string_append_printf (filters_extra_buffer,
""
"%s"
"%i"
"%i"
"%i"
"%i"
"",
escaped_delta_states,
strchr (delta_states, 'c') != NULL,
strchr (delta_states, 'g') != NULL,
strchr (delta_states, 'n') != NULL,
strchr (delta_states, 's') != NULL);
g_free (escaped_delta_states);
}
filters_buffer = g_string_new ("");
buffer_get_filter_xml (filters_buffer, "result", get, term,
filters_extra_buffer->str);
g_string_free (filters_extra_buffer, TRUE);
PRINT_XML (out, filters_buffer->str);
g_string_free (filters_buffer, TRUE);
if (report)
{
int tag_count = resource_tag_count ("report", report, 1);
if (tag_count)
{
PRINT (out,
""
"%i",
tag_count);
if (get->details || get->id)
{
iterator_t tags;
init_resource_tag_iterator (&tags, "report", report, 1, NULL, 1);
while (next (&tags))
{
PRINT (out,
""
"%s"
"%s"
"%s"
"",
resource_tag_iterator_uuid (&tags),
resource_tag_iterator_name (&tags),
resource_tag_iterator_value (&tags),
resource_tag_iterator_comment (&tags));
}
cleanup_iterator (&tags);
}
PRINT (out, "");
}
}
if (report)
{
PRINT
(out,
"%s",
run_status_name (run_status
? run_status
: TASK_STATUS_INTERRUPTED));
PRINT (out,
"%i",
report_host_count (report));
PRINT (out,
"%i",
report_closed_cve_count (report));
PRINT (out,
"%i",
report_vuln_count (report));
PRINT (out,
"%i",
report_os_count (report));
PRINT (out,
"%i",
report_app_count (report));
PRINT (out,
"%i",
report_ssl_cert_count (report));
}
if (task && tsk_uuid)
{
char *tsk_name, *task_target_uuid, *task_target_name;
char *task_target_comment, *comment;
target_t target;
gchar *progress_xml;
iterator_t tags;
int task_tag_count = resource_tag_count ("task", task, 1);
tsk_name = task_name (task);
comment = task_comment (task);
target = task_target (task);
if (task_target_in_trash (task))
{
task_target_uuid = trash_target_uuid (target);
task_target_name = trash_target_name (target);
task_target_comment = trash_target_comment (target);
}
else
{
task_target_uuid = target_uuid (target);
task_target_name = target_name (target);
task_target_comment = target_comment (target);
}
if ((target == 0)
&& (task_run_status (task) == TASK_STATUS_RUNNING))
progress_xml = g_strdup_printf
("%i",
task_upload_progress (task));
else
{
int progress;
progress = report_progress (report);
progress_xml = g_strdup_printf ("%i", progress);
}
PRINT (out,
""
"%s"
"%s"
""
"%i"
"%s"
"%s"
""
"",
tsk_uuid,
tsk_name ? tsk_name : "",
comment ? comment : "",
task_target_uuid ? task_target_uuid : "",
task_target_in_trash (task),
task_target_name ? task_target_name : "",
task_target_comment ? task_target_comment : "",
progress_xml);
g_free (progress_xml);
free (comment);
free (tsk_name);
free (tsk_uuid);
free (task_target_uuid);
free (task_target_name);
free (task_target_comment);
if (task_tag_count)
{
PRINT (out,
""
"%i",
task_tag_count);
init_resource_tag_iterator (&tags, "task", task, 1, NULL, 1);
while (next (&tags))
{
PRINT (out,
""
"%s"
"%s"
"%s"
"",
resource_tag_iterator_uuid (&tags),
resource_tag_iterator_name (&tags),
resource_tag_iterator_value (&tags),
resource_tag_iterator_comment (&tags));
}
cleanup_iterator (&tags);
PRINT (out,
"");
}
PRINT (out,
"");
{
char *source_iface;
/* Info about the situation at the time of scan. */
PRINT (out,
""
"");
source_iface = report_source_iface (report);
if (source_iface)
/* VALUE "" means preference was not set. Missing PREFERENCE means
* we don't know. */
PRINT (out,
""
""
"Network Source Interface"
"source_iface"
"%s"
""
"",
source_iface);
free (source_iface);
PRINT (out,
""
"");
}
}
uuid = report_uuid (report);
if (report_timestamp (uuid, ×tamp))
{
free (uuid);
g_free (term);
tz_revert (zone, tz, old_tz_override);
return -1;
}
free (uuid);
PRINT (out,
"%s",
timestamp);
g_free (timestamp);
start_time = scan_start_time (report);
PRINT (out,
"%s",
start_time);
free (start_time);
{
time_t start_time_epoch;
const char *abbrev;
gchar *report_zone;
start_time_epoch = scan_start_time_epoch (report);
abbrev = NULL;
if (zone && strlen (zone))
report_zone = g_strdup (zone);
else
report_zone = setting_timezone ();
iso_time_tz (&start_time_epoch, report_zone, &abbrev);
if (zone_return)
*zone_return = g_strdup (report_zone ? report_zone : "");
PRINT (out,
"%s"
"%s",
report_zone
? report_zone
: "Coordinated Universal Time",
abbrev ? abbrev : "UTC");
g_free (report_zone);
}
/* Port summary. */
f_host_ports = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
reuse_result_iterator = 0;
if (get->details && (delta == 0))
{
reuse_result_iterator = 1;
if (print_report_port_xml (report, out, get, first_result, max_results,
sort_order, sort_field, f_host_ports, &results))
{
g_free (term);
tz_revert (zone, tz, old_tz_override);
g_hash_table_destroy (f_host_ports);
return -1;
}
}
/* Prepare result counts. */
if (count_filtered)
{
/* We're getting all the filtered results, so we can count them as we
* print them, to save time. */
report_counts_id_full (report, &holes, &infos, &logs,
&warnings, &false_positives, &severity,
get, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
f_holes = f_infos = f_logs = f_warnings = 0;
f_false_positives = f_severity = 0;
}
else
report_counts_id_full (report, &holes, &infos, &logs,
&warnings, &false_positives, &severity,
get, NULL,
&f_holes, &f_infos, &f_logs, &f_warnings,
&f_false_positives, &f_severity);
/* Results. */
if (min_qod == NULL || sscanf (min_qod, "%d", &min_qod_int) != 1)
min_qod_int = MIN_QOD_DEFAULT;
if (delta && get->details)
{
if (init_delta_iterators (report, &results, delta, &delta_results, get,
term, sort_field))
{
g_free (term);
g_hash_table_destroy (f_host_ports);
return -1;
}
g_free (term);
}
else if (get->details)
{
int res;
g_free (term);
if (reuse_result_iterator)
iterator_rewind (&results);
else
{
res = init_result_get_iterator (&results, get, report, NULL, NULL);
if (res)
{
g_hash_table_destroy (f_host_ports);
return -1;
}
}
}
else
g_free (term);
if (get->details)
PRINT (out,
"",
/* Add 1 for 1 indexing. */
ignore_pagination ? 1 : first_result + 1,
ignore_pagination ? -1 : max_results);
if (get->details && result_hosts_only)
result_hosts = make_array ();
else
/* Quiet erroneous compiler warning. */
result_hosts = NULL;
f_host_holes = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
f_host_warnings = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
f_host_infos = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
f_host_logs = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
f_host_false_positives = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
if (delta && get->details)
{
if (print_report_delta_xml (out, &results, &delta_results, delta_states,
ignore_pagination ? 1 : first_result,
ignore_pagination ? -1 : max_results,
task, notes,
notes_details, overrides, overrides_details,
sort_order, sort_field, result_hosts_only,
&orig_filtered_result_count,
&filtered_result_count,
&orig_f_holes, &f_holes,
&orig_f_infos, &f_infos,
&orig_f_logs, &f_logs,
&orig_f_warnings, &f_warnings,
&orig_f_false_positives, &f_false_positives,
result_hosts))
{
fclose (out);
g_free (sort_field);
g_free (levels);
g_free (search_phrase);
g_free (min_qod);
g_free (delta_states);
cleanup_iterator (&results);
cleanup_iterator (&delta_results);
tz_revert (zone, tz, old_tz_override);
g_hash_table_destroy (f_host_ports);
g_hash_table_destroy (f_host_holes);
g_hash_table_destroy (f_host_warnings);
g_hash_table_destroy (f_host_infos);
g_hash_table_destroy (f_host_logs);
g_hash_table_destroy (f_host_false_positives);
return -1;
}
}
else if (get->details)
{
int cert_loaded;
cert_loaded = manage_cert_loaded ();
while (next (&results))
{
const char* level;
GHashTable *f_host_result_counts;
GString *buffer = g_string_new ("");
double result_severity;
buffer_results_xml (buffer,
&results,
task,
notes,
notes_details,
overrides,
overrides_details,
result_tags,
1,
0,
NULL,
NULL,
0,
cert_loaded,
lean);
PRINT_XML (out, buffer->str);
g_string_free (buffer, TRUE);
if (result_hosts_only)
array_add_new_string (result_hosts,
result_iterator_host (&results));
result_severity = result_iterator_severity_double (&results);
if (result_severity > f_severity)
f_severity = result_severity;
level = result_iterator_level (&results);
if (strcasecmp (level, "log") == 0)
{
f_host_result_counts = f_host_logs;
if (count_filtered)
f_logs++;
}
else if (strcasecmp (level, "high") == 0)
{
f_host_result_counts = f_host_holes;
if (count_filtered)
f_holes++;
}
else if (strcasecmp (level, "medium") == 0)
{
f_host_result_counts = f_host_warnings;
if (count_filtered)
f_warnings++;
}
else if (strcasecmp (level, "low") == 0)
{
f_host_result_counts = f_host_infos;
if (count_filtered)
f_infos++;
}
else if (strcasecmp (level, "false positive") == 0)
{
f_host_result_counts = f_host_false_positives;
if (count_filtered)
f_false_positives++;
}
else
f_host_result_counts = NULL;
if (f_host_result_counts)
{
const char *result_host = result_iterator_host (&results);
int result_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_result_counts, result_host));
g_hash_table_replace (f_host_result_counts,
g_strdup (result_host),
GINT_TO_POINTER (result_count + 1));
}
}
PRINT (out, "");
}
if (get->details)
cleanup_iterator (&results);
if (delta && get->details)
cleanup_iterator (&delta_results);
/* Print result counts and severity. */
if (delta)
/** @todo The f_holes, etc. vars are setup to give the page count. */
PRINT (out,
""
"%i"
"%i"
"%i"
"%i"
"%i"
""
"%i"
""
"",
orig_filtered_result_count,
(strchr (levels, 'h') ? orig_f_holes : 0),
(strchr (levels, 'l') ? orig_f_infos : 0),
(strchr (levels, 'g') ? orig_f_logs : 0),
(strchr (levels, 'm') ? orig_f_warnings : 0),
(strchr (levels, 'f') ? orig_f_false_positives : 0));
else
{
if (count_filtered)
filtered_result_count = f_holes + f_infos + f_logs
+ f_warnings + false_positives;
PRINT (out,
""
"%i"
"%i"
"%i"
"%i%i"
"%i%i"
"%i%i"
"%i%i"
""
"%i"
"%i"
""
"",
total_result_count,
total_result_count,
filtered_result_count,
holes,
(strchr (levels, 'h') ? f_holes : 0),
infos,
(strchr (levels, 'l') ? f_infos : 0),
logs,
(strchr (levels, 'g') ? f_logs : 0),
warnings,
(strchr (levels, 'm') ? f_warnings : 0),
false_positives,
(strchr (levels, 'f') ? f_false_positives : 0));
PRINT (out,
""
"%1.1f"
"%1.1f"
"",
severity,
f_severity);
}
if (host_summary)
{
host_summary_buffer = g_string_new ("");
g_string_append_printf (host_summary_buffer,
" %-15s %-16s End\n",
"Host", "Start");
}
else
host_summary_buffer = NULL;
if (get->details && result_hosts_only)
{
gchar *result_host;
int index = 0;
array_terminate (result_hosts);
while ((result_host = g_ptr_array_index (result_hosts, index++)))
{
gboolean present;
iterator_t hosts;
init_report_host_iterator (&hosts, report, result_host, 0);
present = next (&hosts);
if (delta && (present == FALSE))
{
cleanup_iterator (&hosts);
init_report_host_iterator (&hosts, delta, result_host, 0);
present = next (&hosts);
}
if (present)
{
const char *current_host;
int ports_count;
int holes_count, warnings_count, infos_count;
int logs_count, false_positives_count;
current_host = host_iterator_host (&hosts);
ports_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_ports, current_host));
holes_count
= GPOINTER_TO_INT
(g_hash_table_lookup ( f_host_holes, current_host));
warnings_count
= GPOINTER_TO_INT
(g_hash_table_lookup ( f_host_warnings, current_host));
infos_count
= GPOINTER_TO_INT
(g_hash_table_lookup ( f_host_infos, current_host));
logs_count
= GPOINTER_TO_INT
(g_hash_table_lookup ( f_host_logs, current_host));
false_positives_count
= GPOINTER_TO_INT
(g_hash_table_lookup ( f_host_false_positives, current_host));
host_summary_append (host_summary_buffer,
result_host,
host_iterator_start_time (&hosts),
host_iterator_end_time (&hosts));
PRINT (out,
""
"%s",
result_host);
if (host_iterator_asset_uuid (&hosts)
&& strlen (host_iterator_asset_uuid (&hosts)))
PRINT (out,
"",
host_iterator_asset_uuid (&hosts));
else if (lean == 0)
PRINT (out,
"");
PRINT (out,
"%s"
"%s"
"%d"
""
"%d"
"%d"
"%d"
"%d"
"%d"
"%d"
"",
host_iterator_start_time (&hosts),
host_iterator_end_time (&hosts)
? host_iterator_end_time (&hosts)
: "",
ports_count,
(holes_count + warnings_count + infos_count
+ logs_count + false_positives_count),
holes_count,
warnings_count,
infos_count,
logs_count,
false_positives_count);
if (print_report_host_details_xml
(host_iterator_report_host (&hosts), out, lean))
{
tz_revert (zone, tz, old_tz_override);
if (host_summary_buffer)
g_string_free (host_summary_buffer, TRUE);
g_hash_table_destroy (f_host_ports);
g_hash_table_destroy (f_host_holes);
g_hash_table_destroy (f_host_warnings);
g_hash_table_destroy (f_host_infos);
g_hash_table_destroy (f_host_logs);
g_hash_table_destroy (f_host_false_positives);
return -1;
}
PRINT (out,
"");
}
cleanup_iterator (&hosts);
}
array_free (result_hosts);
}
else if (get->details)
{
iterator_t hosts;
init_report_host_iterator (&hosts, report, NULL, 0);
while (next (&hosts))
{
const char *current_host;
int ports_count;
int holes_count, warnings_count, infos_count;
int logs_count, false_positives_count;
current_host = host_iterator_host (&hosts);
ports_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_ports, current_host));
holes_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_holes, current_host));
warnings_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_warnings, current_host));
infos_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_infos, current_host));
logs_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_logs, current_host));
false_positives_count
= GPOINTER_TO_INT
(g_hash_table_lookup (f_host_false_positives, current_host));
host_summary_append (host_summary_buffer,
host_iterator_host (&hosts),
host_iterator_start_time (&hosts),
host_iterator_end_time (&hosts));
PRINT (out,
""
"%s",
host_iterator_host (&hosts));
if (host_iterator_asset_uuid (&hosts)
&& strlen (host_iterator_asset_uuid (&hosts)))
PRINT (out,
"",
host_iterator_asset_uuid (&hosts));
else if (lean == 0)
PRINT (out,
"");
PRINT (out,
"%s"
"%s"
"%d"
""
"%d"
"%d"
"%d"
"%d"
"%d"
"%d"
"",
host_iterator_start_time (&hosts),
host_iterator_end_time (&hosts)
? host_iterator_end_time (&hosts)
: "",
ports_count,
(holes_count + warnings_count + infos_count
+ logs_count + false_positives_count),
holes_count,
warnings_count,
infos_count,
logs_count,
false_positives_count);
if (print_report_host_details_xml
(host_iterator_report_host (&hosts), out, lean))
{
tz_revert (zone, tz, old_tz_override);
if (host_summary_buffer)
g_string_free (host_summary_buffer, TRUE);
g_hash_table_destroy (f_host_ports);
g_hash_table_destroy (f_host_holes);
g_hash_table_destroy (f_host_warnings);
g_hash_table_destroy (f_host_infos);
g_hash_table_destroy (f_host_logs);
g_hash_table_destroy (f_host_false_positives);
return -1;
}
PRINT (out,
"");
}
cleanup_iterator (&hosts);
}
g_hash_table_destroy (f_host_ports);
g_hash_table_destroy (f_host_holes);
g_hash_table_destroy (f_host_warnings);
g_hash_table_destroy (f_host_infos);
g_hash_table_destroy (f_host_logs);
g_hash_table_destroy (f_host_false_positives);
end_time = scan_end_time (report);
PRINT (out,
"%s",
end_time);
free (end_time);
if (delta == 0 && print_report_errors_xml (report, out))
{
tz_revert (zone, tz, old_tz_override);
if (host_summary_buffer)
g_string_free (host_summary_buffer, TRUE);
return -1;
}
g_free (sort_field);
g_free (levels);
g_free (search_phrase);
g_free (min_qod);
g_free (delta_states);
if (host_summary && host_summary_buffer)
*host_summary = g_string_free (host_summary_buffer, FALSE);
if (fclose (out))
{
g_warning ("%s: fclose failed: %s",
__func__,
strerror (errno));
return -1;
}
return 0;
}
/**
* @brief Generate a report.
*
* @param[in] report Report.
* @param[in] delta_report Report to compare with.
* @param[in] get GET data for report.
* @param[in] report_format Report format.
* @param[in] notes_details If notes, Whether to include details.
* @param[in] overrides_details If overrides, Whether to include details.
* @param[out] output_length NULL or location for length of return.
* @param[out] extension NULL or location for report format extension.
* Only defined on success.
* @param[out] content_type NULL or location for report format content
* type. Only defined on success.
* @param[out] filter_term_return Filter term used in report.
* @param[out] zone_return Actual timezone used in report.
* @param[out] host_summary Summary of results per host.
*
* @return Contents of report on success, NULL on error.
*/
gchar *
manage_report (report_t report, report_t delta_report, const get_data_t *get,
const report_format_t report_format,
int notes_details, int overrides_details,
gsize *output_length, gchar **extension, gchar **content_type,
gchar **filter_term_return, gchar **zone_return,
gchar **host_summary)
{
task_t task;
gchar *xml_start, *xml_file, *output_file;
char xml_dir[] = "/tmp/gvmd_XXXXXX";
int ret;
GList *used_rfps;
GError *get_error;
gchar *output;
gsize output_len;
used_rfps = NULL;
/* Print the report as XML to a file. */
if (((report_format_predefined (report_format) == 0)
&& (report_format_trust (report_format) != TRUST_YES))
|| (report_task (report, &task)))
{
return NULL;
}
if (mkdtemp (xml_dir) == NULL)
{
g_warning ("%s: mkdtemp failed", __func__);
return NULL;
}
xml_start = g_strdup_printf ("%s/report-start.xml", xml_dir);
ret = print_report_xml_start (report, delta_report, task, xml_start, get,
notes_details, overrides_details,
1 /* result_tags */,
0 /* ignore_pagination */,
0 /* lean */,
filter_term_return, zone_return, host_summary);
if (ret)
{
g_free (xml_start);
gvm_file_remove_recurse (xml_dir);
return NULL;
}
xml_file = g_strdup_printf ("%s/report.xml", xml_dir);
if (report_format > 0)
{
/* Apply report format(s) */
gchar* report_format_id = report_format_uuid (report_format);
output_file = apply_report_format (report_format_id,
xml_start, xml_file, xml_dir,
&used_rfps);
g_free (report_format_id);
}
else
{
print_report_xml_end(xml_start, xml_file, -1);
output_file = g_strdup(xml_file);
}
if (output_file == NULL)
{
g_warning ("%s: No file returned for report format", __func__);
}
g_free (xml_file);
g_free (xml_start);
/* Read the script output from file. */
if (output_file == NULL)
{
gvm_file_remove_recurse (xml_dir);
return NULL;
}
get_error = NULL;
g_file_get_contents (output_file,
&output,
&output_len,
&get_error);
g_free (output_file);
if (get_error)
{
g_warning ("%s: Failed to get output: %s",
__func__,
get_error->message);
g_error_free (get_error);
if (extension) g_free (*extension);
if (content_type) g_free (*content_type);
gvm_file_remove_recurse (xml_dir);
return NULL;
}
/* Remove the directory. */
gvm_file_remove_recurse (xml_dir);
/* Set convenience return parameters. */
if ((report_format > 0) && (extension || content_type))
{
iterator_t formats;
get_data_t report_format_get;
memset (&report_format_get, '\0', sizeof (report_format_get));
report_format_get.id = report_format_uuid(report_format);
init_report_format_iterator (&formats, &report_format_get);
if (next (&formats) == FALSE)
{
g_free (report_format_get.id);
cleanup_iterator (&formats);
return NULL;
}
assert (report_format_iterator_extension (&formats));
assert (report_format_iterator_content_type (&formats));
if (extension)
*extension = g_strdup (report_format_iterator_extension (&formats));
if (content_type)
*content_type = g_strdup (report_format_iterator_content_type (&formats));
cleanup_iterator (&formats);
g_free (report_format_get.id);
}
/* Return the output. */
if (output_length) *output_length = output_len;
return output;
}
/**
* @brief Size of base64 chunk in manage_send_report.
*/
#define MANAGE_SEND_REPORT_CHUNK64_SIZE 262144
/**
* @brief Size of file chunk in manage_send_report.
*/
#define MANAGE_SEND_REPORT_CHUNK_SIZE (MANAGE_SEND_REPORT_CHUNK64_SIZE * 3 / 4)
/**
* @brief Generate a report.
*
* @param[in] report Report.
* @param[in] delta_report Report to compare with.
* @param[in] report_format Report format.
* @param[in] get GET command data.
* @param[in] notes_details If notes, Whether to include details.
* @param[in] overrides_details If overrides, Whether to include details.
* @param[in] result_tags Whether to include tags in results.
* @param[in] ignore_pagination Whether to ignore pagination.
* @param[in] lean Whether to send lean report.
* @param[in] base64 Whether to base64 encode the report.
* @param[in] send Function to write to client.
* @param[in] send_data_1 Second argument to \p send.
* @param[in] send_data_2 Third argument to \p send.
* @param[in] alert_id ID of alert to escalate report with,
* instead of getting report. NULL to get
* report.
* @param[in] prefix Text to send to client before the report.
*
* @return 0 success, -1 error, -2 failed to find alert report format, -3 error
* during alert, -4 failed to find alert filter, 1 failed to find alert,
* 2 failed to find filter (before anything sent to client).
*/
int
manage_send_report (report_t report, report_t delta_report,
report_format_t report_format, const get_data_t *get,
int notes_details, int overrides_details, int result_tags,
int ignore_pagination, int lean, int base64,
gboolean (*send) (const char *,
int (*) (const char *, void*),
void*),
int (*send_data_1) (const char *, void*), void *send_data_2,
const char *alert_id,
const gchar* prefix)
{
task_t task;
gchar *xml_start, *xml_file;
char xml_dir[] = "/tmp/gvmd_XXXXXX";
int ret;
GList *used_rfps;
gchar *output_file;
char chunk[MANAGE_SEND_REPORT_CHUNK_SIZE + 1];
FILE *stream;
used_rfps = NULL;
if (report_task (report, &task))
return -1;
/* Escalate instead, if requested. */
if (alert_id)
{
alert_t alert = 0;
alert_condition_t condition;
alert_method_t method;
if (find_alert_with_permission (alert_id, &alert, "get_alerts"))
return -1;
if (alert == 0)
return 1;
condition = alert_condition (alert);
method = alert_method (alert);
ret = escalate_2 (alert, task, report, EVENT_TASK_RUN_STATUS_CHANGED,
(void*) TASK_STATUS_DONE, method, condition,
get, notes_details, overrides_details, NULL);
if (ret == -3)
return -4;
if (ret == -1)
return -3;
if (ret)
return -2;
return 0;
}
/* Print the report as XML to a file. */
if ((report_format > 0)
&& (report_format_predefined (report_format) == 0)
&& (report_format_trust (report_format) != TRUST_YES))
return -1;
if (mkdtemp (xml_dir) == NULL)
{
g_warning ("%s: mkdtemp failed", __func__);
return -1;
}
xml_start = g_strdup_printf ("%s/report-start.xml", xml_dir);
ret = print_report_xml_start (report, delta_report, task, xml_start, get,
notes_details, overrides_details, result_tags,
ignore_pagination, lean, NULL, NULL, NULL);
if (ret)
{
g_free (xml_start);
gvm_file_remove_recurse (xml_dir);
if (ret == 2)
return 2;
return -1;
}
xml_file = g_strdup_printf ("%s/report.xml", xml_dir);
if (report_format > 0)
{
/* Apply report format(s). */
gchar* report_format_id = report_format_uuid (report_format);
output_file = apply_report_format (report_format_id,
xml_start, xml_file, xml_dir,
&used_rfps);
g_free (report_format_id);
}
else
{
print_report_xml_end(xml_start, xml_file, -1);
output_file = g_strdup(xml_file);
}
if (output_file == NULL)
{
g_warning ("%s: No file returned for report format", __func__);
}
/* Send the report. */
/* Read the script output from file in chunks, sending to client. */
stream = fopen (output_file, "r");
g_free (output_file);
if (stream == NULL)
{
g_warning ("%s: %s",
__func__,
strerror (errno));
gvm_file_remove_recurse (xml_dir);
return -1;
}
if (prefix && send (prefix, send_data_1, send_data_2))
{
fclose (stream);
g_warning ("%s: send prefix error", __func__);
gvm_file_remove_recurse (xml_dir);
return -1;
}
while (1)
{
int left;
char *dest;
/* Read a chunk. */
left = MANAGE_SEND_REPORT_CHUNK_SIZE;
dest = chunk;
while (1)
{
ret = fread (dest, 1, left, stream);
if (ferror (stream))
{
fclose (stream);
g_warning ("%s: error after fread", __func__);
gvm_file_remove_recurse (xml_dir);
return -1;
}
left -= ret;
if (left == 0)
break;
if (feof (stream))
break;
dest += ret;
}
/* Send the chunk. */
if (left < MANAGE_SEND_REPORT_CHUNK_SIZE)
{
if (base64)
{
gchar *chunk64;
chunk64 = g_base64_encode ((guchar*) chunk,
MANAGE_SEND_REPORT_CHUNK_SIZE
- left);
if (send (chunk64, send_data_1, send_data_2))
{
g_free (chunk64);
fclose (stream);
g_warning ("%s: send error", __func__);
gvm_file_remove_recurse (xml_dir);
return -1;
}
g_free (chunk64);
}
else
{
chunk[MANAGE_SEND_REPORT_CHUNK_SIZE - left] = '\0';
if (send (chunk, send_data_1, send_data_2))
{
fclose (stream);
g_warning ("%s: send error", __func__);
gvm_file_remove_recurse (xml_dir);
return -1;
}
}
}
/* Check if there's more. */
if (feof (stream))
break;
}
fclose (stream);
/* Remove the directory. */
gvm_file_remove_recurse (xml_dir);
/* Return the output. */
return 0;
}
/**
* @brief Get the IP of a host, using the 'hostname' report host details.
*
* The most recent host detail takes preference.
*
* @param[in] host Host name or IP.
*
* @return Newly allocated UUID if available, else NULL.
*/
gchar*
report_host_ip (const char *host)
{
gchar *quoted_host, *ret;
quoted_host = sql_quote (host);
ret = sql_string ("SELECT host FROM report_hosts"
" WHERE id = (SELECT report_host FROM report_host_details"
" WHERE name = 'hostname'"
" AND value = '%s'"
" ORDER BY id DESC LIMIT 1);",
quoted_host);
g_free (quoted_host);
return ret;
}
/**
* @brief Check if a report host is alive and has at least one result.
*
* @param[in] report Report.
* @param[in] host Host name or IP.
*
* @return 0 if dead, else alive.
*/
int
report_host_noticeable (report_t report, const gchar *host)
{
report_host_t report_host = 0;
sql_int64 (&report_host,
"SELECT id FROM report_hosts"
" WHERE report = %llu AND host = '%s';",
report,
host);
return report_host
&& report_host_dead (report_host) == 0
&& report_host_result_count (report_host) > 0;
}
/**
* @brief Parse an OSP report.
*
* @param[in] task Task.
* @param[in] report Report.
* @param[in] report_xml Report XML.
*/
void
parse_osp_report (task_t task, report_t report, const char *report_xml)
{
entity_t entity, child;
entities_t results;
const char *str;
char *defs_file = NULL;
time_t start_time, end_time;
gboolean has_results = FALSE;
GArray *results_array;
assert (task);
assert (report);
assert (report_xml);
if (parse_entity (report_xml, &entity))
{
g_warning ("Couldn't parse %s OSP scan report", report_xml);
return;
}
sql_begin_immediate ();
/* Set the report's start and end times. */
results_array = g_array_new (TRUE, TRUE, sizeof (result_t));
start_time = 0;
str = entity_attribute (entity, "start_time");
if (str)
{
start_time = atoi (str);
set_scan_start_time_epoch (report, start_time);
}
end_time = 0;
str = entity_attribute (entity, "end_time");
if (str)
{
end_time = atoi (str);
set_scan_end_time_epoch (report, end_time);
}
/* Insert results. */
child = entity_child (entity, "results");
if (!child)
{
g_warning ("Missing results element in OSP report %s", report_xml);
goto end_parse_osp_report;
}
results = child->entities;
if (results)
has_results = TRUE;
defs_file = task_definitions_file (task);
while (results)
{
result_t result;
const char *type, *name, *severity, *host, *hostname, *test_id, *port;
const char *qod, *path;
char *desc = NULL, *nvt_id = NULL, *severity_str = NULL;
entity_t r_entity = results->data;
int qod_int;
if (strcmp (entity_name (r_entity), "result"))
{
g_warning ("Erroneous entry in OSP results %s",
entity_name (r_entity));
results = next_entities (results);
continue;
}
type = entity_attribute (r_entity, "type");
name = entity_attribute (r_entity, "name");
severity = entity_attribute (r_entity, "severity");
test_id = entity_attribute (r_entity, "test_id");
host = entity_attribute (r_entity, "host");
hostname = entity_attribute (r_entity, "hostname");
port = entity_attribute (r_entity, "port") ?: "";
qod = entity_attribute (r_entity, "qod") ?: "";
path = entity_attribute (r_entity, "uri") ?: "";
if (!name || !type || !severity || !test_id || !host)
{
GString *string = g_string_new ("");
print_entity_to_string (r_entity, string);
g_warning ("Erroneous attribute in OSP result %s", string->str);
g_string_free (string, TRUE);
results = next_entities (results);
continue;
}
/* Add report host if it doesn't exist. */
manage_report_host_add (report, host, start_time, end_time);
if (!strcmp (type, "Host Detail"))
{
insert_report_host_detail (report, host, "osp", "", "OSP Host Detail",
name, entity_text (r_entity));
results = next_entities (results);
continue;
}
else if (g_str_has_prefix (test_id, "1.3.6.1.4.1.25623.1."))
{
nvt_id = g_strdup (test_id);
severity_str = nvt_severity (test_id, type);
desc = g_strdup (entity_text (r_entity));
}
else if (g_str_has_prefix (test_id, "oval:"))
{
nvt_id = ovaldef_uuid (test_id, defs_file);
severity_str = ovaldef_severity (nvt_id);
}
else
{
nvt_id = g_strdup (name);
desc = g_strdup (entity_text (r_entity));
}
qod_int = atoi (qod);
if (qod_int <= 0 || qod_int > 100)
qod_int = QOD_DEFAULT;
if (port && strcmp (port, "general/Host_Details") == 0)
{
/* TODO: This should probably be handled by the "Host Detail"
* result type with extra source info in OSP.
*/
if (manage_report_host_detail (report, host, desc))
g_warning ("%s: Failed to add report detail for host '%s': %s",
__func__,
host,
desc);
}
else if (host && nvt_id && desc && (strcmp (nvt_id, "HOST_START") == 0))
{
set_scan_host_start_time_ctime (report, host, desc);
}
else if (host && nvt_id && desc && (strcmp (nvt_id, "HOST_END") == 0))
{
set_scan_host_end_time_ctime (report, host, desc);
add_assets_from_host_in_report (report, host);
}
else
{
result = make_osp_result (task,
host,
hostname,
nvt_id,
type,
desc,
port ?: "",
severity_str ?: severity,
qod_int,
path);
g_array_append_val (results_array, result);
}
g_free (nvt_id);
g_free (desc);
g_free (severity_str);
results = next_entities (results);
}
if (has_results)
{
report_add_results_array (report, results_array);
}
end_parse_osp_report:
sql_commit ();
g_array_free (results_array, TRUE);
g_free (defs_file);
free_entity (entity);
}
/* More task stuff. */
/**
* @brief Return the trend of a task, given counts.
*
* @param[in] holes_a Number of holes on earlier report.
* @param[in] warns_a Number of warnings on earlier report.
* @param[in] infos_a Number of infos on earlier report.
* @param[in] severity_a Severity of earlier report.
* @param[in] holes_b Number of holes on later report.
* @param[in] warns_b Number of warnings on later report.
* @param[in] infos_b Number of infos on later report.
* @param[in] severity_b Severity of later report.
*
* @return "up", "down", "more", "less", "same" or if too few reports "".
*/
static const char *
task_trend_calc (int holes_a, int warns_a, int infos_a, double severity_a,
int holes_b, int warns_b, int infos_b, double severity_b)
{
int threat_a, threat_b;
/* Check if the severity score changed. */
if (severity_a > severity_b)
return "up";
if (severity_a < severity_b)
return "down";
/* Calculate trend. */
if (holes_a > 0)
threat_a = 4;
else if (warns_a > 0)
threat_a = 3;
else if (infos_a > 0)
threat_a = 2;
else
threat_a = 1;
if (holes_b > 0)
threat_b = 4;
else if (warns_b > 0)
threat_b = 3;
else if (infos_b > 0)
threat_b = 2;
else
threat_b = 1;
/* Check if the threat level changed. */
if (threat_a > threat_b)
return "up";
if (threat_a < threat_b)
return "down";
/* Check if the threat count changed in the highest level. */
if (holes_a)
{
if (holes_a > holes_b)
return "more";
if (holes_a < holes_b)
return "less";
return "same";
}
if (warns_a)
{
if (warns_a > warns_b)
return "more";
if (warns_a < warns_b)
return "less";
return "same";
}
if (infos_a)
{
if (infos_a > infos_b)
return "more";
if (infos_a < infos_b)
return "less";
return "same";
}
return "same";
}
/**
* @brief Return the trend of a task, given counts.
*
* @param[in] iterator Task iterator.
* @param[in] holes_a Number of holes on earlier report.
* @param[in] warns_a Number of warnings on earlier report.
* @param[in] infos_a Number of infos on earlier report.
* @param[in] severity_a Severity score of earlier report.
* @param[in] holes_b Number of holes on later report.
* @param[in] warns_b Number of warnings on later report.
* @param[in] infos_b Number of infos on later report.
* @param[in] severity_b Severity score of later report.
*
* @return "up", "down", "more", "less", "same" or if too few reports "".
*/
const char *
task_iterator_trend_counts (iterator_t *iterator, int holes_a, int warns_a,
int infos_a, double severity_a, int holes_b,
int warns_b, int infos_b, double severity_b)
{
/* Ensure there are enough reports. */
if (task_iterator_finished_reports (iterator) <= 1)
return "";
/* Skip running tasks. */
if (task_iterator_run_status (iterator) == TASK_STATUS_RUNNING)
return "";
return task_trend_calc (holes_a, warns_a, infos_a, severity_a,
holes_b, warns_b, infos_b, severity_b);
}
/**
* @brief Make a task.
*
* The char* parameters name and comment are used directly and freed
* when the task is freed.
*
* @param[in] name The name of the task.
* @param[in] comment A comment associated the task.
* @param[in] in_assets Whether task must be considered for assets.
* @param[in] event Whether to be generate event and event log.
*
* @return A pointer to the new task.
*/
task_t
make_task (char* name, char* comment, int in_assets, int event)
{
task_t task;
char* uuid = gvm_uuid_make ();
gchar *quoted_name, *quoted_comment;
if (uuid == NULL) abort ();
quoted_name = name ? sql_quote ((gchar*) name) : NULL;
quoted_comment = comment ? sql_quote ((gchar*) comment) : NULL;
sql ("INSERT into tasks"
" (owner, uuid, name, hidden, comment, schedule,"
" schedule_next_time, config_location, target, target_location,"
" scanner_location, schedule_location, alterable,"
" creation_time, modification_time, usage_type)"
" VALUES ((SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', '%s', 0, '%s', 0, 0, 0, 0, 0, 0, 0, 0, m_now (),"
" m_now (), 'scan');",
current_credentials.uuid,
uuid,
quoted_name ? quoted_name : "",
quoted_comment ? quoted_comment : "");
task = sql_last_insert_id ();
if (event)
set_task_run_status (task, TASK_STATUS_NEW);
else
set_task_run_status_internal (task, TASK_STATUS_NEW);
sql ("INSERT INTO task_preferences (task, name, value)"
" VALUES (%llu, 'in_assets', '%s')",
task,
in_assets ? "yes" : "no");
sql ("INSERT INTO task_preferences (task, name, value)"
" VALUES (%llu, 'assets_apply_overrides', 'yes')",
task);
sql ("INSERT INTO task_preferences (task, name, value)"
" VALUES (%llu, 'assets_min_qod', %d)",
task,
MIN_QOD_DEFAULT);
free (uuid);
free (name);
free (comment);
g_free (quoted_name);
g_free (quoted_comment);
return task;
}
/**
* @brief Complete the creation of a task.
*
* @param[in] task The task.
*/
void
make_task_complete (task_t task)
{
assert (task);
cache_permissions_for_resource ("task", task, NULL);
event (EVENT_TASK_RUN_STATUS_CHANGED, (void*) TASK_STATUS_NEW, task, 0);
}
/**
* @brief Set the name of a task.
*
* @param[in] task A task.
* @param[in] name New name.
*/
void
set_task_name (task_t task, const char *name)
{
gchar *quoted_name;
quoted_name = sql_quote (name ? name : "");
sql ("UPDATE tasks SET name = '%s', modification_time = m_now ()"
" WHERE id = %llu;", quoted_name, task);
g_free (quoted_name);
}
/**
* @brief Set the comment of a task.
*
* @param[in] task A task.
* @param[in] comment New comment.
*/
static void
set_task_comment (task_t task, const char *comment)
{
gchar *quoted_comment;
assert (comment);
quoted_comment = sql_quote (comment ? comment : "");
sql ("UPDATE tasks SET comment = '%s', modification_time = m_now ()"
" WHERE id = %llu;", quoted_comment, task);
g_free (quoted_comment);
}
/**
* @brief Create a task from an existing task.
*
* @param[in] name Name of new task. NULL to copy from existing.
* @param[in] comment Comment on new task. NULL to copy from existing.
* @param[in] task_id UUID of existing task.
* @param[in] alterable Whether the new task will be alterable. < 0 to
* to copy from existing.
* @param[out] new_task New task.
*
* @return 0 success, 2 failed to find existing task, 99 permission denied,
* -1 error.
*/
int
copy_task (const char* name, const char* comment, const char *task_id,
int alterable, task_t* new_task)
{
task_t new, old;
int ret;
assert (current_credentials.uuid);
if (task_id == NULL)
return -1;
sql_begin_immediate ();
ret = copy_resource_lock ("task", name, comment, task_id,
"config, target, schedule, schedule_periods,"
" scanner, schedule_next_time,"
" config_location, target_location,"
" schedule_location, scanner_location,"
" hosts_ordering, usage_type, alterable",
1, &new, &old);
if (ret)
{
sql_rollback ();
return ret;
}
if (alterable >= 0)
sql ("UPDATE tasks SET alterable = %i, hidden = 0 WHERE id = %llu;",
alterable,
new);
else
sql ("UPDATE tasks SET hidden = 0 WHERE id = %llu;",
new);
set_task_run_status (new, TASK_STATUS_NEW);
sql ("INSERT INTO task_preferences (task, name, value)"
" SELECT %llu, name, value FROM task_preferences"
" WHERE task = %llu;",
new,
old);
sql ("INSERT INTO task_alerts (task, alert, alert_location)"
" SELECT %llu, alert, alert_location FROM task_alerts"
" WHERE task = %llu;",
new,
old);
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" SELECT make_uuid (), (SELECT owner FROM tasks WHERE id = %llu),"
" name, comment, resource_type, %llu,"
" (SELECT uuid FROM tasks WHERE id = %llu),"
" resource_location, subject_type, subject, subject_location,"
" m_now (), m_now ()"
" FROM permissions"
" WHERE owner = (SELECT owner FROM tasks WHERE id = %llu)"
" AND resource_type = 'task'"
" AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
" AND resource = %llu;",
new,
new,
new,
old,
old);
if (ret)
{
sql_rollback ();
return ret;
}
cache_permissions_for_resource ("task", new, NULL);
sql_commit ();
if (new_task) *new_task = new;
return 0;
}
/**
* @brief Complete deletion of a task.
*
* This sets up a transaction around the delete.
*
* @param[in] task The task.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 on success, 1 if task is hidden, -1 on error.
*/
static int
delete_task_lock (task_t task, int ultimate)
{
int ret, lock_ret, lock_retries;
g_debug (" delete task %llu", task);
sql_begin_immediate ();
/* This prevents other processes (for example a START_TASK) from getting
* a reference to a report ID or the task ID, and then using that
* reference to try access the deleted report or task.
*
* If the task is already active then delete_report (via delete_task)
* will fail and rollback. */
lock_retries = LOCK_RETRIES;
lock_ret = sql_int ("SELECT try_exclusive_lock('reports');");
while ((lock_ret == 0) && (lock_retries > 0))
{
sleep(LOCK_RETRY_DELAY);
lock_ret = sql_int ("SELECT try_exclusive_lock('reports');");
lock_retries--;
}
if (lock_ret == 0)
{
sql_rollback ();
return -1;
}
if (sql_int ("SELECT hidden FROM tasks WHERE id = %llu;", task))
{
sql_rollback ();
return -1;
}
ret = delete_task (task, ultimate);
if (ret)
sql_rollback ();
else
sql_commit ();
return ret;
}
/**
* @brief Request deletion of a task.
*
* Stop the task beforehand with \ref stop_task_internal, if it is running.
*
* Used only for CREATE_TASK in gmp.c. Always ultimate.
*
* @param[in] task_pointer A pointer to the task.
*
* @return 0 if deleted, 1 if delete requested, 2 if task is hidden,
* -1 if error, -5 if scanner is down.
*/
int
request_delete_task (task_t* task_pointer)
{
task_t task = *task_pointer;
int hidden;
g_debug (" request delete task %llu", task);
hidden = sql_int ("SELECT hidden from tasks WHERE id = %llu;",
*task_pointer);
/* Technically the task could be in the trashcan, if someone gets the UUID
* with GET_TASKS before the CREATE_TASK finishes, and removes the task.
* Pretend it was deleted. There'll be half a task in the trashcan. */
if (hidden == 2)
return 0;
if (current_credentials.uuid == NULL) return -1;
switch (stop_task_internal (task))
{
case 0: /* Stopped. */
return delete_task_lock (task, 1);
case 1: /* Stop requested. */
set_task_run_status (task, TASK_STATUS_DELETE_ULTIMATE_REQUESTED);
return 1;
default: /* Programming error. */
assert (0);
case -1: /* Error. */
return -1;
break;
case -5: /* Scanner down. */
return -5;
break;
}
return 0;
}
/**
* @brief Request deletion of a task.
*
* Stop the task beforehand with \ref stop_task_internal, if it is running.
*
* This is only used for DELETE_TASK in gmp.c.
*
* @param[in] task_id UUID of task.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 deleted, 1 delete requested, 2 task is hidden, 3 failed to find
* task, 99 permission denied, -1 error, -5 scanner is down, -7 no CA
* cert.
*/
int
request_delete_task_uuid (const char *task_id, int ultimate)
{
task_t task = 0;
int lock_ret, lock_retries;
/* Tasks have special handling for the trashcan. Other resources have trash
* tables, like targets_trash. Tasks are marked as trash in the tasks table
* by giving the "hidden" field a value of 2. This means that the results can
* stay in the results table and will still refer to the correct task. This
* should all work because there is already handling of the hidden flag
* everywhere else. */
g_debug (" request delete task %s", task_id);
sql_begin_immediate ();
if (acl_user_may ("delete_task") == 0)
{
sql_rollback ();
return 99;
}
if (find_task_with_permission (task_id, &task, "delete_task"))
{
sql_rollback ();
return -1;
}
if (task == 0)
{
if (find_trash_task (task_id, &task))
{
sql_rollback ();
return -1;
}
if (task == 0)
{
sql_rollback ();
return 3;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
if (delete_reports (task))
{
sql_rollback ();
return -1;
}
permissions_set_orphans ("task", task, LOCATION_TRASH);
tags_remove_resource ("task", task, LOCATION_TRASH);
tickets_remove_task (task);
sql ("DELETE FROM results WHERE task = %llu;", task);
sql ("DELETE FROM task_alerts WHERE task = %llu;", task);
sql ("DELETE FROM task_files WHERE task = %llu;", task);
sql ("DELETE FROM task_preferences WHERE task = %llu;", task);
sql ("DELETE FROM tasks WHERE id = %llu;", task);
sql_commit ();
return 0;
}
if (current_credentials.uuid == NULL)
{
sql_rollback ();
return -1;
}
switch (stop_task_internal (task))
{
case 0: /* Stopped. */
{
int ret;
if (ultimate)
{
/* This prevents other processes (for example a START_TASK) from
* getting a reference to a report ID or the task ID, and then
* using that reference to try access the deleted report or task.
*
* If the task is running already then delete_task will lead to
* ROLLBACK. */
lock_retries = LOCK_RETRIES;
lock_ret = sql_int ("SELECT try_exclusive_lock('reports');");
while ((lock_ret == 0) && (lock_retries > 0))
{
sleep(LOCK_RETRY_DELAY);
lock_ret = sql_int ("SELECT try_exclusive_lock('reports');");
lock_retries--;
}
if (lock_ret == 0)
{
sql_rollback ();
return -1;
}
}
ret = delete_task (task, ultimate);
if (ret)
sql_rollback ();
else
sql_commit ();
return ret;
}
case 1: /* Stop requested. */
if (ultimate)
set_task_run_status (task,
TASK_STATUS_DELETE_ULTIMATE_REQUESTED);
else
set_task_run_status (task,
TASK_STATUS_DELETE_REQUESTED);
sql_commit ();
return 1;
default: /* Programming error. */
assert (0);
case -1: /* Error. */
sql_rollback ();
return -1;
break;
case -5: /* Scanner down. */
sql_rollback ();
return -5;
break;
case -7: /* No CA cert. */
sql_rollback ();
return -5;
break;
}
sql_commit ();
return 0;
}
/**
* @brief Complete deletion of a task.
*
* The caller must do the locking, and must do the hidden check.
*
* The caller must handle the case where the task is already in the trashcan.
*
* @param[in] task The task.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 on success, -1 on error.
*/
int
delete_task (task_t task, int ultimate)
{
g_debug (" delete task %llu", task);
assert (current_credentials.uuid);
if (ultimate)
{
if (delete_reports (task))
return -1;
permissions_set_orphans ("task", task,
task_in_trash (task)
? LOCATION_TRASH
: LOCATION_TABLE);
tags_remove_resource ("task", task,
task_in_trash (task)
? LOCATION_TRASH
: LOCATION_TABLE);
tickets_remove_task (task);
sql ("DELETE FROM results_trash WHERE task = %llu;", task);
sql ("DELETE FROM results WHERE task = %llu;", task);
sql ("DELETE FROM task_alerts WHERE task = %llu;", task);
sql ("DELETE FROM task_files WHERE task = %llu;", task);
sql ("DELETE FROM task_preferences WHERE task = %llu;", task);
sql ("DELETE FROM tasks WHERE id = %llu;", task);
}
else
{
permissions_set_locations ("task", task, task, LOCATION_TRASH);
tags_set_locations ("task", task, task, LOCATION_TRASH);
sql ("UPDATE tag_resources"
" SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE resource_type = 'report'"
" AND resource IN (SELECT id FROM reports"
" WHERE reports.task = %llu);",
task);
sql ("UPDATE tag_resources"
" SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE resource_type = 'result'"
" AND resource IN (SELECT id FROM results"
" WHERE results.task = %llu);",
task);
sql ("UPDATE tag_resources_trash"
" SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE resource_type = 'report'"
" AND resource IN (SELECT id FROM reports"
" WHERE reports.task = %llu);",
task);
sql ("UPDATE tag_resources_trash"
" SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE resource_type = 'result'"
" AND resource IN (SELECT id FROM results"
" WHERE results.task = %llu);",
task);
sql ("INSERT INTO results_trash"
" (uuid, task, host, port, nvt, result_nvt, type, description,"
" report, nvt_version, severity, qod, qod_type, owner, date,"
" hostname, path)"
" SELECT uuid, task, host, port, nvt, result_nvt, type,"
" description, report, nvt_version, severity, qod,"
" qod_type, owner, date, hostname, path"
" FROM results"
" WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
task);
tickets_trash_task (task);
sql ("DELETE FROM results"
" WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
task);
sql ("DELETE FROM report_counts"
" WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
task);
sql ("UPDATE tasks SET hidden = 2 WHERE id = %llu;", task);
}
delete_permissions_cache_for_resource ("task", task);
return 0;
}
/**
* @brief Delete all trash tasks.
*
* The caller must do the transaction.
*
* @return 0 on success, -1 on error.
*/
static int
delete_trash_tasks ()
{
iterator_t tasks;
init_user_task_iterator (&tasks, 1, 1);
while (next (&tasks))
{
task_t task;
task = get_iterator_resource (&tasks);
if (delete_reports (task))
{
cleanup_iterator (&tasks);
return -1;
}
tickets_remove_task (task);
sql ("DELETE FROM results WHERE task = %llu;", task);
sql ("DELETE FROM task_alerts WHERE task = %llu;", task);
sql ("DELETE FROM task_files WHERE task = %llu;", task);
sql ("DELETE FROM task_preferences WHERE task = %llu;", task);
sql ("DELETE FROM tasks WHERE id = %llu;", task);
}
cleanup_iterator (&tasks);
return 0;
}
/**
* @brief Fixes the DST offset in schedule_next_time of tasks.
*
* @return changes The number of tasks updated.
*/
static int
cleanup_schedule_times ()
{
sql ("UPDATE tasks"
" SET schedule_next_time"
" = (SELECT next_time_ical (icalendar, timezone)"
" FROM schedules"
" WHERE schedules.id = tasks.schedule)"
" WHERE schedule_next_time != 0"
" AND schedule_next_time"
" = (SELECT next_time_ical (icalendar, timezone)"
" FROM schedules"
" WHERE schedules.id = tasks.schedule)"
" AND schedule_next_time"
" != (SELECT next_time_ical (icalendar, timezone)"
" FROM schedules"
" WHERE schedules.id = tasks.schedule);");
return sql_changes();
}
/**
* @brief Append text to the comment associated with a task.
*
* @param[in] task A pointer to the task.
* @param[in] text The text to append.
* @param[in] length Length of the text.
*/
void
append_to_task_comment (task_t task, const char* text, /* unused */ int length)
{
append_to_task_string (task, "comment", text);
}
/**
* @brief Set the ports for a particular host in a scan.
*
* @param[in] report Report associated with scan.
* @param[in] host Host.
* @param[in] current New value for port currently being scanned.
* @param[in] max New value for last port to be scanned.
*/
void
set_scan_ports (report_t report, const char* host, unsigned int current,
unsigned int max)
{
sql ("UPDATE report_hosts SET current_port = %i, max_port = %i"
" WHERE host = '%s' AND report = %llu;",
current, max, host, report);
}
/**
* @brief Find a task for a specific permission, given a UUID.
*
* @param[in] uuid UUID of task.
* @param[out] task Task return, 0 if successfully failed to find task.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find task), TRUE on error.
*/
gboolean
find_task_with_permission (const char* uuid, task_t* task,
const char *permission)
{
return find_resource_with_permission ("task", uuid, task, permission, 0);
}
/**
* @brief Find a task in the trashcan for a specific permission, given a UUID.
*
* @param[in] uuid UUID of task.
* @param[out] task Task return, 0 if successfully failed to find task.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find task), TRUE on error.
*/
gboolean
find_trash_task_with_permission (const char* uuid, task_t* task,
const char *permission)
{
return find_resource_with_permission ("task", uuid, task, permission, 1);
}
/**
* @brief Find a task in the trashcan, given an identifier.
*
* @param[in] uuid A task identifier.
* @param[out] task Task return, 0 if successfully failed to find task.
*
* @return FALSE on success (including if failed to find task), TRUE on error.
*/
static gboolean
find_trash_task (const char* uuid, task_t* task)
{
if (acl_user_owns_uuid ("task", uuid, 1) == 0)
{
*task = 0;
return FALSE;
}
switch (sql_int64 (task,
"SELECT id FROM tasks WHERE uuid = '%s'"
" AND hidden = 2;",
uuid))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*task = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
return TRUE;
break;
}
return FALSE;
}
/**
* @brief Find a report for a specific permission, given a UUID.
*
* @param[in] uuid UUID of report.
* @param[out] report Report return, 0 if successfully failed to find
* report.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find report), TRUE on error.
*/
gboolean
find_report_with_permission (const char* uuid, report_t* report,
const char *permission)
{
return find_resource_with_permission ("report", uuid, report, permission, 0);
}
/**
* @brief Find a report in the trashcan for a specific permission, given a UUID.
*
* @param[in] uuid UUID of report.
* @param[out] report Report return, 0 if successfully failed to find
* report.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find report), TRUE on error.
*/
static gboolean
find_trash_report_with_permission (const char *uuid, report_t *report,
const char *permission)
{
return find_resource_with_permission ("report", uuid, report, permission, 1);
}
/**
* @brief Reset all running information for a task.
*
* @param[in] task Task.
*/
void
reset_task (task_t task)
{
sql ("UPDATE tasks SET"
" start_time = 0,"
" end_time = 0"
" WHERE id = %llu;",
task);
}
/**
* @brief Add a file to a task, or update the file on the task.
*
* @param[in] task_id Task.
* @param[in] name Name of file.
* @param[in] content Content for file in base64 encoding.
*
* @return 0 success, 1 failed to find task, -1 error.
*/
int
manage_task_update_file (const gchar *task_id, const char *name,
const void *content)
{
gchar* quoted_name = sql_quote (name);
gchar* quoted_content = sql_quote (content);
task_t task;
/** @todo Probably better to save ASCII instead of base64. */
task = 0;
if (find_task_with_permission (task_id, &task, "modify_task"))
return -1;
else if (task == 0)
return 1;
if (sql_int ("SELECT count(*) FROM task_files"
" WHERE task = %llu AND name = '%s';",
task,
quoted_name))
{
/* Update the existing file. */
sql ("UPDATE task_files SET content = '%s'"
" WHERE task = %llu AND name = '%s';",
quoted_content,
task,
quoted_name);
}
else
{
/* Insert the file. */
sql ("INSERT INTO task_files (task, name, content)"
" VALUES (%llu, '%s', '%s');",
task,
quoted_name,
quoted_content);
}
sql ("UPDATE tasks SET modification_time = m_now () WHERE id = %llu;",
task);
g_free (quoted_name);
g_free (quoted_content);
return 0;
}
/**
* @brief Remove a file on a task.
*
* @param[in] task_id Task.
* @param[in] name Name of file.
*
* @return 0 success, 1 failed to find task, -1 error.
*/
int
manage_task_remove_file (const gchar *task_id, const char *name)
{
task_t task;
task = 0;
if (find_task_with_permission (task_id, &task, "modify_task"))
return -1;
else if (task == 0)
return 1;
if (sql_int ("SELECT count(*) FROM task_files"
" WHERE task = %llu AND name = '%s';",
task))
{
gchar* quoted_name = sql_quote (name);
sql ("DELETE FROM task_files WHERE task = %llu AND name = '%s';",
task,
quoted_name);
sql ("UPDATE tasks SET modification_time = m_now () WHERE id = %llu;",
task);
g_free (quoted_name);
return 0;
}
return -1;
}
/**
* @brief Initialise a task file iterator.
*
* @param[in] iterator Iterator.
* @param[in] task Task.
* @param[in] file File name, NULL for all files.
*/
void
init_task_file_iterator (iterator_t* iterator, task_t task, const char* file)
{
gchar* sql;
if (file)
{
gchar *quoted_file = sql_nquote (file, strlen (file));
sql = g_strdup_printf ("SELECT name, content, length(content)"
" FROM task_files"
" WHERE task = %llu"
" AND name = '%s';",
task, quoted_file);
g_free (quoted_file);
}
else
sql = g_strdup_printf ("SELECT name, content, length(content)"
" FROM task_files"
" WHERE task = %llu;",
task);
init_iterator (iterator, "%s", sql);
g_free (sql);
}
/**
* @brief Get the name of the file from a task file iterator.
*
* @param[in] iterator Iterator.
*
* @return Name of the file or NULL if iteration is complete.
*/
DEF_ACCESS (task_file_iterator_name, 0);
/**
* @brief Get the content of the file from a task file iterator.
*
* @param[in] iterator Iterator.
*
* @return Content of the file or NULL if iteration is complete.
*/
DEF_ACCESS (task_file_iterator_content, 1);
/**
* @brief Modify a task.
*
* @param[in] task_id Task.
* @param[in] name Name of file.
* @param[in] comment Comment.
* @param[in] scanner_id Scanner.
* @param[in] target_id Target.
* @param[in] config_id Config.
* @param[in] observers Observers.
* @param[in] alerts Alerts.
* @param[in] alterable Alterable.
* @param[in] groups Groups.
* @param[in] schedule_id Schedule.
* @param[in] schedule_periods Period of schedule.
* @param[in] preferences Preferences.
* @param[in] hosts_ordering Host scan order.
* @param[out] fail_alert_id Alert when failed to find alert.
* @param[out] fail_group_id Group when failed to find group.
*
* @return 0 success, 1 failed to find task, 2 status must be new to edit
* scanner, 3 failed to find scanner, 4 failed to find config, 5 status
* must be new to edit config, 6 user name validation failed, 7 failed
* to find user, 8 failed to find alert, 9 task must be new to modify
* alterable state, 10 failed to find group, 11 failed to find schedule,
* 12 failed to find target, 13 invalid auto_delete value, 14 auto
* delete count out of range, 15 config and scanner types mismatch,
* 16 status must be new to edit target, 17 for container tasks only
* certain fields may be edited, -1 error.
*/
int
modify_task (const gchar *task_id, const gchar *name,
const gchar *comment, const gchar *scanner_id,
const gchar *target_id, const gchar *config_id,
const gchar *observers, array_t *alerts,
const gchar *alterable, array_t *groups,
const gchar *schedule_id,
const gchar *schedule_periods,
array_t *preferences,
const gchar *hosts_ordering,
gchar **fail_alert_id,
gchar **fail_group_id)
{
task_t task;
int type_of_scanner;
scanner_t scanner;
/* @todo Probably better to rollback on error. */
task = 0;
if (find_task_with_permission (task_id, &task, "modify_task"))
return -1;
if (task == 0)
return 1;
if ((task_target (task) == 0)
&& (alerts->len || schedule_id))
return 17;
switch (modify_task_check_config_scanner (task, config_id, scanner_id))
{
case 0:
break;
case 1:
return 15;
case 2:
return 4;
case 3:
return 3;
default:
assert (0);
/* fallthrough */
case -1:
return -1;
}
if (name)
set_task_name (task, name);
if (comment)
set_task_comment (task, comment);
scanner = 0;
if (scanner_id)
{
if (strcmp (scanner_id, "0") == 0)
{
/* Leave it as is. */
}
else if ((task_run_status (task) != TASK_STATUS_NEW)
&& (task_alterable (task) == 0))
return 2;
else if (find_scanner_with_permission (scanner_id,
&scanner,
"get_scanners"))
return -1;
else if (scanner == 0)
return 3;
else
set_task_scanner (task, scanner);
}
if (scanner == 0)
type_of_scanner = scanner_type (task_scanner (task));
else
type_of_scanner = scanner_type (scanner);
if (config_id && (type_of_scanner != SCANNER_TYPE_CVE))
{
config_t config;
config = 0;
if (strcmp (config_id, "0") == 0)
{
/* Leave it as it is. */
}
else if ((task_run_status (task) != TASK_STATUS_NEW)
&& (task_alterable (task) == 0))
return 5;
else if (find_config_with_permission (config_id, &config, "get_configs"))
return -1;
else if (config == 0)
return 4;
else
set_task_config (task, config);
}
if (observers)
{
switch (set_task_observers (task, observers))
{
case 0:
break;
case 1:
return 6;
case 2:
return 7;
break;
case -1:
default:
return -1;
}
}
if (alerts->len)
{
switch (set_task_alerts (task, alerts, fail_alert_id))
{
case 0:
break;
case 1:
return 8;
case -1:
default:
return -1;
}
}
if (alterable && (task_alterable (task) != atoi (alterable)))
{
if (task_run_status (task) != TASK_STATUS_NEW)
return 9;
set_task_alterable (task, strcmp (alterable, "0"));
}
if (groups->len)
{
switch (set_task_groups (task, groups, fail_group_id))
{
case 0:
break;
case 1:
return 10;
case -1:
default:
return -1;
}
}
if (schedule_id)
{
schedule_t schedule = 0;
int periods;
periods = schedule_periods ? atoi (schedule_periods) : 0;
if (strcmp (schedule_id, "0") == 0)
set_task_schedule (task, 0, periods);
else if (find_schedule_with_permission (schedule_id,
&schedule,
"get_schedules"))
return -1;
else if (schedule == 0)
return 11;
else if (set_task_schedule (task, schedule, periods))
return -1;
}
else if (schedule_periods && strlen (schedule_periods))
set_task_schedule_periods (task_id,
atoi (schedule_periods));
if (target_id)
{
target_t target;
target = 0;
if (strcmp (target_id, "0") == 0)
{
/* Leave it as it is. */
}
else if ((task_run_status (task) != TASK_STATUS_NEW)
&& (task_alterable (task) == 0))
return 16;
else if (find_target_with_permission (target_id,
&target,
"get_targets"))
return -1;
else if (target == 0)
return 12;
else
set_task_target (task, target);
}
if (preferences)
switch (set_task_preferences (task, preferences))
{
case 0:
break;
case 1:
return 13;
case 2:
return 14;
default:
return -1;
}
if (hosts_ordering)
set_task_hosts_ordering (task, hosts_ordering);
return 0;
}
/* Targets. */
/**
* @brief Get the maximum allowed number of hosts per target.
*
* @return Maximum.
*/
int
manage_max_hosts ()
{
return max_hosts;
}
/**
* @brief Set the maximum allowed number of hosts per target.
*
* @param[in] new_max New max_hosts value.
*/
static void
manage_set_max_hosts (int new_max)
{
max_hosts = new_max;
}
/**
* @brief Find a target for a specific permission, given a UUID.
*
* @param[in] uuid UUID of target.
* @param[out] target Target return, 0 if successfully failed to find target.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find target), TRUE on error.
*/
gboolean
find_target_with_permission (const char* uuid, target_t* target,
const char *permission)
{
return find_resource_with_permission ("target", uuid, target, permission, 0);
}
/**
* @brief Return number of hosts described by a hosts string.
*
* @param[in] given_hosts String describing hosts.
* @param[in] exclude_hosts String describing hosts excluded from given set.
*
* @return Number of hosts, or -1 on error.
*/
int
manage_count_hosts (const char *given_hosts, const char *exclude_hosts)
{
return manage_count_hosts_max (given_hosts,
exclude_hosts,
manage_max_hosts ());
}
/**
* @brief Trim leading and trailing space from a hosts string.
*
* @param[in] string String. May be modified.
*
* @return Either string or some address within string.
*/
static gchar *
trim_hosts (gchar *string)
{
gchar *host, *end;
/* Trim leading and trailing space. */
host = string;
while ((*host == ' ') || (*host == '\t'))
host++;
end = host;
while (*end)
{
if ((*end == ' ') || (*end == '\t'))
{
*end = '\0';
break;
}
end++;
}
return host;
}
/**
* @brief Clean a hosts string.
*
* @param[in] given_hosts String describing hosts.
* @param[out] max Max number of hosts, adjusted for duplicates.
*
* @return Freshly allocated new hosts string, or NULL on error.
*/
gchar*
clean_hosts (const char *given_hosts, int *max)
{
array_t *clean_array;
GString *clean;
gchar **split, **point, *hosts, *hosts_start, *host;
guint index;
/* Treat newlines like commas. */
hosts = hosts_start = g_strdup (given_hosts);
while (*hosts)
{
if (*hosts == '\n') *hosts = ',';
hosts++;
}
split = g_strsplit (hosts_start, ",", 0);
g_free (hosts_start);
point = split;
if ((point == NULL) || (*point == NULL))
{
g_strfreev (split);
return g_strdup ("");
}
clean_array = make_array ();
while (*point)
{
host = trim_hosts (*point);
if (*host)
{
/* Prevent simple duplicates. */
if (array_find_string (clean_array, host) == NULL)
array_add (clean_array, host);
else if (max)
(*max)--;
}
point += 1;
}
clean = g_string_new ("");
host = (gchar*) g_ptr_array_index (clean_array, 0);
if (host)
g_string_append_printf (clean, "%s", host);
for (index = 1; index < clean_array->len; index++)
{
host = (gchar*) g_ptr_array_index (clean_array, index);
if (host)
g_string_append_printf (clean, ", %s", host);
}
return g_string_free (clean, FALSE);
}
/**
* @brief Start a new IMMEDIATE transaction.
*/
void
manage_transaction_start ()
{
if (!in_transaction)
{
sql_begin_immediate ();
in_transaction = TRUE;
}
gettimeofday (&last_msg, NULL);
}
/**
* @brief Commit the current transaction, if any.
*
* The algorithm is extremely naive (time elapsed since the last message
* was received) but delivers good enough performances when facing
* bursts of messages.
*
* @param[in] force_commit Force committing the pending transaction.
*/
void
manage_transaction_stop (gboolean force_commit)
{
struct timeval now;
if (!in_transaction)
return;
gettimeofday (&now, NULL);
if (force_commit || TIMEVAL_SUBTRACT_MS (now, last_msg) >= 500)
{
sql_commit ();
in_transaction = FALSE;
}
}
/**
* @brief Validate a single port.
*
* @param[in] port A port.
*
* @return 0 success, 1 failed.
*/
static int
validate_port (const char *port)
{
const char *first;
while (*port && isblank (*port)) port++;
if (*port == '\0')
return 1;
first = port;
while (*first && isdigit (*first)) first++;
if (first == port)
return 1;
while (*first && isblank (*first)) first++;
if (*first == '\0')
{
long int number;
number = strtol (port, NULL, 10);
if (number <= 0)
return 1;
if (number > 65535)
return 1;
return 0;
}
return 1;
}
/**
* @brief Validate a single port, for use in override or note.
*
* @param[in] port A port.
*
* @return 0 success, 1 failed.
*/
static int
validate_results_port (const char *port)
{
long int num;
char *end;
if (!port)
return 1;
/* "cpe:abc", "general/tcp", "20/udp"
*
* We keep the "general/tcp" case pretty open because it is not clearly
* restricted anywhere, and is already used with non-alphanumerics in
* "general/Host_Details". We exclude whitespace, ',' and ';' to prevent
* users from entering lists of ports.
*
* Similarly, the CPE case forbids whitespace, but allows ',' and ';' as
* these may occur in valid CPEs. */
if (g_regex_match_simple
("^(cpe:[^\\s]+|general/[^\\s,;]+|[0-9]+/[[:alnum:]]+)$",
port, 0, 0)
== FALSE)
return 1;
if (g_str_has_prefix (port, "cpe:")
|| g_str_has_prefix (port, "general/"))
return 0;
num = strtol (port, &end, 10);
if (*end != '/')
return 1;
if (num > 0 && num <= 65535)
return 0;
return 1;
}
/**
* @brief Convert alive test name to alive test bitfield.
*
* @param[in] alive_tests Name of alive test.
*
* @return Alive test, or -1 on error.
*/
static int
alive_test_from_string (const char* alive_tests)
{
alive_test_t alive_test;
if (alive_tests == NULL
|| strcmp (alive_tests, "") == 0
|| strcmp (alive_tests, "Scan Config Default") == 0)
alive_test = 0;
else if (strcmp (alive_tests, "ICMP, TCP-ACK Service & ARP Ping") == 0)
alive_test = ALIVE_TEST_TCP_ACK_SERVICE | ALIVE_TEST_ICMP | ALIVE_TEST_ARP;
else if (strcmp (alive_tests, "TCP-ACK Service & ARP Ping") == 0)
alive_test = ALIVE_TEST_TCP_ACK_SERVICE | ALIVE_TEST_ARP;
else if (strcmp (alive_tests, "ICMP & ARP Ping") == 0)
alive_test = ALIVE_TEST_ICMP | ALIVE_TEST_ARP;
else if (strcmp (alive_tests, "ICMP & TCP-ACK Service Ping") == 0)
alive_test = ALIVE_TEST_ICMP | ALIVE_TEST_TCP_ACK_SERVICE;
else if (strcmp (alive_tests, "ARP Ping") == 0)
alive_test = ALIVE_TEST_ARP;
else if (strcmp (alive_tests, "TCP-ACK Service Ping") == 0)
alive_test = ALIVE_TEST_TCP_ACK_SERVICE;
else if (strcmp (alive_tests, "TCP-SYN Service Ping") == 0)
alive_test = ALIVE_TEST_TCP_SYN_SERVICE;
else if (strcmp (alive_tests, "ICMP Ping") == 0)
alive_test = ALIVE_TEST_ICMP;
else if (strcmp (alive_tests, "Consider Alive") == 0)
alive_test = ALIVE_TEST_CONSIDER_ALIVE;
else
return -1;
return alive_test;
}
/**
* @brief Set login data for a target.
*
* @param[in] target The target.
* @param[in] type The credential type (e.g. "ssh" or "smb").
* @param[in] credential The credential or 0 to remove.
* @param[in] port The port to authenticate at with credential.
*
* @return 0 on success, -1 on error, 1 target not found, 99 permission denied.
*/
static int
set_target_login_data (target_t target, const char* type,
credential_t credential, int port)
{
gchar *quoted_type;
if (current_credentials.uuid
&& (acl_user_may ("modify_target") == 0))
return 99;
if (type == NULL)
return -1;
if (target == 0)
return 1;
quoted_type = sql_quote (type);
if (sql_int ("SELECT count (*) FROM targets_login_data"
" WHERE target = %llu AND type = '%s';",
target, quoted_type))
{
if (credential == 0)
{
sql ("DELETE FROM targets_login_data"
" WHERE target = '%llu' AND type = '%s';",
target, quoted_type);
}
else
{
sql ("UPDATE targets_login_data"
" SET credential = %llu, port = %d"
" WHERE target = %llu AND type = '%s';",
credential, port, target, quoted_type);
}
}
else if (credential)
{
sql ("INSERT INTO targets_login_data (target, type, credential, port)"
" VALUES (%llu, '%s', %llu, %i)",
target, quoted_type, credential, port);
}
g_free (quoted_type);
return 0;
}
/**
* @brief Get a credential from a target.
*
* @param[in] target The target.
* @param[in] type The credential type (e.g. "ssh" or "smb").
*
* @return 0 on success, -1 on error, 1 credential not found, 99 permission
* denied.
*/
credential_t
target_credential (target_t target, const char* type)
{
gchar *quoted_type;
credential_t credential;
if (target == 0 || type == NULL)
return 0;
quoted_type = sql_quote (type);
if (sql_int ("SELECT NOT EXISTS"
" (SELECT * FROM targets_login_data"
" WHERE target = %llu and type = '%s');",
target, quoted_type))
{
g_free (quoted_type);
return 0;
}
sql_int64 (&credential,
"SELECT credential FROM targets_login_data"
" WHERE target = %llu AND type = '%s';",
target, quoted_type);
g_free (quoted_type);
return credential;
}
/**
* @brief Get a login port from a target.
*
* @param[in] target The target.
* @param[in] type The credential type (e.g. "ssh" or "smb").
*
* @return 0 on success, -1 on error, 1 credential not found, 99 permission
* denied.
*/
int
target_login_port (target_t target, const char* type)
{
gchar *quoted_type;
int port;
if (target == 0 || type == NULL)
return 0;
quoted_type = sql_quote (type);
if (sql_int ("SELECT NOT EXISTS"
" (SELECT * FROM targets_login_data"
" WHERE target = %llu and type = '%s');",
target, quoted_type))
{
g_free (quoted_type);
return 0;
}
port = sql_int ("SELECT port FROM targets_login_data"
" WHERE target = %llu AND type = '%s';",
target, quoted_type);
g_free (quoted_type);
return port;
}
/**
* @brief Create a target.
*
* @param[in] name Name of target.
* @param[in] asset_hosts_filter Asset host filter to select hosts.
* Overrides \p hosts and \p exclude_hosts.
* @param[in] hosts Host list of target.
* @param[in] exclude_hosts List of hosts to exclude from \p hosts.
* @param[in] comment Comment on target.
* @param[in] port_list_id Port list of target (overrides \p port_range).
* @param[in] port_range Port range of target.
* @param[in] ssh_credential SSH credential.
* @param[in] ssh_elevate_credential SSH previlige escalation credential.
* @param[in] ssh_port Port for SSH login.
* @param[in] smb_credential SMB credential.
* @param[in] esxi_credential ESXi credential.
* @param[in] snmp_credential SNMP credential.
* @param[in] reverse_lookup_only Scanner preference reverse_lookup_only.
* @param[in] reverse_lookup_unify Scanner preference reverse_lookup_unify.
* @param[in] alive_tests Alive tests.
* @param[in] allow_simultaneous_ips Scanner preference allow_simultaneous_ips.
* @param[out] target Created target.
*
* @return 0 success, 1 target exists already, 2 error in host specification,
* 3 too many hosts, 4 error in port range, 5 error in SSH port,
* 6 failed to find port list, 7 error in alive tests,
* 8 invalid SSH credential type, 9 invalid SSH elevate credential type,
* 10 invalid SMB credential type, 11 invalid ESXi credential type,
* 12 invalid SNMP credential type, 13 port range or port list required,
* 14 SSH elevate credential without an SSH credential,
* 99 permission denied, -1 error.
*/
int
create_target (const char* name, const char* asset_hosts_filter,
const char* hosts, const char* exclude_hosts,
const char* comment, const char* port_list_id,
const char* port_range, credential_t ssh_credential,
credential_t ssh_elevate_credential,
const char* ssh_port, credential_t smb_credential,
credential_t esxi_credential, credential_t snmp_credential,
const char *reverse_lookup_only,
const char *reverse_lookup_unify, const char *alive_tests,
const char *allow_simultaneous_ips,
target_t* target)
{
gchar *quoted_name, *quoted_hosts, *quoted_exclude_hosts, *quoted_comment;
gchar *port_list_comment, *quoted_ssh_port, *clean, *clean_exclude;
gchar *chosen_hosts;
port_list_t port_list;
int ret, alive_test, max;
target_t new_target;
assert (current_credentials.uuid);
if (port_range && validate_port_range (port_range))
return 4;
if (ssh_port && validate_port (ssh_port))
return 5;
alive_test = alive_test_from_string (alive_tests);
if (alive_test <= -1)
return 7;
if (ssh_elevate_credential && (!ssh_credential))
return 14;
if (ssh_credential && (ssh_elevate_credential == ssh_credential))
return 15;
sql_begin_immediate ();
if (acl_user_may ("create_target") == 0)
{
sql_rollback ();
return 99;
}
if (resource_with_name_exists (name, "target", 0))
{
sql_rollback ();
return 1;
}
if (port_list_id)
{
if (find_port_list_with_permission (port_list_id, &port_list,
"get_port_lists")
|| (port_list == 0))
{
sql_rollback ();
return 6;
}
}
else if (port_range == NULL)
{
sql_rollback ();
return 13;
}
else
{
port_list_comment = g_strdup_printf ("Autogenerated for target %s.", name);
ret = create_port_list_unique (name, port_list_comment, port_range,
&port_list);
g_free (port_list_comment);
if (ret)
{
sql_rollback ();
return ret;
}
}
if (asset_hosts_filter)
{
iterator_t asset_hosts;
int previous;
get_data_t get;
GString *buffer;
memset (&get, 0, sizeof (get));
get.filter = g_strdup (asset_hosts_filter);
init_asset_host_iterator (&asset_hosts, &get);
g_free (get.filter);
previous = 0;
buffer = g_string_new ("");
while (next (&asset_hosts))
{
g_string_append_printf (buffer,
"%s%s",
previous ? ", " : "",
get_iterator_name (&asset_hosts));
previous = 1;
}
cleanup_iterator (&asset_hosts);
chosen_hosts = g_string_free (buffer, FALSE);
g_debug ("asset chosen_hosts: %s", chosen_hosts);
}
else
{
chosen_hosts = g_strdup (hosts);
g_debug ("manual chosen_hosts: %s", chosen_hosts);
}
clean = clean_hosts (chosen_hosts, &max);
g_free (chosen_hosts);
if (exclude_hosts)
clean_exclude = clean_hosts (exclude_hosts, NULL);
else
clean_exclude = g_strdup ("");
max = manage_count_hosts (clean, clean_exclude);
if (max <= 0)
{
g_free (clean);
g_free (clean_exclude);
sql_rollback ();
return 2;
}
if (max > max_hosts)
{
g_free (clean);
g_free (clean_exclude);
sql_rollback ();
return 3;
}
quoted_hosts = sql_quote (clean);
quoted_exclude_hosts = sql_quote (clean_exclude);
g_free (clean);
g_free (clean_exclude);
if (ssh_credential)
quoted_ssh_port = sql_insert (ssh_port ? ssh_port : "22");
else
quoted_ssh_port = g_strdup ("NULL");
if (reverse_lookup_only == NULL || strcmp (reverse_lookup_only, "0") == 0)
reverse_lookup_only = "0";
else
reverse_lookup_only = "1";
if (reverse_lookup_unify == NULL || strcmp (reverse_lookup_unify, "0") == 0)
reverse_lookup_unify = "0";
else
reverse_lookup_unify = "1";
if (allow_simultaneous_ips
&& strcmp (allow_simultaneous_ips, "0") == 0)
allow_simultaneous_ips = "0";
else
allow_simultaneous_ips = "1";
quoted_name = sql_quote (name ?: "");
if (comment)
quoted_comment = sql_quote (comment);
else
quoted_comment = sql_quote ("");
sql ("INSERT INTO targets"
" (uuid, name, owner, hosts, exclude_hosts, comment, "
" port_list, reverse_lookup_only, reverse_lookup_unify, alive_test,"
" allow_simultaneous_ips,"
" creation_time, modification_time)"
" VALUES (make_uuid (), '%s',"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', '%s', '%s', %llu, '%s', '%s', %i,"
" %s,"
" m_now (), m_now ());",
quoted_name, current_credentials.uuid,
quoted_hosts, quoted_exclude_hosts, quoted_comment, port_list,
reverse_lookup_only, reverse_lookup_unify, alive_test,
allow_simultaneous_ips);
new_target = sql_last_insert_id ();
if (target)
*target = new_target;
g_free (quoted_comment);
g_free (quoted_name);
g_free (quoted_hosts);
g_free (quoted_exclude_hosts);
if (ssh_credential)
{
gchar *type = credential_type (ssh_credential);
if (strcmp (type, "usk") && strcmp (type, "up"))
{
sql_rollback ();
g_free (quoted_ssh_port);
return 8;
}
g_free (type);
sql ("INSERT INTO targets_login_data"
" (target, type, credential, port)"
" VALUES (%llu, 'ssh', %llu, %s);",
new_target, ssh_credential, quoted_ssh_port);
}
g_free (quoted_ssh_port);
if (ssh_elevate_credential)
{
gchar *type = credential_type (ssh_elevate_credential);
if (strcmp (type, "up"))
{
sql_rollback ();
return 9;
}
g_free (type);
sql ("INSERT INTO targets_login_data"
" (target, type, credential, port)"
" VALUES (%llu, 'elevate', %llu, %s);",
new_target, ssh_elevate_credential, "0");
}
if (smb_credential)
{
gchar *type = credential_type (smb_credential);
if (strcmp (type, "up"))
{
sql_rollback ();
return 10;
}
g_free (type);
sql ("INSERT INTO targets_login_data"
" (target, type, credential, port)"
" VALUES (%llu, 'smb', %llu, %s);",
new_target, smb_credential, "0");
}
if (esxi_credential)
{
gchar *type = credential_type (esxi_credential);
if (strcmp (type, "up"))
{
sql_rollback ();
return 11;
}
g_free (type);
sql ("INSERT INTO targets_login_data"
" (target, type, credential, port)"
" VALUES (%llu, 'esxi', %llu, %s);",
new_target, esxi_credential, "0");
}
if (snmp_credential)
{
gchar *type = credential_type (snmp_credential);
if (strcmp (type, "snmp"))
{
sql_rollback ();
return 12;
}
g_free (type);
sql ("INSERT INTO targets_login_data"
" (target, type, credential, port)"
" VALUES (%llu, 'snmp', %llu, %s);",
new_target, snmp_credential, "0");
}
sql_commit ();
return 0;
}
/**
* @brief Create a target from an existing target.
*
* @param[in] name Name of new target. NULL to copy from existing.
* @param[in] comment Comment on new target. NULL to copy from existing.
* @param[in] target_id UUID of existing target.
* @param[out] new_target New target.
*
* @return 0 success, 1 target exists already, 2 failed to find existing
* target, 99 permission denied, -1 error.
*/
int
copy_target (const char* name, const char* comment, const char *target_id,
target_t* new_target)
{
int ret;
target_t old_target;
assert (new_target);
ret = copy_resource ("target", name, comment, target_id,
"hosts, exclude_hosts, port_list, reverse_lookup_only,"
" reverse_lookup_unify, alive_test,"
" allow_simultaneous_ips",
1, new_target, &old_target);
if (ret)
return ret;
sql ("INSERT INTO targets_login_data (target, type, credential, port)"
" SELECT %llu, type, credential, port"
" FROM targets_login_data"
" WHERE target = %llu;",
*new_target, old_target);
return 0;
}
/**
* @brief Delete a target.
*
* @param[in] target_id UUID of target.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 fail because a task refers to the target, 2 failed
* to find target, 99 permission denied, -1 error.
*/
int
delete_target (const char *target_id, int ultimate)
{
target_t target = 0;
target_t trash_target;
sql_begin_immediate ();
if (acl_user_may ("delete_target") == 0)
{
sql_rollback ();
return 99;
}
if (find_target_with_permission (target_id, &target, "delete_target"))
{
sql_rollback ();
return -1;
}
if (target == 0)
{
if (find_trash ("target", target_id, &target))
{
sql_rollback ();
return -1;
}
if (target == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
/* Check if it's in use by a task in the trashcan. */
if (sql_int ("SELECT count(*) FROM tasks"
" WHERE target = %llu"
" AND target_location = " G_STRINGIFY (LOCATION_TRASH) ";",
target))
{
sql_rollback ();
return 1;
}
permissions_set_orphans ("target", target, LOCATION_TRASH);
tags_remove_resource ("target", target, LOCATION_TRASH);
sql ("DELETE FROM targets_trash_login_data WHERE target = %llu;", target);
sql ("DELETE FROM targets_trash WHERE id = %llu;", target);
sql_commit ();
return 0;
}
if (ultimate == 0)
{
if (sql_int ("SELECT count(*) FROM tasks"
" WHERE target = %llu"
" AND target_location = " G_STRINGIFY (LOCATION_TABLE)
" AND hidden = 0;",
target))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO targets_trash"
" (uuid, owner, name, hosts, exclude_hosts, comment,"
" port_list, port_list_location,"
" reverse_lookup_only, reverse_lookup_unify, alive_test,"
" allow_simultaneous_ips,"
" creation_time, modification_time)"
" SELECT uuid, owner, name, hosts, exclude_hosts, comment,"
" port_list, " G_STRINGIFY (LOCATION_TABLE) ","
" reverse_lookup_only, reverse_lookup_unify, alive_test,"
" allow_simultaneous_ips,"
" creation_time, modification_time"
" FROM targets WHERE id = %llu;",
target);
trash_target = sql_last_insert_id ();
/* Copy login data */
sql ("INSERT INTO targets_trash_login_data"
" (target, type, credential, port, credential_location)"
" SELECT %llu, type, credential, port, "
G_STRINGIFY (LOCATION_TABLE)
" FROM targets_login_data WHERE target = %llu;",
trash_target, target);
/* Update the location of the target in any trashcan tasks. */
sql ("UPDATE tasks"
" SET target = %llu,"
" target_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE target = %llu"
" AND target_location = " G_STRINGIFY (LOCATION_TABLE) ";",
sql_last_insert_id (),
target);
permissions_set_locations ("target", target,
sql_last_insert_id (),
LOCATION_TRASH);
tags_set_locations ("target", target,
sql_last_insert_id (),
LOCATION_TRASH);
}
else if (sql_int ("SELECT count(*) FROM tasks"
" WHERE target = %llu"
" AND target_location = " G_STRINGIFY (LOCATION_TABLE),
target))
{
sql_rollback ();
return 1;
}
else
{
permissions_set_orphans ("target", target, LOCATION_TABLE);
tags_remove_resource ("target", target, LOCATION_TABLE);
}
sql ("DELETE FROM targets_login_data WHERE target = %llu;", target);
sql ("DELETE FROM targets WHERE id = %llu;", target);
sql_commit ();
return 0;
}
/**
* @brief Modify a target.
*
* @param[in] target_id UUID of target.
* @param[in] name Name of target.
* @param[in] hosts Host list of target.
* @param[in] exclude_hosts List of hosts to exclude from \p hosts.
* @param[in] comment Comment on target.
* @param[in] port_list_id Port list of target (overrides \p port_range).
* @param[in] ssh_credential_id SSH credential.
* @param[in] ssh_elevate_credential_id SSH previlige escalation credential.
* @param[in] ssh_port Port for SSH login.
* @param[in] smb_credential_id SMB credential.
* @param[in] esxi_credential_id ESXi credential.
* @param[in] snmp_credential_id SNMP credential.
* @param[in] reverse_lookup_only Scanner preference reverse_lookup_only.
* @param[in] reverse_lookup_unify Scanner preference reverse_lookup_unify.
* @param[in] alive_tests Alive tests.
* @param[in] allow_simultaneous_ips Scanner preference allow_simultaneous_ips.
*
* @return 0 success, 1 target exists already, 2 error in host specification,
* 3 too many hosts, 4 error in port range, 5 error in SSH port,
* 6 failed to find port list, 7 failed to find SSH cred, 8 failed to
* find SMB cred, 9 failed to find target, 10 error in alive tests,
* 11 zero length name, 12 exclude hosts requires hosts
* 13 hosts requires exclude hosts,
* 14 hosts must be at least one character, 15 target is in use,
* 16 failed to find ESXi cred, 17 failed to find SNMP cred,
* 18 invalid SSH credential type, 19 invalid SMB credential type,
* 20 invalid ESXi credential type, 21 invalid SNMP credential type,
* 22 failed to find SSH elevate cred, 23 invalid SSH elevate
* credential type, 24 SSH elevate credential without SSH credential,
* 25 SSH elevate credential equals SSH credential,
* 99 permission denied, -1 error.
*/
int
modify_target (const char *target_id, const char *name, const char *hosts,
const char *exclude_hosts, const char *comment,
const char *port_list_id, const char *ssh_credential_id,
const char *ssh_elevate_credential_id,
const char *ssh_port, const char *smb_credential_id,
const char *esxi_credential_id, const char* snmp_credential_id,
const char *reverse_lookup_only,
const char *reverse_lookup_unify, const char *alive_tests,
const char *allow_simultaneous_ips)
{
target_t target;
credential_t ssh_credential = 0;
credential_t ssh_elevate_credential = 0;
assert (target_id);
sql_begin_immediate ();
assert (current_credentials.uuid);
if (acl_user_may ("modify_target") == 0)
{
sql_rollback ();
return 99;
}
if (hosts && (exclude_hosts == NULL))
{
sql_rollback ();
return 13;
}
target = 0;
if (find_target_with_permission (target_id, &target, "modify_target"))
{
sql_rollback ();
return -1;
}
if (target == 0)
{
sql_rollback ();
return 9;
}
if (name)
{
gchar *quoted_name;
if (strlen (name) == 0)
{
sql_rollback ();
return 11;
}
if (resource_with_name_exists (name, "target", target))
{
sql_rollback ();
return 1;
}
quoted_name = sql_quote (name);
sql ("UPDATE targets SET"
" name = '%s',"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_name,
target);
g_free (quoted_name);
}
if (comment)
{
gchar *quoted_comment;
quoted_comment = sql_quote (comment);
sql ("UPDATE targets SET"
" comment = '%s',"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_comment,
target);
g_free (quoted_comment);
}
if (allow_simultaneous_ips)
{
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
sql ("UPDATE targets SET"
" allow_simultaneous_ips = '%i',"
" modification_time = m_now ()"
" WHERE id = %llu;",
strcmp (allow_simultaneous_ips, "0") ? 1 : 0,
target);
}
if (alive_tests)
{
int alive_test;
alive_test = alive_test_from_string (alive_tests);
if (alive_test <= -1)
{
sql_rollback ();
return 10;
}
sql ("UPDATE targets SET"
" alive_test = '%i',"
" modification_time = m_now ()"
" WHERE id = %llu;",
alive_test,
target);
}
if (port_list_id)
{
port_list_t port_list;
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
port_list = 0;
if (find_port_list_with_permission (port_list_id, &port_list,
"get_port_lists"))
{
sql_rollback ();
return -1;
}
if (port_list == 0)
{
sql_rollback ();
return 6;
}
sql ("UPDATE targets SET"
" port_list = %llu,"
" modification_time = m_now ()"
" WHERE id = %llu;",
port_list,
target);
}
if (ssh_credential_id)
{
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
ssh_credential = 0;
if (strcmp (ssh_credential_id, "0"))
{
int port_int;
gchar *type;
if (find_credential_with_permission (ssh_credential_id,
&ssh_credential,
"get_credentials"))
{
sql_rollback ();
return -1;
}
if (ssh_credential == 0)
{
sql_rollback ();
return 7;
}
if (ssh_port && strcmp (ssh_port, "0") && strcmp (ssh_port, ""))
{
if (validate_port (ssh_port))
{
sql_rollback ();
return 5;
}
port_int = atoi (ssh_port);
}
else
port_int = 22;
type = credential_type (ssh_credential);
if (strcmp (type, "up") && strcmp (type, "usk"))
{
sql_rollback ();
return 18;
}
g_free (type);
set_target_login_data (target, "ssh", ssh_credential, port_int);
}
else
set_target_login_data (target, "ssh", 0, 0);
}
if (ssh_elevate_credential_id)
{
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
ssh_elevate_credential = 0;
if (strcmp (ssh_elevate_credential_id, "0"))
{
gchar *type;
if (find_credential_with_permission (ssh_elevate_credential_id,
&ssh_elevate_credential,
"get_credentials"))
{
sql_rollback ();
return -1;
}
if (ssh_elevate_credential == 0)
{
sql_rollback ();
return 22;
}
type = credential_type (ssh_elevate_credential);
if (strcmp (type, "up"))
{
sql_rollback ();
return 23;
}
g_free (type);
set_target_login_data (target, "elevate", ssh_elevate_credential, 0);
}
else
set_target_login_data (target, "elevate", 0, 0);
}
if (smb_credential_id)
{
credential_t smb_credential;
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
smb_credential = 0;
if (strcmp (smb_credential_id, "0"))
{
gchar *type;
if (find_credential_with_permission (smb_credential_id,
&smb_credential,
"get_credentials"))
{
sql_rollback ();
return -1;
}
if (smb_credential == 0)
{
sql_rollback ();
return 7;
}
type = credential_type (smb_credential);
if (strcmp (type, "up"))
{
sql_rollback ();
return 19;
}
g_free (type);
set_target_login_data (target, "smb", smb_credential, 0);
}
else
set_target_login_data (target, "smb", 0, 0);
}
if (esxi_credential_id)
{
credential_t esxi_credential;
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
esxi_credential = 0;
if (strcmp (esxi_credential_id, "0"))
{
gchar *type;
if (find_credential_with_permission (esxi_credential_id,
&esxi_credential,
"get_credentials"))
{
sql_rollback ();
return -1;
}
if (esxi_credential == 0)
{
sql_rollback ();
return 16;
}
type = credential_type (esxi_credential);
if (strcmp (type, "up"))
{
sql_rollback ();
return 20;
}
g_free (type);
set_target_login_data (target, "esxi", esxi_credential, 0);
}
else
set_target_login_data (target, "esxi", 0, 0);
}
if (snmp_credential_id)
{
credential_t snmp_credential;
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
snmp_credential = 0;
if (strcmp (snmp_credential_id, "0"))
{
gchar *type;
if (find_credential_with_permission (snmp_credential_id,
&snmp_credential,
"get_credentials"))
{
sql_rollback ();
return -1;
}
if (snmp_credential == 0)
{
sql_rollback ();
return 17;
}
type = credential_type (snmp_credential);
if (strcmp (type, "snmp"))
{
sql_rollback ();
return 21;
}
g_free (type);
set_target_login_data (target, "snmp", snmp_credential, 0);
}
else
set_target_login_data (target, "snmp", 0, 0);
}
if (ssh_credential_id || ssh_elevate_credential_id)
{
if (!ssh_credential_id)
ssh_credential = target_ssh_credential (target);
if (!ssh_elevate_credential_id)
ssh_elevate_credential = target_ssh_elevate_credential (target);
if (ssh_elevate_credential && !ssh_credential)
{
sql_rollback ();
return 24;
}
if (ssh_credential && (ssh_credential == ssh_elevate_credential))
{
sql_rollback ();
return 25;
}
}
if (exclude_hosts)
{
gchar *quoted_exclude_hosts, *quoted_hosts, *clean, *clean_exclude;
int max;
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
if (hosts == NULL)
{
sql_rollback ();
return 12;
}
if (strlen (hosts) == 0)
{
sql_rollback ();
return 14;
}
clean = clean_hosts (hosts, &max);
clean_exclude = clean_hosts (exclude_hosts, NULL);
max = manage_count_hosts (clean, clean_exclude);
if (max <= 0)
{
g_free (clean);
g_free (clean_exclude);
sql_rollback ();
return 2;
}
if (max > max_hosts)
{
g_free (clean);
g_free (clean_exclude);
sql_rollback ();
return 3;
}
quoted_hosts = sql_quote (clean);
quoted_exclude_hosts = sql_quote (clean_exclude);
g_free (clean);
g_free (clean_exclude);
sql ("UPDATE targets SET"
" hosts = '%s',"
" exclude_hosts = '%s',"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_hosts,
quoted_exclude_hosts,
target);
g_free (quoted_hosts);
g_free (quoted_exclude_hosts);
}
if (reverse_lookup_only)
{
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
sql ("UPDATE targets SET"
" reverse_lookup_only = '%i',"
" modification_time = m_now ()"
" WHERE id = %llu;",
strcmp (reverse_lookup_only, "0") ? 1 : 0,
target);
}
if (reverse_lookup_unify)
{
if (target_in_use (target))
{
sql_rollback ();
return 15;
}
sql ("UPDATE targets SET"
" reverse_lookup_unify = '%i',"
" modification_time = m_now ()"
" WHERE id = %llu;",
strcmp (reverse_lookup_unify, "0") ? 1 : 0,
target);
}
sql_commit ();
return 0;
}
/**
* @brief Filter columns for target iterator.
*/
#define TARGET_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "hosts", "exclude_hosts", "ips", "port_list", \
"ssh_credential", "smb_credential", "esxi_credential", "snmp_credential", \
"ssh_elevate_credential", NULL }
/**
* @brief Target iterator columns.
*/
#define TARGET_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (targets), \
{ "hosts", NULL, KEYWORD_TYPE_STRING }, \
{ "target_credential (id, 0, CAST ('ssh' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "target_login_port (id, 0, CAST ('ssh' AS text))", \
"ssh_port", \
KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 0, CAST ('smb' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "port_list", NULL, KEYWORD_TYPE_INTEGER }, \
{ "0", NULL, KEYWORD_TYPE_INTEGER }, \
{ "0", NULL, KEYWORD_TYPE_INTEGER }, \
{ \
"(SELECT uuid FROM port_lists" \
" WHERE port_lists.id = port_list)", \
NULL, \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT name FROM port_lists" \
" WHERE port_lists.id = port_list)", \
"port_list", \
KEYWORD_TYPE_STRING \
}, \
{ "0", NULL, KEYWORD_TYPE_INTEGER }, \
{ "exclude_hosts", NULL, KEYWORD_TYPE_STRING }, \
{ "reverse_lookup_only", NULL, KEYWORD_TYPE_INTEGER }, \
{ "reverse_lookup_unify", NULL, KEYWORD_TYPE_INTEGER }, \
{ "alive_test", NULL, KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 0, CAST ('esxi' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "0", NULL, KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 0, CAST ('snmp' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "0", NULL, KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 0, CAST ('elevate' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "0", NULL, KEYWORD_TYPE_INTEGER }, \
{ "allow_simultaneous_ips", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ \
"(SELECT name FROM credentials" \
" WHERE credentials.id" \
" = target_credential (targets.id, 0," \
" CAST ('ssh' AS text)))", \
"ssh_credential", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT name FROM credentials" \
" WHERE credentials.id" \
" = target_credential (targets.id, 0," \
" CAST ('smb' AS text)))", \
"smb_credential", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT name FROM credentials" \
" WHERE credentials.id" \
" = target_credential (targets.id, 0," \
" CAST ('esxi' AS text)))", \
"esxi_credential", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT name FROM credentials" \
" WHERE credentials.id" \
" = target_credential (targets.id, 0," \
" CAST ('snmp' AS text)))", \
"snmp_credential", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT name FROM credentials" \
" WHERE credentials.id" \
" = target_credential (targets.id, 0," \
" CAST ('elevate' AS text)))", \
"ssh_elevate_credential", \
KEYWORD_TYPE_STRING \
}, \
{ "hosts", NULL, KEYWORD_TYPE_STRING }, \
{ "max_hosts (hosts, exclude_hosts)", \
"ips", \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Target iterator columns for trash case.
*/
#define TARGET_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (targets_trash), \
{ "hosts", NULL, KEYWORD_TYPE_STRING }, \
{ "target_credential (id, 1, CAST ('ssh' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "target_login_port (id, 1, CAST ('ssh' AS text))", \
"ssh_port", \
KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 1, CAST ('smb' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "port_list", NULL, KEYWORD_TYPE_INTEGER }, \
{ "trash_target_credential_location (id, CAST ('ssh' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "trash_target_credential_location (id, CAST ('smb' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ \
"(CASE" \
" WHEN port_list_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT uuid FROM port_lists_trash" \
" WHERE port_lists_trash.id = port_list)" \
" ELSE (SELECT uuid FROM port_lists" \
" WHERE port_lists.id = port_list)" \
" END)", \
NULL, \
KEYWORD_TYPE_STRING \
}, \
{ \
"(CASE" \
" WHEN port_list_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT name FROM port_lists_trash" \
" WHERE port_lists_trash.id = port_list)" \
" ELSE (SELECT name FROM port_lists" \
" WHERE port_lists.id = port_list)" \
" END)", \
NULL, \
KEYWORD_TYPE_STRING \
}, \
{ "port_list_location = " G_STRINGIFY (LOCATION_TRASH), \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "exclude_hosts", NULL, KEYWORD_TYPE_STRING }, \
{ "reverse_lookup_only", NULL, KEYWORD_TYPE_INTEGER }, \
{ "reverse_lookup_unify", NULL, KEYWORD_TYPE_INTEGER }, \
{ "alive_test", NULL, KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 1, CAST ('esxi' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "trash_target_credential_location (id, CAST ('esxi' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 1, CAST ('snmp' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "trash_target_credential_location (id, CAST ('snmp' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "target_credential (id, 1, CAST ('elevate' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "trash_target_credential_location (id, CAST ('elevate' AS text))", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "allow_simultaneous_ips", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of targets.
*
* @param[in] get GET params.
*
* @return Total number of targets in filtered set.
*/
int
target_count (const get_data_t *get)
{
static const char *extra_columns[] = TARGET_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = TARGET_ITERATOR_COLUMNS;
static column_t trash_columns[] = TARGET_ITERATOR_TRASH_COLUMNS;
return count ("target", get, columns, trash_columns, extra_columns, 0, 0, 0,
TRUE);
}
/**
* @brief Initialise a target iterator, given a single target.
*
* @param[in] iterator Iterator.
* @param[in] target Single target to iterate.
*/
void
init_target_iterator_one (iterator_t* iterator, target_t target)
{
get_data_t get;
assert (target);
memset (&get, '\0', sizeof (get));
get.id = target_uuid (target);
get.filter = "owner=any permission=get_targets";
/* We could pass the return up to the caller, but we don't pass in
* a filter id and the callers are all in situations where the
* target cannot disappear, so it's safe to ignore the return. */
init_target_iterator (iterator, &get);
}
/**
* @brief Initialise a target iterator, including observed targets.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find target, 2 failed to find filter,
* -1 error.
*/
int
init_target_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = TARGET_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = TARGET_ITERATOR_COLUMNS;
static column_t trash_columns[] = TARGET_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"target",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Get the hosts of the target from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return Hosts of the target or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_hosts, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the SSH LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return SSH LSC credential.
*/
int
target_iterator_ssh_credential (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
return ret;
}
/**
* @brief Get the SSH LSC port of the target from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return SSH LSC port of the target or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_ssh_port, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get the SMB LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return SMB LSC credential.
*/
int
target_iterator_smb_credential (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
return ret;
}
/**
* @brief Get the location of the SSH LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return 0 in table, 1 in trash
*/
int
target_iterator_ssh_trash (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
return ret;
}
/**
* @brief Get the location of the SMB LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return 0 in table, 1 in trash
*/
int
target_iterator_smb_trash (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
return ret;
}
/**
* @brief Get the port list uuid of the target from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID of the target port list or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_port_list_uuid, GET_ITERATOR_COLUMN_COUNT + 7);
/**
* @brief Get the port list name of the target from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return Name of the target port list or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_port_list_name, GET_ITERATOR_COLUMN_COUNT + 8);
/**
* @brief Get the location of the port list from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return 0 in table, 1 in trash.
*/
int
target_iterator_port_list_trash (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
return ret;
}
/**
* @brief Get the excluded hosts of the target from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return Excluded hosts of the target or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_exclude_hosts, GET_ITERATOR_COLUMN_COUNT + 10);
/**
* @brief Get the reverse lookup only value from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return Reverse lookup only of the target or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_reverse_lookup_only,
GET_ITERATOR_COLUMN_COUNT + 11);
/**
* @brief Get the reverse lookup unify value from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return Reverse lookup unify of the target or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_reverse_lookup_unify,
GET_ITERATOR_COLUMN_COUNT + 12);
/**
* @brief Get the alive test description from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return Reverse lookup unify of the target or NULL if iteration is complete.
*/
const char*
target_iterator_alive_tests (iterator_t* iterator)
{
int tests;
if (iterator->done) return "";
tests = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 13);
if ((tests & ALIVE_TEST_TCP_ACK_SERVICE)
&& (tests & ALIVE_TEST_ICMP)
&& (tests & ALIVE_TEST_ARP))
return "ICMP, TCP-ACK Service & ARP Ping";
if ((tests & ALIVE_TEST_TCP_ACK_SERVICE)
&& (tests & ALIVE_TEST_ARP))
return "TCP-ACK Service & ARP Ping";
if ((tests & ALIVE_TEST_ICMP)
&& (tests & ALIVE_TEST_ARP))
return "ICMP & ARP Ping";
if ((tests & ALIVE_TEST_ICMP)
&& (tests & ALIVE_TEST_TCP_ACK_SERVICE))
return "ICMP & TCP-ACK Service Ping";
if (tests & ALIVE_TEST_ARP)
return "ARP Ping";
if (tests & ALIVE_TEST_TCP_ACK_SERVICE)
return "TCP-ACK Service Ping";
if (tests & ALIVE_TEST_TCP_SYN_SERVICE)
return "TCP-SYN Service Ping";
if (tests & ALIVE_TEST_ICMP)
return "ICMP Ping";
if (tests & ALIVE_TEST_CONSIDER_ALIVE)
return "Consider Alive";
return "Scan Config Default";
}
/**
* @brief Get the ESXi LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return ESXi LSC credential.
*/
int
target_iterator_esxi_credential (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 14);
return ret;
}
/**
* @brief Get the ESXi LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return ESXi LSC credential.
*/
int
target_iterator_esxi_trash (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 15);
return ret;
}
/**
* @brief Get the SNMP LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return ESXi LSC credential.
*/
int
target_iterator_snmp_credential (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 16);
return ret;
}
/**
* @brief Get the SNMP LSC credential location from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return ESXi LSC credential.
*/
int
target_iterator_snmp_trash (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 17);
return ret;
}
/**
* @brief Get the ELEVATE LSC credential from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return ELEVATE LSC credential.
*/
int
target_iterator_ssh_elevate_credential (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 18);
return ret;
}
/**
* @brief Get the ELEVATE LSC credential location from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return ELEVATE LSC credential.
*/
int
target_iterator_ssh_elevate_trash (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 19);
return ret;
}
/**
* @brief Get the allow_simultaneous_ips value from a target iterator.
*
* @param[in] iterator Iterator.
*
* @return allow_simult_ips_same_host or NULL if iteration is complete.
*/
DEF_ACCESS (target_iterator_allow_simultaneous_ips,
GET_ITERATOR_COLUMN_COUNT + 20);
/**
* @brief Return the UUID of a tag.
*
* @param[in] tag Tag.
*
* @return Newly allocated UUID if available, else NULL.
*/
char*
tag_uuid (tag_t tag)
{
return sql_string ("SELECT uuid FROM tags WHERE id = %llu;",
tag);
}
/**
* @brief Return the UUID of a target.
*
* @param[in] target Target.
*
* @return Newly allocated UUID if available, else NULL.
*/
char*
target_uuid (target_t target)
{
return sql_string ("SELECT uuid FROM targets WHERE id = %llu;",
target);
}
/**
* @brief Return the UUID of a trashcan target.
*
* @param[in] target Target.
*
* @return Newly allocated UUID if available, else NULL.
*/
char*
trash_target_uuid (target_t target)
{
return sql_string ("SELECT uuid FROM targets_trash WHERE id = %llu;",
target);
}
/**
* @brief Return the name of a target.
*
* @param[in] target Target.
*
* @return Newly allocated name if available, else NULL.
*/
char*
target_name (target_t target)
{
return sql_string ("SELECT name FROM targets WHERE id = %llu;",
target);
}
/**
* @brief Return the name of a trashcan target.
*
* @param[in] target Target.
*
* @return Newly allocated name if available, else NULL.
*/
char*
trash_target_name (target_t target)
{
return sql_string ("SELECT name FROM targets_trash WHERE id = %llu;",
target);
}
/**
* @brief Return the comment of a target.
*
* @param[in] target Target.
*
* @return Newly allocated name if available, else NULL.
*/
static char*
target_comment (target_t target)
{
return sql_string ("SELECT comment FROM targets WHERE id = %llu;",
target);
}
/**
* @brief Return the comment of a trashcan target.
*
* @param[in] target Target.
*
* @return Newly allocated name if available, else NULL.
*/
static char*
trash_target_comment (target_t target)
{
return sql_string ("SELECT comment FROM targets_trash WHERE id = %llu;",
target);
}
/**
* @brief Return whether a trashcan target is readable.
*
* @param[in] target Target.
*
* @return 1 if readable, else 0.
*/
int
trash_target_readable (target_t target)
{
char *uuid;
target_t found = 0;
if (target == 0)
return 0;
uuid = target_uuid (target);
if (find_trash ("target", uuid, &found))
{
g_free (uuid);
return 0;
}
g_free (uuid);
return found > 0;
}
/**
* @brief Return the hosts associated with a target.
*
* @param[in] target Target.
*
* @return Newly allocated comma separated list of hosts if available,
* else NULL.
*/
char*
target_hosts (target_t target)
{
return sql_string ("SELECT hosts FROM targets WHERE id = %llu;",
target);
}
/**
* @brief Return the excluded hosts associated with a target.
*
* @param[in] target Target.
*
* @return Newly allocated comma separated list of excluded hosts if available,
* else NULL.
*/
char*
target_exclude_hosts (target_t target)
{
return sql_string ("SELECT exclude_hosts FROM targets WHERE id = %llu;",
target);
}
/**
* @brief Return the reverse_lookup_only value of a target.
*
* @param[in] target Target.
*
* @return Reverse lookup only value if available, else NULL.
*/
char*
target_reverse_lookup_only (target_t target)
{
return sql_string ("SELECT reverse_lookup_only FROM targets"
" WHERE id = %llu;", target);
}
/**
* @brief Return the reverse_lookup_unify value of a target.
*
* @param[in] target Target.
*
* @return Reverse lookup unify value if available, else NULL.
*/
char*
target_reverse_lookup_unify (target_t target)
{
return sql_string ("SELECT reverse_lookup_unify FROM targets"
" WHERE id = %llu;", target);
}
/**
* @brief Return the allow_simultaneous_ips value of a target.
*
* @param[in] target Target.
*
* @return The allow_simultaneous_ips value if available, else NULL.
*/
char*
target_allow_simultaneous_ips (target_t target)
{
return sql_string ("SELECT allow_simultaneous_ips FROM targets"
" WHERE id = %llu;", target);
}
/**
* @brief Return the SSH LSC port of a target.
*
* @param[in] target Target.
*
* @return Newly allocated port if available, else NULL.
*/
char*
target_ssh_port (target_t target)
{
int port = target_login_port (target, "ssh");
return port ? g_strdup_printf ("%d", port) : NULL;
}
/**
* @brief Return the SSH credential associated with a target, if any.
*
* @param[in] target Target.
*
* @return SSH credential if any, else 0.
*/
credential_t
target_ssh_credential (target_t target)
{
return target_credential (target, "ssh");
}
/**
* @brief Return the SMB credential associated with a target, if any.
*
* @param[in] target Target.
*
* @return SMB credential if any, else 0.
*/
credential_t
target_smb_credential (target_t target)
{
return target_credential (target, "smb");
}
/**
* @brief Return the ESXi credential associated with a target, if any.
*
* @param[in] target Target.
*
* @return ESXi credential if any, else 0.
*/
credential_t
target_esxi_credential (target_t target)
{
return target_credential (target, "esxi");
}
/**
* @brief Return the ELEVATE credential associated with a target, if any.
*
* @param[in] target Target.
*
* @return ELEVATE credential if any, else 0.
*/
credential_t
target_ssh_elevate_credential (target_t target)
{
return target_credential (target, "elevate");
}
/**
* @brief Return the port list associated with a target, if any.
*
* @param[in] target Target.
*
* @return Port list
*/
port_list_t
target_port_list (target_t target)
{
port_list_t port_list;
switch (sql_int64 (&port_list,
"SELECT port_list FROM targets"
" WHERE id = %llu;",
target))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
return 0;
break;
default: /* Programming error. */
assert (0);
case -1:
/** @todo Move return to arg; return -1. */
return 0;
break;
}
return port_list;
}
/**
* @brief Return the port range of a target, in GMP port range list format.
*
* For "OpenVAS Default", return the explicit port ranges instead of "default".
*
* @param[in] target Target.
*
* @return Newly allocated port range if available, else NULL.
*/
char*
target_port_range (target_t target)
{
GString *range;
iterator_t ranges;
range = g_string_new ("");
init_port_range_iterator (&ranges, target_port_list (target), 0, 1,
"type, CAST (start AS INTEGER)");
if (next (&ranges))
{
const char *start, *end;
int type;
start = port_range_iterator_start (&ranges);
end = port_range_iterator_end (&ranges);
type = port_range_iterator_type_int (&ranges);
/* Scanner can only handle: T:1-3,5-6,9,U:1-2 */
if (end && strcmp (end, "0") && strcmp (end, start))
g_string_append_printf (range, "%s%s-%s",
(type == PORT_PROTOCOL_UDP ? "U:" : "T:"),
start, end);
else
g_string_append_printf (range, "%s%s",
(type == PORT_PROTOCOL_UDP ? "U:" : "T:"),
start);
while (next (&ranges))
{
int tcp;
start = port_range_iterator_start (&ranges);
end = port_range_iterator_end (&ranges);
tcp = (type == PORT_PROTOCOL_TCP);
type = port_range_iterator_type_int (&ranges);
if (end && strcmp (end, "0") && strcmp (end, start))
g_string_append_printf (range, ",%s%s-%s",
(tcp && type == PORT_PROTOCOL_UDP ? "U:" : ""),
start, end);
else
g_string_append_printf (range, ",%s%s",
(tcp && type == PORT_PROTOCOL_UDP ? "U:" : ""),
start);
}
}
cleanup_iterator (&ranges);
return g_string_free (range, FALSE);
}
/**
* @brief Return a target's alive tests.
*
* @param[in] target Target.
*
* @return Alive test bitfield.
*/
alive_test_t
target_alive_tests (target_t target)
{
return sql_int ("SELECT alive_test FROM targets WHERE id = %llu;",
target);
}
/**
* @brief Return whether a target is in use by a task.
*
* @param[in] target Target.
*
* @return 1 if in use, else 0.
*/
int
target_in_use (target_t target)
{
return !!sql_int ("SELECT count(*) FROM tasks"
" WHERE target = %llu"
" AND target_location = " G_STRINGIFY (LOCATION_TABLE)
" AND hidden = 0;",
target);
}
/**
* @brief Return whether a trashcan target is referenced by a task.
*
* @param[in] target Target.
*
* @return 1 if in use, else 0.
*/
int
trash_target_in_use (target_t target)
{
return !!sql_int ("SELECT count(*) FROM tasks"
" WHERE target = %llu"
" AND target_location = " G_STRINGIFY (LOCATION_TRASH),
target);
}
/**
* @brief Return whether a target is writable.
*
* @param[in] target Target.
*
* @return 1 if writable, else 0.
*/
int
target_writable (target_t target)
{
return 1;
}
/**
* @brief Return whether a trashcan target is writable.
*
* @param[in] target Target.
*
* @return 1 if writable, else 0.
*/
int
trash_target_writable (target_t target)
{
return trash_target_in_use (target) == 0;
}
/**
* @brief Initialise a target task iterator.
*
* Iterates over all tasks that use the target.
*
* @param[in] iterator Iterator.
* @param[in] target Target.
*/
void
init_target_task_iterator (iterator_t* iterator, target_t target)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (target);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_tasks"));
available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT name, uuid, %s FROM tasks"
" WHERE target = %llu"
" AND hidden = 0"
" ORDER BY name ASC;",
with_clause ? with_clause : "",
available,
target);
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the name from a target_task iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the host, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (target_task_iterator_name, 0);
/**
* @brief Get the uuid from a target_task iterator.
*
* @param[in] iterator Iterator.
*
* @return The uuid of the host, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (target_task_iterator_uuid, 1);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
target_task_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 2);
}
/* SecInfo Alerts. */
/**
* @brief Check for new SCAP SecInfo after an update.
*/
static void
check_for_new_scap ()
{
if (manage_scap_loaded ())
{
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM scap.cves"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_NEW_SECINFO, "cve", 0, 0);
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM scap.cpes"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_NEW_SECINFO, "cpe", 0, 0);
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM scap.ovaldefs"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_NEW_SECINFO, "ovaldef", 0, 0);
}
}
/**
* @brief Check for new CERT SecInfo after an update.
*/
static void
check_for_new_cert ()
{
if (manage_cert_loaded ())
{
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM cert.cert_bund_advs"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_NEW_SECINFO, "cert_bund_adv", 0, 0);
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM cert.dfn_cert_advs"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_NEW_SECINFO, "dfn_cert_adv", 0, 0);
}
}
/**
* @brief Print an URL for a New NVTs alert.
*
* @param[in] url Format string for url.
* @param[in] oid SecInfo ID.
* @param[in] type SecInfo Type.
*
* @return Freshly allocated url.
*/
static gchar *
alert_url_print (const gchar *url, const gchar *oid, const gchar *type)
{
int formatting;
const gchar *point, *end;
GString *new_url;
assert (url);
new_url = g_string_new ("");
for (formatting = 0, point = url, end = (url + strlen (url));
point < end;
point++)
if (formatting)
{
switch (*point)
{
case '$':
g_string_append_c (new_url, '$');
break;
case 'o':
{
g_string_append (new_url, oid);
break;
}
case 't':
{
g_string_append (new_url, type);
break;
}
default:
g_string_append_c (new_url, '$');
g_string_append_c (new_url, *point);
break;
}
formatting = 0;
}
else if (*point == '$')
formatting = 1;
else
g_string_append_c (new_url, *point);
return g_string_free (new_url, FALSE);
}
/**
* @brief Create list for New NVTs event.
*
* @param[in] event Event.
* @param[in] event_data Event type specific details.
* @param[in] alert Alert.
* @param[in] example Whether the message is an example only.
* @param[out] count_return NULL, or address for row count.
*
* @return Freshly allocated list.
*/
static gchar *
new_nvts_list (event_t event, const void* event_data, alert_t alert,
int example, int *count_return)
{
iterator_t rows;
GString *buffer;
int count;
char *details_url;
const gchar *type;
time_t feed_version_epoch;
feed_version_epoch = nvts_feed_version_epoch();
details_url = alert_data (alert, "method", "details_url");
type = (gchar*) event_data;
if (details_url && strlen (details_url))
buffer = g_string_new (NEW_NVTS_HEADER);
else
buffer = g_string_new (NEW_NVTS_HEADER_OID);
count = 0;
// TODO This should use an iterator provided by manage_sql_nvts.c.
if (example)
init_iterator (&rows,
"SELECT oid, name, solution_type, cvss_base, qod FROM nvts"
" LIMIT 4;");
else if (event == EVENT_NEW_SECINFO)
init_iterator (&rows,
"SELECT oid, name, solution_type, cvss_base, qod FROM nvts"
" WHERE creation_time > %ld"
" ORDER BY creation_time DESC;",
feed_version_epoch);
else
init_iterator (&rows,
"SELECT oid, name, solution_type, cvss_base, qod FROM nvts"
" WHERE modification_time > %ld"
" AND creation_time <= %ld"
" ORDER BY modification_time DESC;",
feed_version_epoch,
feed_version_epoch);
while (next (&rows))
{
gchar *url;
const char *name;
name = iterator_string (&rows, 1);
if (details_url && strlen (details_url))
url = alert_url_print (details_url, iterator_string (&rows, 0), type);
else
url = NULL;
g_string_append_printf (buffer,
"%-57.57s%-3s %13s %8s %3s%%%s%s%s",
name,
strlen (name) > 60
? "..."
: (strlen (name) > 57 ? name + 57 : " "),
iterator_string (&rows, 2),
iterator_string (&rows, 3),
iterator_string (&rows, 4),
url ? "\n " : " ",
url ? url : iterator_string (&rows, 0),
url ? "\n\n" : "\n");
g_free (url);
count++;
}
cleanup_iterator (&rows);
if (count_return)
*count_return = count;
return g_string_free (buffer, FALSE);
}
/**
* @brief Create list for New CVEs event.
*
* @param[in] event Event.
* @param[in] event_data Event type specific details.
* @param[in] alert Alert.
* @param[in] example Whether the message is an example only.
* @param[out] count_return NULL, or address for row count.
*
* @return Freshly allocated message.
*/
static gchar *
new_cves_list (event_t event, const void* event_data, alert_t alert,
int example, int *count_return)
{
iterator_t rows;
GString *buffer;
int count;
char *details_url;
const gchar *type;
details_url = alert_data (alert, "method", "details_url");
type = (gchar*) event_data;
buffer = g_string_new (NEW_CVES_HEADER);
count = 0;
if (example)
init_iterator (&rows,
"SELECT uuid, name, cvss, description FROM cves"
" LIMIT 4;");
else if (event == EVENT_NEW_SECINFO)
init_iterator (&rows,
"SELECT uuid, name, cvss, description FROM cves"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY creation_time DESC;");
else
init_iterator (&rows,
"SELECT uuid, name, cvss, description FROM cves"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY modification_time DESC;");
while (next (&rows))
{
gchar *url;
const char *name, *desc;
name = iterator_string (&rows, 1);
if (details_url && strlen (details_url))
url = alert_url_print (details_url, iterator_string (&rows, 0), type);
else
url = NULL;
desc = iterator_string (&rows, 3);
g_string_append_printf (buffer,
"%-15.15s %8s %50.50s%s%s%s%s",
name,
iterator_string (&rows, 2),
desc,
strlen (desc) > 53
? "..."
: (strlen (desc) > 50 ? desc + 50 : " "),
url ? "\n " : "",
url ? url : "",
url ? "\n\n" : "\n");
g_free (url);
count++;
}
cleanup_iterator (&rows);
if (count_return)
*count_return = count;
return g_string_free (buffer, FALSE);
}
/**
* @brief Create list for New CPEs event.
*
* @param[in] event Event.
* @param[in] event_data Event type specific details.
* @param[in] alert Alert.
* @param[in] example Whether the message is an example only.
* @param[out] count_return NULL, or address for row count.
*
* @return Freshly allocated list.
*/
static gchar *
new_cpes_list (event_t event, const void* event_data, alert_t alert,
int example, int *count_return)
{
iterator_t rows;
GString *buffer;
int count;
char *details_url;
const gchar *type;
details_url = alert_data (alert, "method", "details_url");
type = (gchar*) event_data;
buffer = g_string_new (NEW_CPES_HEADER);
count = 0;
if (example)
init_iterator (&rows,
"SELECT uuid, name, title FROM cpes"
" LIMIT 4;");
else if (event == EVENT_NEW_SECINFO)
init_iterator (&rows,
"SELECT uuid, name, title FROM cpes"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY creation_time DESC;");
else
init_iterator (&rows,
"SELECT uuid, name, title FROM cpes"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY modification_time DESC;");
while (next (&rows))
{
gchar *url;
const char *name, *title;
name = iterator_string (&rows, 1);
title = iterator_string (&rows, 2);
if (details_url && strlen (details_url))
url = alert_url_print (details_url, iterator_string (&rows, 0), type);
else
url = NULL;
g_string_append_printf (buffer,
"%-57.57s%-3s %-s%s%s%s",
name,
strlen (name) > 60
? "..."
: (strlen (name) > 57 ? name + 57 : " "),
title,
url ? "\n " : "",
url ? url : "",
url ? "\n\n" : "\n");
g_free (url);
count++;
}
cleanup_iterator (&rows);
if (count_return)
*count_return = count;
return g_string_free (buffer, FALSE);
}
/**
* @brief Create list for "New CERT-Bund Advisories" event message.
*
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] alert Alert.
* @param[in] example Whether the message is an example only.
* @param[out] count_return NULL, or address for row count.
*
* @return Freshly allocated string.
*/
static gchar *
new_cert_bunds_list (event_t event, const void* event_data, alert_t alert,
int example, int *count_return)
{
iterator_t rows;
GString *buffer;
int count;
char *details_url;
const gchar *type;
details_url = alert_data (alert, "method", "details_url");
type = (gchar*) event_data;
buffer = g_string_new (NEW_CERT_BUNDS_HEADER);
count = 0;
if (example)
init_iterator (&rows,
"SELECT uuid, name, title FROM cert_bund_advs"
" LIMIT 4;");
else if (event == EVENT_NEW_SECINFO)
init_iterator (&rows,
"SELECT uuid, name, title FROM cert_bund_advs"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY creation_time DESC;");
else
init_iterator (&rows,
"SELECT uuid, name, title FROM cert_bund_advs"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY modification_time DESC;");
while (next (&rows))
{
gchar *url;
const char *name, *title;
name = iterator_string (&rows, 1);
title = iterator_string (&rows, 2);
if (details_url && strlen (details_url))
url = alert_url_print (details_url, iterator_string (&rows, 0), type);
else
url = NULL;
g_string_append_printf (buffer,
"%-11s %-s%s%s%s",
name,
title,
url ? "\n " : "",
url ? url : "",
url ? "\n\n" : "\n");
g_free (url);
count++;
}
cleanup_iterator (&rows);
if (count_return)
*count_return = count;
return g_string_free (buffer, FALSE);
}
/**
* @brief Create list for "New DFN-CERT Advisories" event message.
*
* @param[in] event Event.
* @param[in] event_data Event type specific details.
* @param[in] alert Alert.
* @param[in] example Whether the message is an example only.
* @param[out] count_return NULL, or address for row count.
*
* @return Freshly allocated string.
*/
static gchar *
new_dfn_certs_list (event_t event, const void* event_data, alert_t alert,
int example, int *count_return)
{
iterator_t rows;
GString *buffer;
int count;
char *details_url;
const gchar *type;
details_url = alert_data (alert, "method", "details_url");
type = (gchar*) event_data;
buffer = g_string_new (NEW_DFN_CERTS_HEADER);
count = 0;
if (example)
init_iterator (&rows,
"SELECT uuid, name, title FROM dfn_cert_advs"
" LIMIT 4;");
else if (event == EVENT_NEW_SECINFO)
init_iterator (&rows,
"SELECT uuid, name, title FROM dfn_cert_advs"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY creation_time DESC;");
else
init_iterator (&rows,
"SELECT uuid, name, title FROM dfn_cert_advs"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY modification_time DESC;");
while (next (&rows))
{
gchar *url;
const char *name, *title;
name = iterator_string (&rows, 1);
title = iterator_string (&rows, 2);
if (details_url && strlen (details_url))
url = alert_url_print (details_url, iterator_string (&rows, 0), type);
else
url = NULL;
g_string_append_printf (buffer,
"%-18s %-s%s%s%s",
name,
title,
url ? "\n " : "",
url ? url : "",
url ? "\n\n" : "\n");
g_free (url);
count++;
}
cleanup_iterator (&rows);
if (count_return)
*count_return = count;
return g_string_free (buffer, FALSE);
}
/**
* @brief Create list for "New OVAL Definitions" event.
*
* @param[in] event Event.
* @param[in] event_data Event type specific details.
* @param[in] alert Alert.
* @param[in] example Whether the message is an example only.
* @param[out] count_return NULL, or address for row count.
*
* @return Freshly allocated list.
*/
static gchar *
new_oval_defs_list (event_t event, const void* event_data, alert_t alert,
int example, int *count_return)
{
iterator_t rows;
GString *buffer;
int count;
char *details_url;
const gchar *type;
details_url = alert_data (alert, "method", "details_url");
type = (gchar*) event_data;
buffer = g_string_new (NEW_OVAL_DEFS_HEADER);
count = 0;
if (example)
init_iterator (&rows,
"SELECT uuid, name, title FROM ovaldefs"
" LIMIT 4;");
else if (event == EVENT_NEW_SECINFO)
init_iterator (&rows,
"SELECT uuid, name, title FROM ovaldefs"
" WHERE creation_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY creation_time DESC;");
else
init_iterator (&rows,
"SELECT uuid, name, title FROM ovaldefs"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" ORDER BY modification_time DESC;");
while (next (&rows))
{
gchar *url;
const char *name, *title;
name = iterator_string (&rows, 1);
title = iterator_string (&rows, 2);
if (details_url && strlen (details_url))
url = alert_url_print (details_url, iterator_string (&rows, 0), type);
else
url = NULL;
g_string_append_printf (buffer,
"%-30s %-s%s%s%s",
name,
title,
url ? "\n " : "",
url ? url : "",
url ? "\n\n" : "\n");
g_free (url);
count++;
}
cleanup_iterator (&rows);
if (count_return)
*count_return = count;
return g_string_free (buffer, FALSE);
}
/**
* @brief Create message for New NVTs event.
*
* @param[in] event Event.
* @param[in] event_data Event data.
* @param[in] alert Alert.
* @param[out] count_return NULL, or address for row count.
*
* @return Freshly allocated list.
*/
static gchar *
new_secinfo_list (event_t event, const void* event_data, alert_t alert,
int *count_return)
{
g_debug ("%s: event_data: %s", __func__, (gchar*) event_data);
if (strcasecmp (event_data, "nvt_example") == 0)
return new_nvts_list (event, "nvt", alert, 1, count_return);
if (strcasecmp (event_data, "nvt") == 0)
return new_nvts_list (event, "nvt", alert, 0, count_return);
if (strcasecmp (event_data, "cve_example") == 0)
return new_cves_list (event, "cve", alert, 1, count_return);
if (strcasecmp (event_data, "cve") == 0)
return new_cves_list (event, "cve", alert, 0, count_return);
if (strcasecmp (event_data, "cpe_example") == 0)
return new_cpes_list (event, "cpe", alert, 1, count_return);
if (strcasecmp (event_data, "cpe") == 0)
return new_cpes_list (event, "cpe", alert, 0, count_return);
if (strcasecmp (event_data, "cert_bund_adv_example") == 0)
return new_cert_bunds_list (event, "cert_bund_adv", alert, 1, count_return);
if (strcasecmp (event_data, "cert_bund_adv") == 0)
return new_cert_bunds_list (event, "cert_bund_adv", alert, 0, count_return);
if (strcasecmp (event_data, "dfn_cert_adv_example") == 0)
return new_dfn_certs_list (event, "dfn_cert_adv", alert, 1, count_return);
if (strcasecmp (event_data, "dfn_cert_adv") == 0)
return new_dfn_certs_list (event, "dfn_cert_adv", alert, 0, count_return);
if (strcasecmp (event_data, "ovaldef_example") == 0)
return new_oval_defs_list (event, "ovaldef", alert, 1, count_return);
if (strcasecmp (event_data, "ovaldef") == 0)
return new_oval_defs_list (event, "ovaldef", alert, 0, count_return);
if (count_return)
{
g_warning ("%s: Type error: %s", __func__, (char *) event_data);
*count_return = 0;
}
return g_strdup ("ERROR generating list");
}
/**
* @brief Create message for New NVTs event.
*
* @param[in] event Event.
* @param[in] event_data Event type specific details.
* @param[in] alert Alert.
*
* @return Freshly allocated message.
*/
static gchar *
new_secinfo_message (event_t event, const void* event_data, alert_t alert)
{
gchar *type, *list, *message, *name, *point;
int count, example;
list = new_secinfo_list (event, event_data, alert, &count);
assert (count > 0);
type = g_strdup (event_data);
if (type && (point = strstr (type, "_example")))
{
example = 1;
point[0] = '\0';
}
else
example = 0;
name = alert_name (alert);
message = g_strdup_printf ("%s%i%s%s%s%s, according to the\nalert \"%s\":\n\n%s",
example
? "Warning: This is an example alert only.\n\n"
: "",
count,
event == EVENT_NEW_SECINFO
? " new "
: " ",
count == 1
? type_name (type)
: type_name_plural (type),
event == EVENT_NEW_SECINFO
? ""
: (count == 1 ? " was" : " were"),
event == EVENT_NEW_SECINFO
? " appeared in the feed"
: " updated in the feed",
name,
list);
free (name);
g_free (list);
return message;
}
/**
* @brief Check for updated SCAP SecInfo after an update.
*/
static void
check_for_updated_scap ()
{
if (manage_scap_loaded ())
{
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM scap.cves"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_UPDATED_SECINFO, "cve", 0, 0);
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM scap.cpes"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_UPDATED_SECINFO, "cpe", 0, 0);
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM scap.ovaldefs"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'scap_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_UPDATED_SECINFO, "ovaldef", 0, 0);
}
}
/**
* @brief Check for updated CERT SecInfo after an update.
*/
static void
check_for_updated_cert ()
{
if (manage_cert_loaded ())
{
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM cert.cert_bund_advs"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_UPDATED_SECINFO, "cert_bund_adv", 0, 0);
if (sql_int ("SELECT EXISTS"
" (SELECT * FROM cert.dfn_cert_advs"
" WHERE modification_time"
" > coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0)"
" AND creation_time"
" <= coalesce (CAST ((SELECT value FROM meta"
" WHERE name"
" = 'cert_check_time')"
" AS INTEGER),"
" 0));"))
event (EVENT_UPDATED_SECINFO, "dfn_cert_adv", 0, 0);
}
}
/* Credentials. */
/**
* @brief Ensure that there is an encryption key.
*
* This prevents contention problems that can happen when the key is
* created on the fly during a GMP operation.
*
* Up to caller to create transaction.
*
* @return 0 success, -1 error.
*/
static int
check_db_encryption_key ()
{
lsc_crypt_ctx_t crypt_ctx;
gchar *secret;
crypt_ctx = lsc_crypt_new ();
/* The encryption layer creates the key if it does not exist. */
secret = lsc_crypt_encrypt (crypt_ctx, "dummy", "dummy", NULL);
lsc_crypt_release (crypt_ctx);
if (secret == NULL)
return -1;
g_free (secret);
return 0;
}
/**
* @brief Check that a string represents a valid Private Key.
*
* @param[in] key_str Private Key string.
* @param[in] key_phrase Private Key passphrase.
*
* @return 0 if valid, 1 otherwise.
*/
int
check_private_key (const char *key_str, const char *key_phrase)
{
gnutls_x509_privkey_t key;
gnutls_datum_t data;
int ret;
assert (key_str);
if (gnutls_x509_privkey_init (&key))
return 1;
data.size = strlen (key_str);
data.data = (void *) g_strdup (key_str);
ret = gnutls_x509_privkey_import2 (key, &data, GNUTLS_X509_FMT_PEM,
key_phrase, 0);
if (ret)
{
gchar *public_key;
public_key = gvm_ssh_public_from_private (key_str, key_phrase);
if (public_key == NULL)
{
gnutls_x509_privkey_deinit (key);
g_free (data.data);
g_message ("%s: import failed: %s",
__func__, gnutls_strerror (ret));
return 1;
}
g_free (public_key);
}
g_free (data.data);
gnutls_x509_privkey_deinit (key);
return 0;
}
/**
* @brief Find a credential for a specific permission, given a UUID.
*
* @param[in] uuid UUID of credential.
* @param[out] credential Credential return, 0 if successfully failed
* to find Credential.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find credential), TRUE
* on error.
*/
gboolean
find_credential_with_permission (const char* uuid,
credential_t* credential,
const char *permission)
{
return find_resource_with_permission ("credential", uuid, credential,
permission, 0);
}
/**
* @brief Set data for a credential.
*
* @param[in] credential The credential.
* @param[in] type The data type (e.g. "username" or "secret").
* @param[in] value The value to set or NULL to remove data entry.
*
* @return 0 on success, -1 on error, 1 credential not found, 99 permission
* denied.
*/
static int
set_credential_data (credential_t credential,
const char* type,
const char* value)
{
gchar *quoted_type;
if (current_credentials.uuid
&& (acl_user_may ("modify_credential") == 0))
return 99;
if (type == NULL)
return -1;
if (credential == 0)
return 1;
quoted_type = sql_quote (type);
if (sql_int ("SELECT count (*) FROM credentials_data"
" WHERE credential = '%llu' AND type = '%s';",
credential, quoted_type))
{
if (value == NULL)
{
sql ("DELETE FROM credentials_data"
" WHERE credential = '%llu' AND type = '%s';",
credential, quoted_type);
}
else
{
gchar *quoted_value;
quoted_value = sql_quote (value);
sql ("UPDATE credentials_data SET value = '%s'"
" WHERE credential = %llu AND type = '%s';",
quoted_value, credential, quoted_type);
g_free (quoted_value);
}
}
else if (value != NULL)
{
gchar *quoted_value;
quoted_value = sql_quote (value);
sql ("INSERT INTO credentials_data (credential, type, value)"
" VALUES (%llu, '%s', '%s')",
credential, quoted_type, quoted_value);
g_free (quoted_value);
}
g_free (quoted_type);
return 0;
}
/**
* @brief Test if a username is valid to use in a credential.
*
* Valid usernames may only contain alphanumeric characters and a few
* special ones to avoid problems with installer package generation.
*
* @param[in] username The username string to test.
*
* @return Whether the username is valid.
*/
static int
validate_credential_username (const gchar *username)
{
const char *s;
s = username;
while (*s)
if (isalnum (*s)
|| strchr ("-_\\.@", *s))
s++;
else
return 0;
return 1;
}
/**
* @brief Test if a username is valid for a credential export format.
*
* @param[in] username The username string to test.
* @param[in] format The credential format to validate for.
*
* @return Whether the username is valid.
*/
static gboolean
validate_credential_username_for_format (const gchar *username,
credential_format_t format)
{
const char *s, *name_characters;
name_characters = NULL;
switch (format)
{
case CREDENTIAL_FORMAT_NONE:
case CREDENTIAL_FORMAT_KEY:
case CREDENTIAL_FORMAT_PEM:
// No validation required
break;
case CREDENTIAL_FORMAT_RPM:
case CREDENTIAL_FORMAT_DEB:
name_characters = "-_";
break;
case CREDENTIAL_FORMAT_EXE:
name_characters = "-_\\.@";
break;
case CREDENTIAL_FORMAT_ERROR:
return 0;
}
if (name_characters)
{
s = username;
while (*s)
if (isalnum (*s)
|| strchr (name_characters, *s))
s++;
else
return FALSE;
}
return TRUE;
}
/**
* @brief Length of password generated in create_credential.
*/
#define PASSWORD_LENGTH 10
/**
* @brief Create a Credential.
*
* @param[in] name Name of LSC credential. Must be at least one
* character long.
* @param[in] comment Comment on LSC credential.
* @param[in] login Name of LSC credential user. Must be at least
* one character long.
* @param[in] given_password Password for password-only credential, NULL to
* generate credentials.
* @param[in] key_private Private key, or NULL.
* @param[in] key_public Public key, or NULL.
* @param[in] certificate Certificate, or NULL.
* @param[in] community SNMP community string, or NULL.
* @param[in] auth_algorithm SNMP authentication algorithm, or NULL.
* @param[in] privacy_password SNMP privacy password.
* @param[in] privacy_algorithm SNMP privacy algorithm.
* @param[in] given_type Credential type or NULL.
* @param[in] allow_insecure Whether to allow insecure uses.
* @param[out] credential Created Credential.
*
* @return 0 success, 1 LSC credential exists already, 2 invalid username,
* 3 Failed to create public key from private key/password,
* 4 Invalid credential type, 5 login username missing,
* 6 password missing, 7 private key missing, 8 certificate missing,
* 9 public key missing, 10 autogenerate not supported,
* 11 community missing, 12 auth algorithm missing,
* 14 privacy algorithm missing,
* 15 invalid auth algorithm, 16 invalid privacy algorithm,
* 17 invalid certificate, 99 permission denied, -1 error.
*/
int
create_credential (const char* name, const char* comment, const char* login,
const char* given_password,
const char* key_private, const char* key_public,
const char* certificate, const char* community,
const char* auth_algorithm, const char* privacy_password,
const char* privacy_algorithm,
const char* given_type, const char* allow_insecure,
credential_t *credential)
{
gchar *quoted_name, *quoted_comment, *quoted_type;
int i;
GRand *rand;
gchar generated_password[PASSWORD_LENGTH];
gchar *generated_private_key;
credential_t new_credential;
int auto_generate, allow_insecure_int;
int using_snmp_v3;
int ret;
assert (name && strlen (name) > 0);
assert (current_credentials.uuid);
assert (comment);
sql_begin_immediate ();
if (acl_user_may ("create_credential") == 0)
{
sql_rollback ();
return 99;
}
if (resource_with_name_exists (name, "credential", 0))
{
sql_rollback ();
return 1;
}
if (allow_insecure
&& strcmp (allow_insecure, "")
&& strcmp (allow_insecure, "0"))
allow_insecure_int = 1;
else
allow_insecure_int = 0;
if (given_type && strcmp (given_type, ""))
{
if (strcmp (given_type, "cc")
&& strcmp (given_type, "pgp")
&& strcmp (given_type, "pw")
&& strcmp (given_type, "snmp")
&& strcmp (given_type, "smime")
&& strcmp (given_type, "up")
&& strcmp (given_type, "usk"))
{
sql_rollback ();
return 4;
}
else
quoted_type = g_strdup (given_type);
}
else if (community
|| (login && given_password && auth_algorithm))
quoted_type = g_strdup ("snmp");
else if (certificate && key_private)
quoted_type = g_strdup ("cc");
else if (login && key_private)
quoted_type = g_strdup ("usk");
else if (login && given_password)
quoted_type = g_strdup ("up");
else if (login && key_private == NULL && given_password == NULL)
quoted_type = g_strdup ("usk"); /* auto-generate */
else
{
g_warning ("%s: Cannot determine type of new credential", __func__);
return -1;
}
/* Validate credential data */
auto_generate = ((given_password == NULL) && (key_private == NULL)
&& (key_public == NULL) && (certificate == NULL)
&& (community == NULL));
ret = 0;
if (auto_generate
&& (strcmp (quoted_type, "cc") == 0
|| strcmp (quoted_type, "pgp") == 0
|| strcmp (quoted_type, "smime") == 0
|| strcmp (quoted_type, "snmp") == 0))
ret = 10; // Type does not support autogenerate
using_snmp_v3 = 0;
if (login == NULL
&& strcmp (quoted_type, "cc")
&& strcmp (quoted_type, "pgp")
&& strcmp (quoted_type, "pw")
&& strcmp (quoted_type, "smime")
&& strcmp (quoted_type, "snmp"))
ret = 5;
else if (given_password == NULL && auto_generate == 0
&& (strcmp (quoted_type, "up") == 0
|| strcmp (quoted_type, "pw") == 0))
// (username) password requires a password
ret = 6;
else if (key_private == NULL && auto_generate == 0
&& (strcmp (quoted_type, "cc") == 0
|| strcmp (quoted_type, "usk") == 0))
ret = 7;
else if (certificate == NULL && auto_generate == 0
&& (strcmp (quoted_type, "cc") == 0
|| strcmp (quoted_type, "smime") == 0))
ret = 8;
else if (key_public == NULL && auto_generate == 0
&& strcmp (quoted_type, "pgp") == 0)
ret = 9;
else if (strcmp (quoted_type, "snmp") == 0)
{
if (login || given_password || auth_algorithm
|| privacy_password || privacy_algorithm)
using_snmp_v3 = 1;
if (community == NULL)
{
if (login == NULL || given_password == NULL)
ret = 11;
}
else if (auth_algorithm == NULL && using_snmp_v3)
ret = 12;
else if ((privacy_algorithm == NULL
|| strcmp (privacy_algorithm, "") == 0)
&& privacy_password
&& strcmp (privacy_password, ""))
ret = 14;
else if (auth_algorithm
&& strcmp (auth_algorithm, "md5")
&& strcmp (auth_algorithm, "sha1"))
ret = 15;
else if (privacy_algorithm
&& strcmp (privacy_algorithm, "")
&& strcmp (privacy_algorithm, "aes")
&& strcmp (privacy_algorithm, "des"))
ret = 16;
}
if (ret)
{
g_free (quoted_type);
sql_rollback ();
return ret;
}
/* Create base credential */
quoted_name = sql_quote (name);
quoted_comment = sql_quote (comment ? comment : "");
sql ("INSERT INTO credentials"
" (uuid, name, owner, comment, creation_time, modification_time,"
" type, allow_insecure)"
" VALUES"
" (make_uuid (), '%s',"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', m_now (), m_now (), '%s', %d);",
quoted_name,
current_credentials.uuid,
quoted_comment,
quoted_type,
allow_insecure_int);
g_free (quoted_name);
g_free (quoted_comment);
new_credential = sql_last_insert_id ();
/* Add non-secret data */
if (login)
{
/*
* Ensure the login does not contain characters that cause problems
* with package generation.
*/
if (validate_credential_username (login) == 0)
{
sql_rollback ();
return 2;
}
set_credential_data (new_credential,
"username", login);
}
if (key_public)
set_credential_data (new_credential, "public_key", key_public);
if (certificate)
{
gchar *certificate_truncated;
certificate_truncated = truncate_certificate (certificate);
if (certificate_truncated)
set_credential_data (new_credential,
"certificate", certificate_truncated);
else
{
sql_rollback();
return 17;
}
g_free (certificate_truncated);
}
if (auth_algorithm)
set_credential_data (new_credential,
"auth_algorithm", auth_algorithm);
if (privacy_algorithm)
set_credential_data (new_credential,
"privacy_algorithm", privacy_algorithm);
g_free (quoted_type);
/* Add secret data like passwords and private keys */
/* Private key with optional passphrase */
if (key_private)
{
lsc_crypt_ctx_t crypt_ctx;
gchar *key_private_truncated, *generated_key_public;
/* Try truncate the private key, but if that fails try get the public
* key anyway, in case it's a key type that truncate_private_key does
* not understand. */
if (key_private)
key_private_truncated = truncate_private_key (key_private);
else
return 3;
generated_key_public = gvm_ssh_public_from_private
(key_private_truncated
? key_private_truncated
: key_private,
given_password);
if (generated_key_public == NULL)
{
g_free (key_private_truncated);
return 3;
}
g_free (generated_key_public);
/* Encrypt password and private key. Note that we do not need
to call sql_quote because the result of the encryption is
base64 encoded and does not contain apostrophes. */
if (!disable_encrypted_credentials)
{
gchar *secret;
crypt_ctx = lsc_crypt_new ();
secret = lsc_crypt_encrypt (crypt_ctx,
"password", given_password,
"private_key", key_private, NULL);
if (!secret)
{
lsc_crypt_release (crypt_ctx);
sql_rollback ();
return -1;
}
set_credential_data (new_credential, "secret", secret);
g_free (secret);
}
else
{
crypt_ctx = NULL;
set_credential_data (new_credential, "password", given_password);
set_credential_data (new_credential, "private_key", key_private);
}
lsc_crypt_release (crypt_ctx);
if (credential)
*credential = new_credential;
sql_commit ();
return 0;
}
/* SNMP passwords */
if (community || using_snmp_v3)
{
lsc_crypt_ctx_t crypt_ctx;
/* Encrypt passwords. Note that we do not need
to call sql_quote because the result of the encryption is
base64 encoded and does not contain apostrophes. */
if (!disable_encrypted_credentials)
{
gchar *secret;
crypt_ctx = lsc_crypt_new ();
secret = lsc_crypt_encrypt (crypt_ctx,
"community", community,
"password", given_password,
"privacy_password",
privacy_password ? privacy_password : "",
NULL);
if (!secret)
{
lsc_crypt_release (crypt_ctx);
sql_rollback ();
return -1;
}
set_credential_data (new_credential, "secret", secret);
g_free (secret);
}
else
{
crypt_ctx = NULL;
set_credential_data (new_credential, "community", community);
set_credential_data (new_credential, "password", given_password);
set_credential_data (new_credential, "privacy_password",
privacy_password ? privacy_password : "");
}
lsc_crypt_release (crypt_ctx);
if (credential)
*credential = new_credential;
sql_commit ();
return 0;
}
/* Password only */
if (given_password)
{
lsc_crypt_ctx_t crypt_ctx;
/* Password-only credential. */
if (!disable_encrypted_credentials)
{
crypt_ctx = lsc_crypt_new ();
gchar *secret = lsc_crypt_encrypt (crypt_ctx,
"password", given_password,
NULL);
if (!secret)
{
lsc_crypt_release (crypt_ctx);
sql_rollback ();
return -1;
}
set_credential_data (new_credential, "secret", secret);
g_free (secret);
}
else
{
crypt_ctx = NULL;
set_credential_data (new_credential, "password", given_password);
}
if (credential)
*credential = new_credential;
sql_commit ();
return 0;
}
/*
* Auto-generate credential
*/
/* Create the keys and packages. */
rand = g_rand_new ();
for (i = 0; i < PASSWORD_LENGTH - 1; i++)
{
generated_password[i] = (gchar) g_rand_int_range (rand, '0', 'z');
if (generated_password[i] == '\\')
generated_password[i] = '{';
}
generated_password[PASSWORD_LENGTH - 1] = '\0';
g_rand_free (rand);
if (given_type == NULL || strcmp (given_type, "usk") == 0)
{
if (lsc_user_keys_create (generated_password, &generated_private_key))
{
sql_rollback ();
return -1;
}
}
else
generated_private_key = NULL;
{
lsc_crypt_ctx_t crypt_ctx;
/* Generated key credential. */
if (!disable_encrypted_credentials)
{
gchar *secret;
crypt_ctx = lsc_crypt_new ();
if (generated_private_key)
secret = lsc_crypt_encrypt (crypt_ctx,
"password", generated_password,
"private_key", generated_private_key,
NULL);
else
secret = lsc_crypt_encrypt (crypt_ctx,
"password", generated_password,
NULL);
if (!secret)
{
lsc_crypt_release (crypt_ctx);
sql_rollback ();
return -1;
}
set_credential_data (new_credential, "secret", secret);
g_free (secret);
}
else
{
set_credential_data (new_credential, "password", generated_password);
if (generated_private_key)
set_credential_data (new_credential,
"private_key", generated_private_key);
crypt_ctx = NULL;
}
lsc_crypt_release (crypt_ctx);
g_free (generated_private_key);
}
if (credential)
*credential = new_credential;
sql_commit ();
return 0;
}
/**
* @brief Create an LSC Credential from an existing one.
*
* @param[in] name Name of new Credential. NULL to copy
* from existing.
* @param[in] comment Comment on new Credential. NULL to copy
* from existing.
* @param[in] credential_id UUID of existing Credential.
* @param[out] new_credential New Credential.
*
* @return 0 success, 1 Credential exists already, 2 failed to find
* existing Credential, -1 error.
*/
int
copy_credential (const char* name, const char* comment,
const char *credential_id,
credential_t* new_credential)
{
int ret;
credential_t credential;
assert (new_credential);
ret = copy_resource ("credential", name, comment, credential_id,
"type", 1, new_credential, &credential);
if (ret)
return ret;
sql ("INSERT INTO credentials_data (credential, type, value)"
" SELECT %llu, type, value FROM credentials_data"
" WHERE credential = %llu;",
*new_credential, credential);
return 0;
}
/**
* @brief Modify a Credential.
*
* @param[in] credential_id UUID of Credential.
* @param[in] name Name of Credential.
* @param[in] comment Comment on Credential.
* @param[in] login Login of Credential.
* @param[in] password Password or passphrase of Credential.
* @param[in] key_private Private key of Credential.
* @param[in] key_public Public key of Credential.
* @param[in] certificate Certificate of Credential.
* @param[in] community SNMP Community of Credential.
* @param[in] auth_algorithm Authentication algorithm of Credential.
* @param[in] privacy_password Privacy password of Credential.
* @param[in] privacy_algorithm Privacy algorithm of Credential.
* @param[in] allow_insecure Whether to allow insecure use.
*
* @return 0 success, 1 failed to find credential, 2 credential with new name
* exists, 3 credential_id required, 4 invalid login name,
* 5 invalid certificate, 6 invalid auth_algorithm,
* 7 invalid privacy_algorithm, 8 invalid private key,
* 9 invalid public key,
* 10 privacy password must be empty if algorithm is empty
* 99 permission denied,
* -1 internal error.
*/
int
modify_credential (const char *credential_id,
const char *name, const char *comment,
const char *login, const char* password,
const char* key_private, const char* key_public,
const char* certificate,
const char* community, const char* auth_algorithm,
const char* privacy_password, const char* privacy_algorithm,
const char* allow_insecure)
{
credential_t credential;
iterator_t iterator;
int ret;
if (credential_id == NULL)
return 3;
sql_begin_immediate ();
assert (current_credentials.uuid);
if (acl_user_may ("modify_credential") == 0)
{
sql_rollback ();
return 99;
}
credential = 0;
if (find_credential_with_permission (credential_id, &credential,
"modify_credential"))
{
sql_rollback ();
return -1;
}
if (credential == 0)
{
sql_rollback ();
return 1;
}
/* Check whether a credential with the same name exists already. */
if (name)
{
if (resource_with_name_exists (name, "credential", credential))
{
sql_rollback ();
return 2;
}
}
/* Update values */
if (name)
set_credential_name (credential, name);
if (comment)
set_credential_comment (credential, comment);
if (allow_insecure)
{
if (strcmp (allow_insecure, "") && strcmp (allow_insecure, "0"))
sql ("UPDATE credentials SET allow_insecure = 1 WHERE id = %llu;",
credential);
else
sql ("UPDATE credentials SET allow_insecure = 0 WHERE id = %llu;",
credential);
}
ret = 0;
if (login && ret == 0)
{
/*
* Ensure the login is not empty and does not contain characters that
* cause problems with package generation.
*/
if (strcmp (login, "") == 0
|| validate_credential_username (login) == 0)
ret = 4;
if (ret == 0)
set_credential_login (credential, login);
}
if (certificate && ret == 0)
{
// Truncate certificate which also validates it.
gchar *certificate_truncated;
certificate_truncated = truncate_certificate (certificate);
if (certificate_truncated)
{
set_credential_certificate (credential, certificate_truncated);
g_free (certificate_truncated);
}
else
ret = 5;
}
if (auth_algorithm && ret == 0)
{
if (strcmp (auth_algorithm, "md5")
&& strcmp (auth_algorithm, "sha1"))
ret = 6;
else
set_credential_auth_algorithm (credential, auth_algorithm);
}
if (privacy_algorithm && ret == 0)
{
if (strcmp (privacy_algorithm, "aes")
&& strcmp (privacy_algorithm, "des")
&& strcmp (privacy_algorithm, ""))
ret = 7;
else
{
iterator_t credential_iterator;
const char *used_password;
init_credential_iterator_one (&credential_iterator, credential);
if (privacy_password)
used_password = privacy_password;
else
{
next (&credential_iterator);
used_password
= credential_iterator_privacy_password (&credential_iterator);
if (used_password == NULL)
used_password = "";
}
// Privacy password must be empty if algorithm is empty
if (strcmp (privacy_algorithm, "") == 0
&& strcmp (used_password, ""))
ret = 10;
else
set_credential_privacy_algorithm (credential, privacy_algorithm);
cleanup_iterator (&credential_iterator);
}
}
if (key_public && ret == 0)
{
set_credential_public_key (credential, key_public);
}
if (ret)
{
sql_rollback ();
return ret;
}
init_credential_iterator_one (&iterator, credential);
if (next (&iterator))
{
gchar *key_private_truncated = NULL;
const char *key_private_to_use;
const char *type = credential_iterator_type (&iterator);
if (key_private)
{
char *generated_key_public = NULL;
/* Try truncate the private key, but if that fails try get the
* public key anyway, in case it's a key type that
* truncate_private_key does not understand. */
key_private_truncated = truncate_private_key (key_private);
key_private_to_use = key_private_truncated ? key_private_truncated
: key_private;
if (strcmp (type, "cc") == 0)
{
generated_key_public
= gvm_ssh_public_from_private
(key_private_to_use,
NULL);
}
else if (strcmp (type, "usk") == 0)
{
generated_key_public
= gvm_ssh_public_from_private
(key_private_to_use,
password
? password
: credential_iterator_password (&iterator));
}
if (generated_key_public == NULL)
{
sql_rollback ();
cleanup_iterator (&iterator);
g_free (generated_key_public);
return 8;
}
g_free (generated_key_public);
}
else
key_private_to_use = NULL;
if (strcmp (type, "cc") == 0)
{
if (key_private_to_use)
set_credential_private_key (credential,
key_private_to_use,
NULL);
}
else if (strcmp (type, "up") == 0
|| strcmp (type, "pw") == 0)
{
if (password)
set_credential_password (credential, password);
}
else if (strcmp (type, "usk") == 0)
{
if (key_private_to_use || password)
{
if (check_private_key (key_private_truncated
? key_private_to_use
: credential_iterator_private_key
(&iterator),
password
? password
: credential_iterator_password
(&iterator)))
{
sql_rollback ();
cleanup_iterator (&iterator);
return 8;
}
set_credential_private_key
(credential,
key_private_truncated
? key_private_to_use
: credential_iterator_private_key (&iterator),
password
? password
: credential_iterator_password (&iterator));
}
}
else if (strcmp (type, "snmp") == 0)
{
if (privacy_password)
{
const char *used_algorithm;
// Privacy_algorithm should be set above if given
used_algorithm
= credential_iterator_privacy_algorithm (&iterator);
if (used_algorithm == NULL)
used_algorithm = "";
// privacy password must be empty if algorithm is empty
if (strcmp (used_algorithm, "") == 0
&& strcmp (privacy_password, ""))
{
sql_rollback ();
cleanup_iterator (&iterator);
return 10;
}
}
if (community || password || privacy_password)
{
set_credential_snmp_secret
(credential,
community
? community
: credential_iterator_community (&iterator),
password
? password
: credential_iterator_password (&iterator),
privacy_password
? privacy_password
: credential_iterator_privacy_password (&iterator));
}
}
else if (strcmp (type, "pgp") == 0
|| strcmp (type, "smime") == 0)
{
set_credential_data (credential, "secret", "");
}
else
{
g_warning ("%s: Unknown credential type: %s", __func__, type);
sql_rollback ();
cleanup_iterator (&iterator);
g_free (key_private_truncated);
return -1;
}
g_free (key_private_truncated);
}
else
{
g_warning ("%s: credential iterator next() failed", __func__);
sql_rollback ();
cleanup_iterator (&iterator);
return -1;
}
cleanup_iterator (&iterator);
sql_commit ();
return 0;
}
/**
* @brief Delete a Credential.
*
* @param[in] credential_id UUID of Credential.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 fail because the credential is in use,
* 2 failed to find credential, 99 permission denied, -1 error.
*/
int
delete_credential (const char *credential_id, int ultimate)
{
credential_t credential = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_credential") == 0)
{
sql_rollback ();
return 99;
}
if (find_credential_with_permission (credential_id, &credential,
"delete_credential"))
{
sql_rollback ();
return -1;
}
if (credential == 0)
{
if (find_trash ("credential", credential_id, &credential))
{
sql_rollback ();
return -1;
}
if (credential == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
/* Check if it's in use by another resource in the trashcan. */
if (trash_credential_in_use (credential))
{
sql_rollback ();
return 1;
}
permissions_set_orphans ("credential", credential,
LOCATION_TRASH);
tags_remove_resource ("credential", credential,
LOCATION_TRASH);
sql ("DELETE FROM credentials_trash_data WHERE credential = %llu;",
credential);
sql ("DELETE FROM credentials_trash WHERE id = %llu;", credential);
sql_commit ();
return 0;
}
/* Check if it's in use by another resource. */
if (credential_in_use (credential))
{
sql_rollback ();
return 1;
}
if (ultimate == 0)
{
credential_t trash_credential;
sql ("INSERT INTO credentials_trash"
" (uuid, owner, name, comment, creation_time,"
" modification_time, type)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time, type"
" FROM credentials WHERE id = %llu;",
credential);
trash_credential = sql_last_insert_id ();
sql ("INSERT INTO credentials_trash_data"
" (credential, type, value)"
" SELECT %llu, type, value"
" FROM credentials_data"
" WHERE credential = %llu",
trash_credential, credential);
/* Update the credential references in any trashcan targets or scanners.
* This situation is possible if the user deletes the credential when
* the target or scanner is in the trashcan. */
sql ("UPDATE targets_trash_login_data"
" SET credential_location = " G_STRINGIFY (LOCATION_TRASH) ","
" credential = %llu"
" WHERE credential = %llu"
" AND credential_location = " G_STRINGIFY (LOCATION_TABLE) ";",
trash_credential,
credential);
sql ("UPDATE scanners_trash"
" SET credential_location = " G_STRINGIFY (LOCATION_TRASH) ","
" credential = %llu"
" WHERE credential = %llu"
" AND credential_location = " G_STRINGIFY (LOCATION_TABLE) ";",
trash_credential,
credential);
permissions_set_locations ("credential", credential,
trash_credential,
LOCATION_TRASH);
tags_set_locations ("credential", credential,
trash_credential,
LOCATION_TRASH);
}
else
{
permissions_set_orphans ("credential", credential, LOCATION_TABLE);
tags_remove_resource ("credential", credential, LOCATION_TABLE);
}
sql ("DELETE FROM credentials_data WHERE credential = %llu;", credential);
sql ("DELETE FROM credentials WHERE id = %llu;", credential);
sql_commit ();
return 0;
}
/**
* @brief Filter columns for LSC Credential iterator.
*/
#define CREDENTIAL_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "login", "type", "allow_insecure", NULL }
/**
* @brief LSC Credential iterator columns.
*/
#define CREDENTIAL_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (credentials), \
/* public generic data */ \
{ "type", NULL, KEYWORD_TYPE_STRING }, \
{ "allow_insecure", NULL, KEYWORD_TYPE_INTEGER }, \
/* public type specific data */ \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'username')", \
"login", \
KEYWORD_TYPE_STRING \
}, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'certificate')", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'auth_algorithm')", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'privacy_algorithm')", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'public_key')", \
NULL, \
KEYWORD_TYPE_STRING }, \
/* private data */ \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'secret')", \
"secret", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'password')", \
"password", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'private_key')", \
"private_key", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'community')", \
"community", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials.id AND type = 'privacy_password')", \
"privacy_password", \
KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief LSC Credential iterator columns for trash case.
*/
#define CREDENTIAL_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (credentials_trash), \
/* public generic data */ \
{ "type", NULL, KEYWORD_TYPE_STRING }, \
{ "allow_insecure", NULL, KEYWORD_TYPE_INTEGER }, \
/* public type specific data */ \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id AND type = 'username')", \
"login", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id AND type = 'certificate')", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id" \
" AND type = 'auth_algorithm')", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id" \
" AND type = 'privacy_algorithm')", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_data" \
" WHERE credential = credentials_trash.id AND type = 'public_key')", \
NULL, \
KEYWORD_TYPE_STRING }, \
/* private data */ \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id AND type = 'secret')", \
"secret", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id AND type = 'password')", \
"password", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id AND type = 'private_key')", \
"private_key", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id AND type = 'community')", \
"community", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT value FROM credentials_trash_data" \
" WHERE credential = credentials_trash.id" \
" AND type = 'privacy_password')", \
"privacy_password", \
KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of LSC Credentials.
*
* @param[in] get GET params.
*
* @return Total number of LSC Credentials in filtered set.
*/
int
credential_count (const get_data_t *get)
{
static const char *filter_columns[] = CREDENTIAL_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = CREDENTIAL_ITERATOR_COLUMNS;
static column_t trash_columns[] = CREDENTIAL_ITERATOR_TRASH_COLUMNS;
return count ("credential", get, columns, trash_columns, filter_columns,
0, 0, 0, TRUE);
}
/**
* @brief Check whether a Credential is in use.
*
* @param[in] credential Credential.
*
* @return 1 yes, 0 no.
*/
int
credential_in_use (credential_t credential)
{
int ret;
char *uuid = credential_uuid (credential);
ret = !!(sql_int ("SELECT count (*) FROM targets_login_data"
" WHERE credential = %llu;",
credential)
|| sql_int ("SELECT count (*) FROM scanners"
" WHERE credential = %llu;",
credential)
|| sql_int ("SELECT count (*) FROM alert_method_data"
" WHERE (name = 'recipient_credential'"
" OR name = 'scp_credential'"
" OR name = 'smb_credential'"
" OR name = 'tp_sms_credential'"
" OR name = 'verinice_server_credential'"
" OR name = 'pkcs12_credential')"
" AND data = '%s'",
uuid));
free (uuid);
return ret;
}
/**
* @brief Check whether a trashcan Credential is in use.
*
* @param[in] credential Credential.
*
* @return 1 yes, 0 no.
*/
int
trash_credential_in_use (credential_t credential)
{
int ret;
char *uuid = trash_credential_uuid (credential);
ret = !!(sql_int ("SELECT count (*) FROM targets_trash_login_data"
" WHERE credential = %llu"
" AND credential_location"
" = " G_STRINGIFY (LOCATION_TRASH) ";",
credential)
|| sql_int ("SELECT count (*) FROM scanners_trash"
" WHERE credential = %llu"
" AND credential_location"
" = " G_STRINGIFY (LOCATION_TRASH) ";",
credential)
|| sql_int ("SELECT count (*) FROM alert_method_data_trash"
" WHERE (name = 'recipient_credential'"
" OR name = 'scp_credential'"
" OR name = 'smb_credential'"
" OR name = 'tp_sms_credential'"
" OR name = 'verinice_server_credential'"
" OR name = 'pkcs12_credential')"
" AND data = '%s'",
uuid));
free (uuid);
return ret;
}
/**
* @brief Check whether a Credential is writable.
*
* @param[in] credential Credential.
*
* @return 1 yes, 0 no.
*/
int
credential_writable (credential_t credential)
{
return 1;
}
/**
* @brief Check whether a trashcan Credential is writable.
*
* @param[in] credential Credential.
*
* @return 1 yes, 0 no.
*/
int
trash_credential_writable (credential_t credential)
{
return 1;
}
/**
* @brief Get a value from a credential.
*
* @param[in] credential The Credential.
* @param[in] value_name Name of the value.
*
* @return Value.
*/
gchar *
credential_value (credential_t credential, const char* value_name)
{
if (credential == 0)
return NULL;
return sql_string ("SELECT value FROM credentials_data"
" WHERE credential = %llu"
" AND type = '%s';",
credential, value_name);
}
/**
* @brief Get a possibly encrypted credential value in decrypted form.
*
* @param[in] credential The Credential.
* @param[in] value_name Name of the value.
*
* @return Value.
*/
gchar *
credential_encrypted_value (credential_t credential, const char* value_name)
{
gchar *value;
value = sql_string ("SELECT value FROM credentials_data"
" WHERE credential = %llu"
" AND type = '%s';",
credential, value_name);
if (value == NULL)
{
gchar *secret;
const char* decrypted;
lsc_crypt_ctx_t crypt_ctx;
crypt_ctx = lsc_crypt_new ();
secret = sql_string ("SELECT value FROM credentials_data"
" WHERE credential = %llu"
" AND type = 'secret';",
credential);
decrypted = lsc_crypt_decrypt (crypt_ctx, secret, value_name);
if (decrypted)
value = g_strdup (decrypted);
lsc_crypt_release (crypt_ctx);
g_free (secret);
}
return value;
}
/**
* @brief Set the name of a Credential.
*
* @param[in] credential The Credential.
* @param[in] name Name.
*/
static void
set_credential_name (credential_t credential, const char *name)
{
gchar *quoted_name = sql_quote (name);
sql ("UPDATE credentials SET name = '%s', modification_time = m_now ()"
" WHERE id = %llu;",
quoted_name,
credential);
g_free (quoted_name);
}
/**
* @brief Set the comment of a Credential.
*
* @param[in] credential The Credential.
* @param[in] comment Comment.
*/
static void
set_credential_comment (credential_t credential,
const char *comment)
{
gchar *quoted_comment = sql_quote (comment);
sql ("UPDATE credentials SET comment = '%s', modification_time = m_now ()"
" WHERE id = %llu;",
quoted_comment,
credential);
g_free (quoted_comment);
}
/**
* @brief Set the login of a Credential.
*
* @param[in] credential The Credential.
* @param[in] login Login.
*/
static void
set_credential_login (credential_t credential, const char *login)
{
set_credential_data (credential, "username", login);
sql ("UPDATE credentials SET"
" modification_time = m_now ()"
" WHERE id = %llu;",
credential);
}
/**
* @brief Set the certificate of a Credential.
*
* @param[in] credential The Credential.
* @param[in] certificate Certificate.
*/
static void
set_credential_certificate (credential_t credential, const char *certificate)
{
set_credential_data (credential, "certificate", certificate);
sql ("UPDATE credentials SET"
" modification_time = m_now ()"
" WHERE id = %llu;",
credential);
}
/**
* @brief Set the auth_algorithm of a Credential.
*
* @param[in] credential The Credential.
* @param[in] algorithm Authentication algorithm.
*/
static void
set_credential_auth_algorithm (credential_t credential, const char *algorithm)
{
set_credential_data (credential, "auth_algorithm", algorithm);
sql ("UPDATE credentials SET"
" modification_time = m_now ()"
" WHERE id = %llu;",
credential);
}
/**
* @brief Set the privacy_algorithm of a Credential.
*
* @param[in] credential The Credential.
* @param[in] algorithm Privacy algorithm.
*/
void
set_credential_privacy_algorithm (credential_t credential,
const char *algorithm)
{
set_credential_data (credential, "privacy_algorithm", algorithm);
sql ("UPDATE credentials SET"
" modification_time = m_now ()"
" WHERE id = %llu;",
credential);
}
/**
* @brief Set the password of a Credential.
*
* @param[in] credential The Credential.
* @param[in] password Password.
*/
static void
set_credential_password (credential_t credential, const char *password)
{
lsc_crypt_ctx_t crypt_ctx;
if (!disable_encrypted_credentials)
{
gchar *encrypted_blob;
crypt_ctx = lsc_crypt_new ();
encrypted_blob = lsc_crypt_encrypt (crypt_ctx,
"password", password, NULL);
if (!encrypted_blob)
{
g_critical ("%s: encryption failed", G_STRFUNC);
lsc_crypt_release (crypt_ctx);
return;
}
set_credential_data (credential, "secret", encrypted_blob);
set_credential_data (credential, "password", NULL);
set_credential_data (credential, "private_key", NULL);
g_free (encrypted_blob);
}
else
{
crypt_ctx = NULL;
set_credential_data (credential, "secret", NULL);
set_credential_data (credential, "password", password);
set_credential_data (credential, "private_key", NULL);
}
sql ("UPDATE credentials SET modification_time = m_now ()"
" WHERE id = %llu;",
credential);
lsc_crypt_release (crypt_ctx);
}
/**
* @brief Set the private key and passphrase of a Credential.
*
* @param[in] credential The Credential.
* @param[in] private_key Private key.
* @param[in] passphrase Passphrase.
*/
static void
set_credential_private_key (credential_t credential,
const char *private_key, const char *passphrase)
{
lsc_crypt_ctx_t crypt_ctx;
if (!disable_encrypted_credentials)
{
gchar *encrypted_blob;
crypt_ctx = lsc_crypt_new ();
encrypted_blob = lsc_crypt_encrypt (crypt_ctx,
"private_key", private_key,
"password", passphrase,
NULL);
if (!encrypted_blob)
{
g_critical ("%s: encryption failed", G_STRFUNC);
lsc_crypt_release (crypt_ctx);
return;
}
set_credential_data (credential, "secret", encrypted_blob);
set_credential_data (credential, "password", NULL);
set_credential_data (credential, "private_key", NULL);
g_free (encrypted_blob);
}
else
{
crypt_ctx = NULL;
set_credential_data (credential, "secret", NULL);
set_credential_data (credential, "password", passphrase);
set_credential_data (credential, "private_key", private_key);
}
sql ("UPDATE credentials SET modification_time = m_now ()"
" WHERE id = %llu;",
credential);
lsc_crypt_release (crypt_ctx);
}
/**
* @brief Set the public key of a Credential.
*
* @param[in] credential The Credential.
* @param[in] public_key Public key.
*/
void
set_credential_public_key (credential_t credential, const char *public_key)
{
set_credential_data (credential, "public_key", public_key);
sql ("UPDATE credentials SET"
" modification_time = m_now ()"
" WHERE id = %llu;",
credential);
}
/**
* @brief Set the community, password and privacy password of a Credential.
*
* @param[in] credential The Credential.
* @param[in] community SNMP community.
* @param[in] password Authentication password.
* @param[in] privacy_password Privacy password.
*/
static void
set_credential_snmp_secret (credential_t credential, const char* community,
const char *password, const char *privacy_password)
{
lsc_crypt_ctx_t crypt_ctx;
if (!disable_encrypted_credentials)
{
gchar *encrypted_blob;
crypt_ctx = lsc_crypt_new ();
encrypted_blob = lsc_crypt_encrypt (crypt_ctx,
"community", community,
"password", password,
"privacy_password", privacy_password,
NULL);
if (!encrypted_blob)
{
g_critical ("%s: encryption failed", G_STRFUNC);
lsc_crypt_release (crypt_ctx);
return;
}
set_credential_data (credential, "secret", encrypted_blob);
set_credential_data (credential, "community", NULL);
set_credential_data (credential, "password", NULL);
set_credential_data (credential, "privacy_password", NULL);
g_free (encrypted_blob);
}
else
{
crypt_ctx = NULL;
set_credential_data (credential, "secret", NULL);
set_credential_data (credential, "community", community);
set_credential_data (credential, "password", password);
set_credential_data (credential, "privacy_password", privacy_password);
}
sql ("UPDATE credentials SET modification_time = m_now ()"
" WHERE id = %llu;",
credential);
lsc_crypt_release (crypt_ctx);
}
/**
* @brief Initialise a Credential iterator, given a single Credential.
*
* @param[in] iterator Iterator.
* @param[in] credential Single Credential to iterate.
*/
void
init_credential_iterator_one (iterator_t* iterator,
credential_t credential)
{
get_data_t get;
assert (credential);
memset (&get, '\0', sizeof (get));
get.id = credential_uuid (credential);
get.filter = "owner=any permission=get_credentials";
/* We could pass the return up to the caller, but we don't pass in
* a filter id and the callers are all in situations where the
* credential cannot disappear, so it's safe to ignore the return. */
init_credential_iterator (iterator, &get);
}
/**
* @brief Initialise a Credential iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find filter, 2 failed to find
* filter (filt_id), -1 error.
*/
int
init_credential_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = CREDENTIAL_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = CREDENTIAL_ITERATOR_COLUMNS;
static column_t trash_columns[] = CREDENTIAL_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"credential",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Get possibly encrypted data from credentials.
*
* @param[in] iterator Iterator.
* @param[in] type Type of data.
*
* @return Data.
*/
static const char*
credential_iterator_encrypted_data (iterator_t* iterator, const char* type)
{
const char *secret, *unencrypted;
if (iterator->done)
return NULL;
secret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
if (type == NULL)
{
g_warning ("%s: NULL data type given", __func__);
return NULL;
}
else if (strcmp (type, "password") == 0)
unencrypted = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
else if (strcmp (type, "private_key") == 0)
unencrypted = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
else if (strcmp (type, "community") == 0)
unencrypted = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 10);
else if (strcmp (type, "privacy_password") == 0)
unencrypted = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
else
{
g_warning ("%s: unknown data type \"%s\"", __func__, type);
return NULL;
}
/* If we do not have a private key, there is no encrypted data.
Return the password as is or NULL. */
if (secret)
{
/* This is an encrypted credential. */
if (!iterator->crypt_ctx)
iterator->crypt_ctx = lsc_crypt_new ();
return lsc_crypt_decrypt (iterator->crypt_ctx, secret, type);
}
else
{
return unencrypted;
}
}
/**
* @brief Get the login from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Login, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_iterator_type, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the login from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Login, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
int credential_iterator_allow_insecure (iterator_t* iterator)
{
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
}
/**
* @brief Get the credential type abbreviation from an LSC credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Credential type, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_iterator_login, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get the certificate from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Certificate, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_iterator_certificate, GET_ITERATOR_COLUMN_COUNT + 3);
/**
* @brief Get the authentication algorithm from an LSC credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Auth algorithm, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_iterator_auth_algorithm, GET_ITERATOR_COLUMN_COUNT + 4);
/**
* @brief Get the authentication algorithm from an LSC credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Auth algorithm, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_iterator_privacy_algorithm,
GET_ITERATOR_COLUMN_COUNT + 5);
/**
* @brief Get the public key from an LSC credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Public key, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_iterator_public_key,
GET_ITERATOR_COLUMN_COUNT + 6);
/**
* @brief Get the password from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Password, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
const char*
credential_iterator_password (iterator_t* iterator)
{
return credential_iterator_encrypted_data (iterator, "password");
}
/**
* @brief Get the private_key from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Private_key, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
const char*
credential_iterator_private_key (iterator_t* iterator)
{
return credential_iterator_encrypted_data (iterator, "private_key");
}
/**
* @brief Get the SNMP community from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return SNMP community, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
const char*
credential_iterator_community (iterator_t* iterator)
{
return credential_iterator_encrypted_data (iterator, "community");
}
/**
* @brief Get the privacy password from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return SNMP community, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
const char*
credential_iterator_privacy_password (iterator_t* iterator)
{
return credential_iterator_encrypted_data (iterator, "privacy_password");
}
/**
* @brief Get the rpm from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Rpm, or NULL if iteration is complete. Free with g_free().
*/
char*
credential_iterator_rpm (iterator_t *iterator)
{
const char *private_key, *login, *pass;
void *rpm;
char *public_key;
gsize rpm_size;
gchar *rpm64;
if (iterator->done) return NULL;
private_key = credential_iterator_private_key (iterator);
pass = credential_iterator_password (iterator);
public_key = gvm_ssh_public_from_private (private_key, pass);
if (!public_key)
return NULL;
login = credential_iterator_login (iterator);
if (credential_iterator_format_available
(iterator, CREDENTIAL_FORMAT_RPM) == FALSE)
{
g_free (public_key);
return NULL;
}
else if (lsc_user_rpm_recreate (login, public_key, &rpm, &rpm_size))
{
g_warning ("%s: Failed to create RPM", __func__);
g_free (public_key);
return NULL;
}
g_free (public_key);
rpm64 = (rpm && rpm_size)
? g_base64_encode (rpm, rpm_size)
: g_strdup ("");
free (rpm);
return rpm64;
}
/**
* @brief Get the deb from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Deb, or NULL if iteration is complete. Free with g_free().
*/
char*
credential_iterator_deb (iterator_t *iterator)
{
const char *login, *private_key, *pass;
char *public_key, *maintainer;
void *deb;
gsize deb_size;
gchar *deb64;
if (iterator->done) return NULL;
maintainer = NULL;
setting_value (SETTING_UUID_LSC_DEB_MAINTAINER, &maintainer);
private_key = credential_iterator_private_key (iterator);
pass = credential_iterator_password (iterator);
public_key = gvm_ssh_public_from_private (private_key, pass);
if (!public_key)
return NULL;
login = credential_iterator_login (iterator);
if (credential_iterator_format_available
(iterator, CREDENTIAL_FORMAT_DEB) == FALSE)
{
g_free (public_key);
free (maintainer);
return NULL;
}
else if (lsc_user_deb_recreate (login, public_key,
maintainer ? maintainer : "",
&deb, &deb_size))
{
g_warning ("%s: Failed to create DEB", __func__);
g_free (public_key);
free (maintainer);
return NULL;
}
g_free (public_key);
free (maintainer);
deb64 = (deb && deb_size)
? g_base64_encode (deb, deb_size)
: g_strdup ("");
free (deb);
return deb64;
}
/**
* @brief Get the exe from a Credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Exe, or NULL if iteration is complete. Free with g_free().
*/
char*
credential_iterator_exe (iterator_t *iterator)
{
const char *login, *password;
void *exe;
gsize exe_size;
gchar *exe64;
if (iterator->done) return NULL;
login = credential_iterator_login (iterator);
password = credential_iterator_password (iterator);
if (credential_iterator_format_available
(iterator, CREDENTIAL_FORMAT_EXE) == FALSE)
return NULL;
else if (lsc_user_exe_recreate (login, password, &exe, &exe_size))
{
g_warning ("%s: Failed to create EXE", __func__);
return NULL;
}
exe64 = (exe && exe_size)
? g_base64_encode (exe, exe_size)
: g_strdup ("");
free (exe);
return exe64;
}
/**
* @brief Test if a credential format is available for an iterator.
*
* @param[in] iterator Iterator.
* @param[in] format The format to test availability of.
*
* @return Whether format is available for the current credential of iterator.
*/
gboolean
credential_iterator_format_available (iterator_t* iterator,
credential_format_t format)
{
const char *type, *login, *private_key;
type = credential_iterator_type (iterator);
login = credential_iterator_login (iterator);
private_key = credential_iterator_private_key (iterator);
switch (format)
{
case CREDENTIAL_FORMAT_NONE:
return TRUE;
case CREDENTIAL_FORMAT_KEY:
case CREDENTIAL_FORMAT_RPM:
case CREDENTIAL_FORMAT_DEB:
if (strcasecmp (type, "usk") == 0 && private_key)
return validate_credential_username_for_format (login, format);
else
return FALSE;
case CREDENTIAL_FORMAT_EXE:
if (strcasecmp (type, "up") == 0)
return validate_credential_username_for_format (login, format);
else
return FALSE;
case CREDENTIAL_FORMAT_PEM:
if (strcasecmp (type, "cc") == 0)
return validate_credential_username_for_format (login, format);
else
return FALSE;
case CREDENTIAL_FORMAT_ERROR:
return FALSE;
}
return FALSE;
}
/**
* @brief Get XML of available formats for a credential iterator.
*
* @param[in] iterator Iterator.
*
* @return Newly allocated XML string.
*/
gchar *
credential_iterator_formats_xml (iterator_t* iterator)
{
GString *xml;
xml = g_string_new ("");
if (credential_iterator_format_available (iterator,
CREDENTIAL_FORMAT_KEY))
g_string_append (xml, "key");
if (credential_iterator_format_available (iterator,
CREDENTIAL_FORMAT_RPM))
g_string_append (xml, "rpm");
if (credential_iterator_format_available (iterator,
CREDENTIAL_FORMAT_DEB))
g_string_append (xml, "deb");
if (credential_iterator_format_available (iterator,
CREDENTIAL_FORMAT_EXE))
g_string_append (xml, "exe");
if (credential_iterator_format_available (iterator,
CREDENTIAL_FORMAT_PEM))
g_string_append (xml, "pem");
g_string_append (xml, "");
return g_string_free (xml, FALSE);
}
/**
* @brief Get the UUID of a Credential.
*
* @param[in] credential Credential.
*
* @return UUID.
*/
char*
credential_uuid (credential_t credential)
{
return sql_string ("SELECT uuid FROM credentials WHERE id = %llu;",
credential);
}
/**
* @brief Get the UUID of a Credential in the trashcan.
*
* @param[in] credential Credential.
*
* @return UUID.
*/
char*
trash_credential_uuid (credential_t credential)
{
return sql_string ("SELECT uuid FROM credentials_trash"
" WHERE id = %llu;",
credential);
}
/**
* @brief Get the name of an LSC credential.
*
* @param[in] credential Credential.
*
* @return Name.
*/
char*
credential_name (credential_t credential)
{
return sql_string ("SELECT name FROM credentials WHERE id = %llu;",
credential);
}
/**
* @brief Get the name of an LSC credential in the trashcan.
*
* @param[in] credential Credential.
*
* @return Name.
*/
char*
trash_credential_name (credential_t credential)
{
return sql_string ("SELECT name FROM credentials_trash"
" WHERE id = %llu;",
credential);
}
/**
* @brief Get the type of a Credential.
*
* @param[in] credential Credential.
*
* @return Credential type.
*/
char*
credential_type (credential_t credential)
{
return sql_string ("SELECT type FROM credentials WHERE id = %llu;",
credential);
}
/**
* @brief Return whether a trashcan credential is readable.
*
* @param[in] credential Credential.
*
* @return 1 if readable, else 0.
*/
int
trash_credential_readable (credential_t credential)
{
char *uuid;
credential_t found = 0;
if (credential == 0)
return 0;
uuid = credential_uuid (credential);
if (find_trash ("credential", uuid, &found))
{
g_free (uuid);
return 0;
}
g_free (uuid);
return found > 0;
}
/**
* @brief Initialise a Credential target iterator.
*
* Iterates over all targets that use the credential.
*
* @param[in] iterator Iterator.
* @param[in] credential Name of credential.
* @param[in] ascending Whether to sort ascending or descending.
*/
void
init_credential_target_iterator (iterator_t* iterator,
credential_t credential,
int ascending)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (credential);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_targets"));
available = acl_where_owned ("target", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT uuid, name, %s FROM targets"
" WHERE id IN"
" (SELECT target FROM targets_login_data"
" WHERE credential = %llu)"
" ORDER BY name %s;",
with_clause ? with_clause : "",
available,
credential,
ascending ? "ASC" : "DESC");
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the uuid from an Credential Target iterator.
*
* @param[in] iterator Iterator.
*
* @return Uuid, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_target_iterator_uuid, 0);
/**
* @brief Get the name from an Credential Target iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_target_iterator_name, 1);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
credential_target_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 2);
}
/**
* @brief Initialise a Credential scanner iterator.
*
* Iterates over all scanners that use the credential.
*
* @param[in] iterator Iterator.
* @param[in] credential Name of credential.
* @param[in] ascending Whether to sort ascending or descending.
*/
void
init_credential_scanner_iterator (iterator_t* iterator,
credential_t credential,
int ascending)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (credential);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_scanners"));
available = acl_where_owned ("scanner", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT uuid, name, %s FROM scanners"
" WHERE credential = %llu"
" ORDER BY name %s;",
with_clause ? with_clause : "",
available,
credential,
ascending ? "ASC" : "DESC");
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the uuid from an Credential Scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Uuid, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_scanner_iterator_uuid, 0);
/**
* @brief Get the name from an Credential Scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (credential_scanner_iterator_name, 1);
/**
* @brief Get the read permission status from a Credential Scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
credential_scanner_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 2);
}
/* Notes. */
/**
* @brief Find a note for a specific permission, given a UUID.
*
* @param[in] uuid UUID of note.
* @param[out] note Note return, 0 if successfully failed to find note.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find note), TRUE on error.
*/
gboolean
find_note_with_permission (const char* uuid, note_t* note,
const char *permission)
{
return find_resource_with_permission ("note", uuid, note, permission, 0);
}
/**
* @brief Check if an NVT exists.
*
* @param[in] nvt NVT OID.
*
* @return 1 if exists, else 0.
*/
static gboolean
nvt_exists (const char* nvt)
{
gchar *quoted_nvt;
quoted_nvt = sql_quote (nvt);
if (g_str_has_prefix (nvt, "CVE-"))
{
if (sql_int ("SELECT count (*) FROM cves WHERE uuid = '%s'", quoted_nvt)
!= 0)
{
g_free (quoted_nvt);
return 1;
}
}
else if (strcmp (nvt, "0")
&& (sql_int ("SELECT count (*) FROM nvts WHERE oid = '%s'", quoted_nvt)
!= 0))
{
g_free (quoted_nvt);
return 1;
}
g_free (quoted_nvt);
return 0;
}
/**
* @brief Create a note.
*
* @param[in] active NULL or -1 on, 0 off, n on for n days.
* @param[in] nvt OID of noted NVT.
* @param[in] text Note text.
* @param[in] hosts Hosts to apply note to, NULL for any host.
* @param[in] port Port to apply note to, NULL for any port.
* @param[in] severity Severity to apply note to, "" or NULL for any.
* @param[in] threat Threat to apply note to, "" or NULL for any threat.
* Only used if severity is "" or NULL.
* @param[in] task Task to apply note to, 0 for any task.
* @param[in] result Result to apply note to, 0 for any result.
* @param[out] note Created note.
*
* @return 0 success, 1 failed to find NVT, 2 invalid port, 99 permission
* denied, -1 error.
*/
int
create_note (const char* active, const char* nvt, const char* text,
const char* hosts, const char* port, const char* severity,
const char* threat, task_t task, result_t result, note_t *note)
{
gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
double severity_dbl;
if (acl_user_may ("create_note") == 0)
return 99;
if (nvt == NULL)
return -1;
if (!nvt_exists (nvt))
return 1;
if (port && validate_results_port (port))
return 2;
if (text == NULL)
return -1;
if (threat && strcmp (threat, "High") && strcmp (threat, "Medium")
&& strcmp (threat, "Low") && strcmp (threat, "Log")
&& strcmp (threat, ""))
return -1;
quoted_text = sql_insert (text);
quoted_hosts = sql_insert (hosts);
quoted_port = sql_insert (port);
severity_dbl = 0.0;
if (severity != NULL && strcmp (severity, ""))
{
if (sscanf (severity, "%lf", &severity_dbl) != 1
|| ((severity_dbl < 0.0 || severity_dbl > 10.0)
&& severity_dbl != SEVERITY_LOG))
return 3;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else if (threat != NULL && strcmp (threat, ""))
{
if (strcmp (threat, "Alarm") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "High") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Medium") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Low") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Log") == 0)
severity_dbl = SEVERITY_LOG;
else
return -1;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else
quoted_severity = g_strdup ("NULL");
sql ("INSERT INTO notes"
" (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
" port, severity, task, result, end_time)"
" VALUES"
" (make_uuid (), (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', %i, %i, %s, %s, %s, %s, %llu, %llu, %i);",
current_credentials.uuid,
nvt,
time (NULL),
time (NULL),
quoted_text,
quoted_hosts,
quoted_port,
quoted_severity,
task,
result,
(active == NULL || (strcmp (active, "-1") == 0))
? 0
: (strcmp (active, "0")
? (time (NULL) + (atoi (active) * 60 * 60 * 24))
: 1));
g_free (quoted_text);
g_free (quoted_hosts);
g_free (quoted_port);
g_free (quoted_severity);
if (note)
*note = sql_last_insert_id ();
return 0;
}
/**
* @brief Create a note from an existing note.
*
* @param[in] note_id UUID of existing note.
* @param[out] new_note New note.
*
* @return 0 success, 1 note exists already, 2 failed to find existing
* note, -1 error.
*/
int
copy_note (const char *note_id, note_t* new_note)
{
return copy_resource ("note", NULL, NULL, note_id,
"nvt, text, hosts, port, severity, task, result,"
"end_time",
1, new_note, NULL);
}
/**
* @brief Delete a note.
*
* @param[in] note_id UUID of note.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 2 failed to find note, 99 permission denied, -1 error.
*/
int
delete_note (const char *note_id, int ultimate)
{
note_t note = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_note") == 0)
{
sql_rollback ();
return 99;
}
if (find_note_with_permission (note_id, ¬e, "delete_note"))
{
sql_rollback ();
return -1;
}
if (note == 0)
{
if (find_trash ("note", note_id, ¬e))
{
sql_rollback ();
return -1;
}
if (note == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
permissions_set_orphans ("note", note, LOCATION_TRASH);
tags_remove_resource ("note", note, LOCATION_TRASH);
sql ("DELETE FROM notes_trash WHERE id = %llu;", note);
sql_commit ();
return 0;
}
if (ultimate == 0)
{
sql ("INSERT INTO notes_trash"
" (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
" port, severity, task, result, end_time)"
" SELECT uuid, owner, nvt, creation_time, modification_time, text,"
" hosts, port, severity, task, result, end_time"
" FROM notes WHERE id = %llu;",
note);
permissions_set_locations ("note", note,
sql_last_insert_id (),
LOCATION_TRASH);
tags_set_locations ("note", note,
sql_last_insert_id (),
LOCATION_TRASH);
}
else
{
permissions_set_orphans ("note", note, LOCATION_TABLE);
tags_remove_resource ("note", note, LOCATION_TABLE);
}
sql ("DELETE FROM notes WHERE id = %llu;", note);
sql_commit ();
return 0;
}
/**
* @brief Return the UUID of a note.
*
* @param[in] note Note.
* @param[out] id Pointer to a newly allocated string.
*
* @return 0.
*/
int
note_uuid (note_t note, char ** id)
{
*id = sql_string ("SELECT uuid FROM notes WHERE id = %llu;",
note);
return 0;
}
/**
* @brief Modify a note.
*
* @param[in] note_id Note.
* @param[in] active NULL or -2 leave as is, -1 on, 0 off, n on for n
* days.
* @param[in] nvt OID of noted NVT.
* @param[in] text Note text.
* @param[in] hosts Hosts to apply note to, NULL for any host.
* @param[in] port Port to apply note to, NULL for any port.
* @param[in] severity Severity to apply note to, "" or NULL for any.
* @param[in] threat Threat to apply note to, "" or NULL for any threat.
* Only used if severity is "" or NULL.
* @param[in] task_id Task to apply note to, NULL for any task.
* @param[in] result_id Result to apply note to, 0 for any result.
*
* @return 0 success, -1 error, 1 syntax error in active, 2 invalid port,
* 3 invalid severity, 4 failed to find NVT, 5 failed to find note,
* 6 failed to find task, 7 failed to find result.
*/
int
modify_note (const gchar *note_id, const char *active, const char *nvt,
const char *text, const char *hosts, const char *port,
const char *severity, const char *threat, const gchar *task_id,
const gchar *result_id)
{
gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
double severity_dbl;
gchar *quoted_nvt;
note_t note;
task_t task;
result_t result;
note = 0;
if (find_note_with_permission (note_id, ¬e, "modify_note"))
return -1;
else if (note == 0)
return 5;
task = 0;
if (task_id)
{
if (find_task_with_permission (task_id, &task, NULL))
return -1;
if (task == 0)
{
if (find_trash_task_with_permission (task_id, &task, NULL))
return -1;
if (task == 0)
return 6;
}
}
result = 0;
if (result_id)
{
if (find_result_with_permission (result_id, &result, NULL))
return -1;
if (result == 0)
return 7;
}
if (text == NULL)
return -1;
if (nvt && !nvt_exists (nvt))
return 4;
if (threat && strcmp (threat, "High") && strcmp (threat, "Medium")
&& strcmp (threat, "Low") && strcmp (threat, "Log")
&& strcmp (threat, "Alarm") && strcmp (threat, ""))
return -1;
if (port && validate_results_port (port))
return 2;
quoted_text = sql_insert (text);
quoted_hosts = sql_insert (hosts);
quoted_port = sql_insert (port);
quoted_nvt = sql_insert (nvt);
severity_dbl = 0.0;
if (severity != NULL && strcmp (severity, ""))
{
if (sscanf (severity, "%lf", &severity_dbl) != 1
|| ((severity_dbl < 0.0 || severity_dbl > 10.0)
&& severity_dbl != SEVERITY_LOG))
return 3;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else if (threat != NULL && strcmp (threat, ""))
{
if (strcmp (threat, "Alarm") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "High") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Medium") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Low") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Log") == 0)
severity_dbl = SEVERITY_LOG;
else
return -1;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else
quoted_severity = g_strdup ("NULL");
if ((active == NULL) || (strcmp (active, "-2") == 0))
sql ("UPDATE notes SET"
" modification_time = %i,"
" text = %s,"
" hosts = %s,"
" port = %s,"
" severity = %s,"
" %s%s%s"
" task = %llu,"
" result = %llu"
" WHERE id = %llu;",
time (NULL),
quoted_text,
quoted_hosts,
quoted_port,
quoted_severity,
nvt ? "nvt = " : "",
nvt ? quoted_nvt : "",
nvt ? "," : "",
task,
result,
note);
else
{
const char *point;
point = active;
if (strcmp (point, "-1"))
{
while (*point && isdigit (*point)) point++;
if (*point)
return 1;
}
sql ("UPDATE notes SET"
" end_time = %i,"
" modification_time = %i,"
" text = %s,"
" hosts = %s,"
" port = %s,"
" severity = %s,"
"%s%s%s"
" task = %llu,"
" result = %llu"
" WHERE id = %llu;",
(strcmp (active, "-1")
? (strcmp (active, "0")
? (time (NULL) + atoi (active) * 60 * 60 * 24)
: 1)
: 0),
time (NULL),
quoted_text,
quoted_hosts,
quoted_port,
quoted_severity,
nvt ? "nvt = " : "",
nvt ? quoted_nvt : "",
nvt ? "," : "",
task,
result,
note);
}
g_free (quoted_text);
g_free (quoted_hosts);
g_free (quoted_port);
g_free (quoted_severity);
g_free (quoted_nvt);
return 0;
}
/**
* @brief Filter columns for note iterator.
*/
#define NOTE_ITERATOR_FILTER_COLUMNS \
{ ANON_GET_ITERATOR_FILTER_COLUMNS, "name", "nvt", "text", "nvt_id", \
"task_name", "task_id", "hosts", "port", "active", "result", "severity", \
"end_time", "active_days", NULL }
/**
* @brief Note iterator columns.
*/
#define NOTE_ITERATOR_COLUMNS \
{ \
{ "notes.id", "id", KEYWORD_TYPE_INTEGER }, \
{ "notes.uuid", "uuid", KEYWORD_TYPE_STRING }, \
{ \
"(CASE" \
" WHEN notes.nvt LIKE 'CVE-%%'" \
" THEN notes.nvt" \
" ELSE (SELECT name FROM nvts WHERE oid = notes.nvt)" \
" END)", \
"name", \
KEYWORD_TYPE_STRING \
}, \
{ "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (notes.creation_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (notes.modification_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "notes.creation_time", "created", KEYWORD_TYPE_INTEGER }, \
{ "notes.modification_time", "modified", KEYWORD_TYPE_INTEGER }, \
{ "(SELECT name FROM users WHERE users.id = notes.owner)", \
"_owner", \
KEYWORD_TYPE_STRING }, \
{ "owner", NULL, KEYWORD_TYPE_INTEGER }, \
/* Columns specific to notes. */ \
{ "notes.nvt", "oid", KEYWORD_TYPE_STRING }, \
{ "notes.text", "text", KEYWORD_TYPE_STRING }, \
{ "notes.hosts", "hosts", KEYWORD_TYPE_STRING }, \
{ "notes.port", "port", KEYWORD_TYPE_STRING }, \
{ "notes.task", NULL, KEYWORD_TYPE_INTEGER }, \
{ "notes.result", "result", KEYWORD_TYPE_INTEGER }, \
{ "notes.end_time", "end_time", KEYWORD_TYPE_INTEGER }, \
{ "CAST (((notes.end_time = 0) OR (notes.end_time >= m_now ()))" \
" AS INTEGER)", \
"active", \
KEYWORD_TYPE_INTEGER }, \
{ \
"(CASE" \
" WHEN notes.nvt LIKE 'CVE-%%'" \
" THEN notes.nvt" \
" ELSE (SELECT name FROM nvts WHERE oid = notes.nvt)" \
" END)", \
"nvt", \
KEYWORD_TYPE_STRING \
}, \
{ "notes.nvt", "nvt_id", KEYWORD_TYPE_STRING }, \
{ "(SELECT uuid FROM tasks WHERE id = notes.task)", \
"task_id", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT name FROM tasks WHERE id = notes.task)", \
"task_name", \
KEYWORD_TYPE_STRING }, \
{ "notes.severity", "severity", KEYWORD_TYPE_DOUBLE }, \
{ "(SELECT name FROM users WHERE users.id = notes.owner)", \
"_owner", \
KEYWORD_TYPE_STRING }, \
{ "days_from_now (notes.end_time)", \
"active_days", \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Note iterator columns for trash case.
*/
#define NOTE_ITERATOR_TRASH_COLUMNS \
{ \
{ "notes_trash.id", "id", KEYWORD_TYPE_INTEGER }, \
{ "notes_trash.uuid", "uuid", KEYWORD_TYPE_STRING }, \
{ "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING }, \
{ "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (notes_trash.creation_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (notes_trash.modification_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "notes_trash.creation_time", "created", KEYWORD_TYPE_INTEGER }, \
{ "notes_trash.modification_time", "modified", KEYWORD_TYPE_INTEGER }, \
{ "(SELECT name FROM users WHERE users.id = notes_trash.owner)", \
"_owner", \
KEYWORD_TYPE_STRING }, \
{ "owner", NULL, KEYWORD_TYPE_INTEGER }, \
/* Columns specific to notes_trash. */ \
{ "notes_trash.nvt", "oid", KEYWORD_TYPE_STRING }, \
{ "notes_trash.text", "text", KEYWORD_TYPE_STRING }, \
{ "notes_trash.hosts", "hosts", KEYWORD_TYPE_STRING }, \
{ "notes_trash.port", "port", KEYWORD_TYPE_STRING }, \
{ "severity_to_level (notes_trash.severity, 1)", \
"threat", \
KEYWORD_TYPE_STRING }, \
{ "notes_trash.task", NULL, KEYWORD_TYPE_INTEGER }, \
{ "notes_trash.result", "result", KEYWORD_TYPE_INTEGER }, \
{ "notes_trash.end_time", NULL, KEYWORD_TYPE_INTEGER }, \
{ "CAST (((notes_trash.end_time = 0) OR (notes_trash.end_time >= m_now ()))" \
" AS INTEGER)", \
"active", \
KEYWORD_TYPE_INTEGER }, \
{ \
"(CASE" \
" WHEN notes_trash.nvt LIKE 'CVE-%%'" \
" THEN notes_trash.nvt" \
" ELSE (SELECT name FROM nvts WHERE oid = notes_trash.nvt)" \
" END)", \
"nvt", \
KEYWORD_TYPE_STRING \
}, \
{ "notes_trash.nvt", "nvt_id", KEYWORD_TYPE_STRING }, \
{ "(SELECT uuid FROM tasks WHERE id = notes_trash.task)", \
"task_id", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT name FROM tasks WHERE id = notes_trash.task)", \
"task_name", \
KEYWORD_TYPE_STRING }, \
{ "notes_trash.severity", "severity", KEYWORD_TYPE_DOUBLE }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of notes.
*
* @param[in] get GET params.
* @param[in] result Result to limit notes to, 0 for all.
* @param[in] task If result is > 0, task whose notes on result to
* include, otherwise task to limit notes to. 0 for
* all tasks.
* @param[in] nvt NVT to limit notes to, 0 for all.
*
* @return Total number of notes in filtered set.
*/
int
note_count (const get_data_t *get, nvt_t nvt, result_t result, task_t task)
{
static const char *filter_columns[] = NOTE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = NOTE_ITERATOR_COLUMNS;
static column_t trash_columns[] = NOTE_ITERATOR_TRASH_COLUMNS;
gchar *result_clause, *filter, *task_id;
int ret;
/* Treat the "task_id" filter keyword as if the task was given in "task". */
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
task_id = filter_term_value (filter ? filter : get->filter, "task_id");
g_free (filter);
if (task_id)
{
find_task_with_permission (task_id, &task, "get_tasks");
g_free (task_id);
}
if (result)
{
gchar *severity_sql;
if (setting_dynamic_severity_int ())
severity_sql = g_strdup_printf ("(SELECT CASE"
" WHEN results.severity"
" > " G_STRINGIFY (SEVERITY_LOG)
" THEN CAST (nvts.cvss_base AS real)"
" ELSE results.severity END"
" FROM results, nvts"
" WHERE (nvts.oid = results.nvt)"
" AND (results.id = %llu))",
result);
else
severity_sql = g_strdup_printf ("(SELECT results.severity"
" FROM results"
" WHERE results.id = %llu)",
result);
result_clause = g_strdup_printf (" AND"
" (result = %llu"
" OR (result = 0 AND nvt ="
" (SELECT results.nvt FROM results"
" WHERE results.id = %llu)))"
" AND (hosts is NULL"
" OR hosts = ''"
" OR hosts_contains (hosts,"
" (SELECT results.host FROM results"
" WHERE results.id = %llu)))"
" AND (port is NULL"
" OR port = ''"
" OR port ="
" (SELECT results.port FROM results"
" WHERE results.id = %llu))"
" AND (severity_matches_ov (%s,"
" severity))"
" AND (task = 0 OR task = %llu)",
result,
result,
result,
result,
severity_sql,
task);
g_free (severity_sql);
}
else if (task)
{
result_clause = g_strdup_printf
(" AND (notes.task = %llu OR notes.task = 0)"
" AND nvt IN"
" (SELECT DISTINCT nvt FROM results"
" WHERE results.task = %llu)"
" AND (notes.result = 0"
" OR (SELECT task FROM results"
" WHERE results.id = notes.result)"
" = %llu)",
task,
task,
task);
}
else if (nvt)
{
result_clause = g_strdup_printf
(" AND (notes.nvt = (SELECT oid FROM nvts"
" WHERE nvts.id = %llu))",
nvt);
}
else
result_clause = NULL;
ret = count ("note",
get,
columns,
trash_columns,
filter_columns,
task || nvt,
NULL,
result_clause,
TRUE);
g_free (result_clause);
return ret;
}
/**
* @brief Initialise a note iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
* @param[in] result Result to limit notes to, 0 for all.
* @param[in] task If result is > 0, task whose notes on result to
* include, otherwise task to limit notes to. 0 for
* all tasks.
* @param[in] nvt NVT to limit notes to, 0 for all.
*
* @return 0 success, 1 failed to find target, 2 failed to find filter,
* -1 error.
*/
int
init_note_iterator (iterator_t* iterator, const get_data_t *get, nvt_t nvt,
result_t result, task_t task)
{
static const char *filter_columns[] = NOTE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = NOTE_ITERATOR_COLUMNS;
static column_t trash_columns[] = NOTE_ITERATOR_TRASH_COLUMNS;
gchar *result_clause, *filter, *task_id;
int ret;
assert (current_credentials.uuid);
assert ((nvt && get->id) == 0);
assert ((task && get->id) == 0);
assert (result ? nvt == 0 : 1);
assert (task ? nvt == 0 : 1);
/* Treat the "task_id" filter keyword as if the task was given in "task". */
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
task_id = filter_term_value (filter ? filter : get->filter, "task_id");
g_free (filter);
if (task_id)
{
find_task_with_permission (task_id, &task, "get_tasks");
g_free (task_id);
}
if (result)
{
gchar *severity_sql;
if (setting_dynamic_severity_int ())
severity_sql = g_strdup_printf ("(SELECT CASE"
" WHEN results.severity"
" > " G_STRINGIFY (SEVERITY_LOG)
" THEN CAST (nvts.cvss_base AS real)"
" ELSE results.severity END"
" FROM results, nvts"
" WHERE (nvts.oid = results.nvt)"
" AND (results.id = %llu))",
result);
else
severity_sql = g_strdup_printf ("(SELECT results.severity"
" FROM results"
" WHERE results.id = %llu)",
result);
result_clause = g_strdup_printf (" AND"
" (result = %llu"
" OR (result = 0 AND nvt ="
" (SELECT results.nvt FROM results"
" WHERE results.id = %llu)))"
" AND (hosts is NULL"
" OR hosts = ''"
" OR hosts_contains (hosts,"
" (SELECT results.host FROM results"
" WHERE results.id = %llu)))"
" AND (port is NULL"
" OR port = ''"
" OR port ="
" (SELECT results.port FROM results"
" WHERE results.id = %llu))"
" AND (severity_matches_ov (%s,"
" severity))"
" AND (task = 0 OR task = %llu)",
result,
result,
result,
result,
severity_sql,
task);
g_free (severity_sql);
}
else if (task)
{
result_clause = g_strdup_printf
(" AND (notes.task = %llu OR notes.task = 0)"
" AND nvt IN (SELECT DISTINCT nvt FROM results"
" WHERE results.task = %llu)"
" AND (notes.result = 0"
" OR (SELECT task FROM results"
" WHERE results.id = notes.result)"
" = %llu)",
task,
task,
task);
}
else if (nvt)
{
result_clause = g_strdup_printf
(" AND (notes.nvt = (SELECT oid FROM nvts"
" WHERE nvts.id = %llu))",
nvt);
}
else
result_clause = NULL;
ret = init_get_iterator (iterator,
"note",
get,
columns,
trash_columns,
filter_columns,
task || nvt,
NULL,
result_clause,
TRUE);
g_free (result_clause);
return ret;
}
/**
* @brief Get the NVT OID from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return NVT OID, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (note_iterator_nvt_oid, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the text from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return Text, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (note_iterator_text, GET_ITERATOR_COLUMN_COUNT + 1);
/**
* @brief Get the hosts from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return Hosts, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (note_iterator_hosts, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get the port from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return Port, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (note_iterator_port, GET_ITERATOR_COLUMN_COUNT + 3);
/**
* @brief Get the task from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return The task associated with the note, or 0 on error.
*/
task_t
note_iterator_task (iterator_t* iterator)
{
if (iterator->done) return 0;
return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
}
/**
* @brief Get the result from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return The result associated with the note, or 0 on error.
*/
result_t
note_iterator_result (iterator_t* iterator)
{
if (iterator->done) return 0;
return (result_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
}
/**
* @brief Get the end time from an note iterator.
*
* @param[in] iterator Iterator.
*
* @return Time until which note applies. 0 for always. 1 means the
* note has been explicitly turned off.
*/
time_t
note_iterator_end_time (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = (time_t) iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
return ret;
}
/**
* @brief Get the active status from an note iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if active, else 0.
*/
int
note_iterator_active (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
return ret;
}
/**
* @brief Get the NVT name from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return NVT name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (note_iterator_nvt_name, GET_ITERATOR_COLUMN_COUNT + 8);
/**
* @brief Get the NVT type from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return NVT type, or NULL. Static string.
*/
const char *
note_iterator_nvt_type (iterator_t *iterator)
{
const char *oid;
oid = note_iterator_nvt_oid (iterator);
if (oid == NULL)
return NULL;
if (g_str_has_prefix (oid, "CVE-"))
return "cve";
if (g_str_has_prefix (oid, "oval:"))
return "ovaldef";
return "nvt";
}
/**
* @brief Get the severity from a note iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity to apply the note to, or NULL if iteration is complete.
* Freed by cleanup_iterator.
*/
DEF_ACCESS (note_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 12);
/* Overrides. */
/**
* @brief Find a override for a specific permission, given a UUID.
*
* @param[in] uuid UUID of override.
* @param[out] override Override return, 0 if successfully failed to find
* override.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find override), TRUE on
* error.
*/
gboolean
find_override_with_permission (const char* uuid, override_t* override,
const char *permission)
{
return find_resource_with_permission ("override", uuid, override, permission,
0);
}
/**
* @brief Create an override.
*
* @param[in] active NULL or -1 on, 0 off, n on for n days.
* @param[in] nvt OID of overridden NVT.
* @param[in] text Override text.
* @param[in] hosts Hosts to apply override to, NULL for any host.
* @param[in] port Port to apply override to, NULL for any port.
* @param[in] threat Threat to apply override to, "" or NULL for any threat.
* @param[in] new_threat Threat to override result to.
* @param[in] severity Severity to apply override to, "" or NULL for any.
* @param[in] new_severity Severity score to override "Alarm" type results to.
* @param[in] task Task to apply override to, 0 for any task.
* @param[in] result Result to apply override to, 0 for any result.
* @param[out] override Created override.
*
* @return 0 success, 1 failed to find NVT, 2 invalid port, 3 invalid severity,
* 99 permission denied, -1 error.
*/
int
create_override (const char* active, const char* nvt, const char* text,
const char* hosts, const char* port, const char* threat,
const char* new_threat, const char* severity,
const char* new_severity, task_t task, result_t result,
override_t* override)
{
gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
double severity_dbl, new_severity_dbl;
GHashTable *reports;
GHashTableIter reports_iter;
report_t *reports_ptr;
gchar *override_id, *users_where;
int auto_cache_rebuild;
override_t new_override;
if (acl_user_may ("create_override") == 0)
return 99;
if (nvt == NULL)
return -1;
if (text == NULL)
return -1;
if (!nvt_exists (nvt))
return 1;
if (port && validate_results_port (port))
return 2;
if (threat && strcmp (threat, "High") && strcmp (threat, "Medium")
&& strcmp (threat, "Low") && strcmp (threat, "Log")
&& strcmp (threat, "Alarm") && strcmp (threat, ""))
return -1;
if (new_threat && strcmp (new_threat, "High") && strcmp (new_threat, "Medium")
&& strcmp (new_threat, "Low") && strcmp (new_threat, "Log")
&& strcmp (new_threat, "False Positive")
&& strcmp (new_threat, "Alarm") && strcmp (new_threat, ""))
return -1;
severity_dbl = 0.0;
if (severity != NULL && strcmp (severity, ""))
{
if (sscanf (severity, "%lf", &severity_dbl) != 1
|| ((severity_dbl < 0.0 || severity_dbl > 10.0)
&& severity_dbl != SEVERITY_LOG))
return 3;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else if (threat != NULL && strcmp (threat, ""))
{
if (strcmp (threat, "Alarm") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "High") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Medium") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Low") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Log") == 0)
severity_dbl = SEVERITY_LOG;
else
return -1;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else
quoted_severity = g_strdup ("NULL");
new_severity_dbl = 0.0;
if (new_severity != NULL && strcmp (new_severity, ""))
{
if (sscanf (new_severity, "%lf", &new_severity_dbl) != 1
|| ((new_severity_dbl < 0.0 || new_severity_dbl > 10.0)
&& new_severity_dbl != SEVERITY_LOG
&& new_severity_dbl != SEVERITY_FP))
{
g_free (quoted_severity);
return 2;
}
}
else if (new_threat != NULL && strcmp (new_threat, ""))
{
if (strcmp (new_threat, "Alarm") == 0)
new_severity_dbl = 10.0;
else if (strcmp (new_threat, "High") == 0)
new_severity_dbl = 10.0;
else if (strcmp (new_threat, "Medium") == 0)
new_severity_dbl = 5.0;
else if (strcmp (new_threat, "Low") == 0)
new_severity_dbl = 2.0;
else if (strcmp (new_threat, "Log") == 0)
new_severity_dbl = SEVERITY_LOG;
else
return -1;
}
else
{
g_free (quoted_severity);
return -1;
}
quoted_text = sql_insert (text);
quoted_hosts = sql_insert (hosts);
quoted_port = sql_insert (port);
result_nvt_notice (nvt);
sql ("INSERT INTO overrides"
" (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
" port, severity, new_severity, task, result, end_time,"
" result_nvt)"
" VALUES"
" (make_uuid (), (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', %i, %i, %s, %s, %s, %s, %1.1f, %llu, %llu, %i,"
" (SELECT id FROM result_nvts WHERE nvt = '%s'));",
current_credentials.uuid,
nvt,
time (NULL),
time (NULL),
quoted_text,
quoted_hosts,
quoted_port,
quoted_severity,
new_severity_dbl,
task,
result,
(active == NULL || (strcmp (active, "-1") == 0))
? 0
: (strcmp (active, "0")
? (time (NULL) + (atoi (active) * 60 * 60 * 24))
: 1),
nvt);
g_free (quoted_text);
g_free (quoted_hosts);
g_free (quoted_port);
g_free (quoted_severity);
if (override)
*override = sql_last_insert_id ();
new_override = sql_last_insert_id ();
override_uuid (new_override, &override_id);
users_where = acl_users_with_access_where ("override", override_id, NULL,
"id");
reports = reports_for_override (new_override);
reports_ptr = NULL;
g_hash_table_iter_init (&reports_iter, reports);
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, 0, 1, users_where);
else
report_clear_count_cache (*reports_ptr, 0, 1, users_where);
}
g_hash_table_destroy (reports);
g_free (override_id);
g_free (users_where);
return 0;
}
/**
* @brief Return the UUID of an override.
*
* @param[in] override Override.
* @param[out] id Pointer to a newly allocated string.
*
* @return 0.
*/
int
override_uuid (override_t override, char ** id)
{
*id = sql_string ("SELECT uuid FROM overrides WHERE id = %llu;",
override);
return 0;
}
/**
* @brief Create a override from an existing override.
*
* @param[in] override_id UUID of existing override.
* @param[out] new_override New override.
*
* @return 0 success, 1 override exists already, 2 failed to find existing
* override, -1 error.
*/
int
copy_override (const char *override_id, override_t* new_override)
{
return copy_resource ("override", NULL, NULL, override_id,
"nvt, text, hosts, port, severity, new_severity, task,"
" result, end_time, result_nvt",
1, new_override, NULL);
}
/**
* @brief Delete a override.
*
* @param[in] override_id UUID of override.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 2 failed to find override, 99 permission denied, -1 error.
*/
int
delete_override (const char *override_id, int ultimate)
{
override_t override;
GHashTable *reports;
GHashTableIter reports_iter;
report_t *reports_ptr;
gchar *users_where;
int auto_cache_rebuild;
sql_begin_immediate ();
if (acl_user_may ("delete_override") == 0)
{
sql_rollback ();
return 99;
}
override = 0;
if (find_override_with_permission (override_id, &override, "delete_override"))
{
sql_rollback ();
return -1;
}
if (override == 0)
{
if (find_trash ("override", override_id, &override))
{
sql_rollback ();
return -1;
}
if (override == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
permissions_set_orphans ("override", override, LOCATION_TRASH);
tags_remove_resource ("override", override, LOCATION_TRASH);
sql ("DELETE FROM overrides_trash WHERE id = %llu;", override);
sql_commit ();
return 0;
}
reports = reports_for_override (override);
users_where = acl_users_with_access_where ("override", override_id, NULL,
"id");
if (ultimate == 0)
{
sql ("INSERT INTO overrides_trash"
" (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
" port, severity, new_severity, task, result, end_time, result_nvt)"
" SELECT uuid, owner, nvt, creation_time, modification_time, text,"
" hosts, port, severity, new_severity,task,"
" result, end_time, result_nvt"
" FROM overrides WHERE id = %llu;",
override);
permissions_set_locations ("override", override,
sql_last_insert_id (),
LOCATION_TRASH);
tags_set_locations ("override", override,
sql_last_insert_id (),
LOCATION_TRASH);
}
else
{
permissions_set_orphans ("override", override, LOCATION_TABLE);
tags_remove_resource ("override", override, LOCATION_TABLE);
}
sql ("DELETE FROM overrides WHERE id = %llu;", override);
g_hash_table_iter_init (&reports_iter, reports);
reports_ptr = NULL;
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, 0, 1, users_where);
else
report_clear_count_cache (*reports_ptr, 0, 1, users_where);
}
g_hash_table_destroy (reports);
g_free (users_where);
sql_commit ();
return 0;
}
/**
* @brief Modify an override.
*
* @param[in] override_id Override.
* @param[in] active NULL or -2 leave as is, -1 on, 0 off, n on for n
* days.
* @param[in] nvt OID of noted NVT.
* @param[in] text Override text.
* @param[in] hosts Hosts to apply override to, NULL for any host.
* @param[in] port Port to apply override to, NULL for any port.
* @param[in] threat Threat to apply override to, "" or NULL for any threat.
* @param[in] new_threat Threat to override result to.
* @param[in] severity Severity to apply override to, "" or NULL for any threat.
* @param[in] new_severity Severity score to override "Alarm" type results to.
* @param[in] task_id Task to apply override to, 0 for any task.
* @param[in] result_id Result to apply override to, 0 for any result.
*
* @return 0 success, -1 error, 1 syntax error in active, 2 invalid port,
* 3 invalid severity score, 4 failed to find NVT, 5 failed to find
* override, 6 failed to find task, 7 failed to find result,
* 8 invalid threat, 9 invalid new_threat, 10 invalid new_severity,
* 11 missing new_severity.
*/
int
modify_override (const gchar *override_id, const char *active, const char *nvt,
const char *text, const char *hosts, const char *port,
const char *threat, const char *new_threat,
const char *severity, const char *new_severity,
const gchar *task_id, const gchar *result_id)
{
gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
double severity_dbl, new_severity_dbl;
gchar *quoted_nvt;
GHashTable *reports;
GString *cache_invalidated_sql;
int cache_invalidated;
override_t override;
task_t task;
result_t result;
reports = NULL;
cache_invalidated = 0;
override = 0;
if (find_override_with_permission (override_id, &override, "modify_override"))
return -1;
if (override == 0)
return 5;
task = 0;
if (task_id)
{
if (find_task_with_permission (task_id, &task, NULL))
return -1;
if (task == 0)
{
if (find_trash_task_with_permission (task_id, &task, NULL))
return -1;
if (task == 0)
return 6;
}
}
result = 0;
if (result_id)
{
if (find_result_with_permission (result_id, &result, NULL))
return -1;
if (result == 0)
return 7;
}
if (text == NULL)
return -1;
if (port && validate_results_port (port))
return 2;
if (nvt && !nvt_exists (nvt))
return 4;
severity_dbl = 0.0;
if (severity != NULL && strcmp (severity, ""))
{
if (sscanf (severity, "%lf", &severity_dbl) != 1
|| ((severity_dbl < 0.0 || severity_dbl > 10.0)
&& severity_dbl != SEVERITY_LOG))
return 3;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else if (threat != NULL && strcmp (threat, ""))
{
if (strcmp (threat, "Alarm") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "High") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Medium") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Low") == 0)
severity_dbl = 0.1;
else if (strcmp (threat, "Log") == 0)
severity_dbl = SEVERITY_LOG;
else
return 8;
quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
}
else
quoted_severity = g_strdup ("NULL");
new_severity_dbl = 0.0;
if (new_severity != NULL && strcmp (new_severity, ""))
{
if (sscanf (new_severity, "%lf", &new_severity_dbl) != 1
|| ((new_severity_dbl < 0.0 || new_severity_dbl > 10.0)
&& new_severity_dbl != SEVERITY_LOG
&& new_severity_dbl != SEVERITY_FP))
{
g_free (quoted_severity);
return 10;
}
}
else if (new_threat != NULL && strcmp (new_threat, ""))
{
if (strcmp (new_threat, "Alarm") == 0)
new_severity_dbl = 10.0;
else if (strcmp (new_threat, "High") == 0)
new_severity_dbl = 10.0;
else if (strcmp (new_threat, "Medium") == 0)
new_severity_dbl = 5.0;
else if (strcmp (new_threat, "Low") == 0)
new_severity_dbl = 2.0;
else if (strcmp (new_threat, "Log") == 0)
new_severity_dbl = SEVERITY_LOG;
else
{
g_free (quoted_severity);
return 9;
}
}
else
{
g_free (quoted_severity);
return 11;
}
quoted_text = sql_insert (text);
quoted_hosts = sql_insert (hosts);
quoted_port = sql_insert (port);
quoted_nvt = nvt ? sql_quote (nvt) : NULL;
// Tests if a cache rebuild is necessary.
// The "active" status is checked separately
cache_invalidated_sql = g_string_new ("");
g_string_append_printf (cache_invalidated_sql,
"SELECT (cast (new_severity AS numeric) != %1.1f)",
new_severity_dbl);
g_string_append_printf (cache_invalidated_sql,
" OR (task != %llu)",
task);
g_string_append_printf (cache_invalidated_sql,
" OR (result != %llu)",
result);
if (strcmp (quoted_severity, "NULL") == 0)
g_string_append_printf (cache_invalidated_sql,
" OR (severity IS NOT NULL)");
else
g_string_append_printf (cache_invalidated_sql,
" OR (cast (severity AS numeric) != %1.1f)",
severity_dbl);
if (strcmp (quoted_hosts, "NULL") == 0)
g_string_append_printf (cache_invalidated_sql,
" OR (hosts IS NOT NULL)");
else
g_string_append_printf (cache_invalidated_sql,
" OR (hosts != %s)",
quoted_hosts);
if (strcmp (quoted_port, "NULL") == 0)
g_string_append_printf (cache_invalidated_sql,
" OR (hosts IS NOT NULL)");
else
g_string_append_printf (cache_invalidated_sql,
" OR (port != %s)",
quoted_port);
g_string_append_printf (cache_invalidated_sql,
" FROM overrides WHERE id = %llu",
override);
if (sql_int ("%s", cache_invalidated_sql->str))
{
cache_invalidated = 1;
}
g_string_free (cache_invalidated_sql, TRUE);
// Check active status for changes, get old reports for rebuild if necessary
// and update override.
result_nvt_notice (quoted_nvt);
if ((active == NULL) || (strcmp (active, "-2") == 0))
{
if (cache_invalidated)
reports = reports_for_override (override);
sql ("UPDATE overrides SET"
" modification_time = %i,"
" text = %s,"
" hosts = %s,"
" port = %s,"
" severity = %s,"
" %s%s%s"
" %s%s%s"
" new_severity = %f,"
" task = %llu,"
" result = %llu"
" WHERE id = %llu;",
time (NULL),
quoted_text,
quoted_hosts,
quoted_port,
quoted_severity,
nvt ? "nvt = '" : "",
nvt ? quoted_nvt : "",
nvt ? "'," : "",
nvt ? "result_nvt = (SELECT id FROM result_nvts WHERE nvt='" : "",
nvt ? quoted_nvt : "",
nvt ? "')," : "",
new_severity_dbl,
task,
result,
override);
}
else
{
const char *point;
point = active;
int new_end_time;
if (strcmp (point, "-1"))
{
while (*point && isdigit (*point)) point++;
if (*point)
{
return 1;
}
}
new_end_time = (strcmp (active, "-1")
? (strcmp (active, "0")
? (time (NULL) + atoi (active) * 60 * 60 * 24)
: 1)
: 0);
if (cache_invalidated == 0
&& sql_int ("SELECT end_time != %d FROM overrides"
" WHERE id = %llu",
new_end_time, override))
cache_invalidated = 1;
if (cache_invalidated)
reports = reports_for_override (override);
sql ("UPDATE overrides SET"
" end_time = %i,"
" modification_time = %i,"
" text = %s,"
" hosts = %s,"
" port = %s,"
" severity = %s,"
" %s%s%s"
" %s%s%s"
" new_severity = %f,"
" task = %llu,"
" result = %llu"
" WHERE id = %llu;",
new_end_time,
time (NULL),
quoted_text,
quoted_hosts,
quoted_port,
quoted_severity,
nvt ? "nvt = '" : "",
nvt ? quoted_nvt : "",
nvt ? "'," : "",
nvt ? "result_nvt = (SELECT id FROM result_nvts WHERE nvt='" : "",
nvt ? quoted_nvt : "",
nvt ? "')," : "",
new_severity_dbl,
task,
result,
override);
}
g_free (quoted_text);
g_free (quoted_hosts);
g_free (quoted_port);
g_free (quoted_severity);
g_free (quoted_nvt);
if (cache_invalidated)
{
GHashTableIter reports_iter;
report_t *reports_ptr;
gchar *users_where;
int auto_cache_rebuild;
users_where = acl_users_with_access_where ("override", override_id, NULL,
"id");
reports_add_for_override (reports, override);
g_hash_table_iter_init (&reports_iter, reports);
reports_ptr = NULL;
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, 0, 1, users_where);
else
report_clear_count_cache (*reports_ptr, 0, 1, users_where);
}
g_free (users_where);
}
if (reports)
g_hash_table_destroy (reports);
return 0;
}
/**
* @brief Filter columns for override iterator.
*/
#define OVERRIDE_ITERATOR_FILTER_COLUMNS \
{ ANON_GET_ITERATOR_FILTER_COLUMNS, "name", "nvt", "text", "nvt_id", \
"task_name", "task_id", "hosts", "port", "threat", "new_threat", "active", \
"result", "severity", "new_severity", "active_days", NULL }
/**
* @brief Override iterator columns.
*/
#define OVERRIDE_ITERATOR_COLUMNS \
{ \
{ "overrides.id", "id", KEYWORD_TYPE_INTEGER }, \
{ "overrides.uuid", "uuid", KEYWORD_TYPE_STRING }, \
{ \
"(CASE" \
" WHEN overrides.nvt LIKE 'CVE-%%'" \
" THEN overrides.nvt" \
" ELSE (SELECT name FROM nvts WHERE oid = overrides.nvt)" \
" END)", \
"name", \
KEYWORD_TYPE_STRING \
}, \
{ "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (overrides.creation_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (overrides.modification_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "overrides.creation_time", "created", KEYWORD_TYPE_INTEGER }, \
{ "overrides.modification_time", "modified", KEYWORD_TYPE_INTEGER }, \
{ \
"(SELECT name FROM users WHERE users.id = overrides.owner)", \
"_owner", \
KEYWORD_TYPE_STRING \
}, \
{ "owner", NULL, KEYWORD_TYPE_INTEGER }, \
/* Columns specific to overrides. */ \
{ "overrides.nvt", "oid", KEYWORD_TYPE_STRING }, \
{ "overrides.text", "text", KEYWORD_TYPE_STRING }, \
{ "overrides.hosts", "hosts", KEYWORD_TYPE_STRING }, \
{ "overrides.port", "port", KEYWORD_TYPE_STRING }, \
{ "severity_to_level (overrides.severity, 1)", \
"threat", \
KEYWORD_TYPE_STRING }, \
{ "severity_to_level (overrides.new_severity, 0)", \
"new_threat", \
KEYWORD_TYPE_STRING }, \
{ "overrides.task", NULL, KEYWORD_TYPE_STRING }, \
{ "overrides.result", "result", KEYWORD_TYPE_INTEGER }, \
{ "overrides.end_time", NULL, KEYWORD_TYPE_INTEGER }, \
{ \
"CAST (((overrides.end_time = 0) OR (overrides.end_time >= m_now ()))" \
" AS INTEGER)", \
"active", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(CASE" \
" WHEN overrides.nvt LIKE 'CVE-%%'" \
" THEN overrides.nvt" \
" ELSE (SELECT name FROM nvts WHERE oid = overrides.nvt)" \
" END)", \
"name", \
KEYWORD_TYPE_STRING \
}, \
{ "overrides.nvt", "nvt_id", KEYWORD_TYPE_STRING }, \
{ "(SELECT uuid FROM tasks WHERE id = overrides.task)", \
"task_id", \
KEYWORD_TYPE_STRING }, \
{ "(SELECT name FROM tasks WHERE id = overrides.task)", \
"task_name", \
KEYWORD_TYPE_STRING }, \
{ "overrides.severity", "severity", KEYWORD_TYPE_DOUBLE }, \
{ "overrides.new_severity", "new_severity", KEYWORD_TYPE_DOUBLE }, \
{ \
"(SELECT name FROM users WHERE users.id = overrides.owner)", \
"_owner", \
KEYWORD_TYPE_STRING \
}, \
{ "days_from_now (overrides.end_time)", \
"active_days", \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Override iterator columns for trash case.
*/
#define OVERRIDE_ITERATOR_TRASH_COLUMNS \
{ \
{ "overrides_trash.id", "id", KEYWORD_TYPE_INTEGER }, \
{ "overrides_trash.uuid", "uuid", KEYWORD_TYPE_STRING }, \
{ "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING }, \
{ "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (overrides_trash.creation_time)", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "iso_time (overrides_trash.modification_time)", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "overrides_trash.creation_time", \
"created", \
KEYWORD_TYPE_INTEGER }, \
{ "overrides_trash.modification_time", \
"modified", \
KEYWORD_TYPE_INTEGER }, \
{ \
"(SELECT name FROM users WHERE users.id = overrides_trash.owner)", \
"_owner", \
KEYWORD_TYPE_STRING \
}, \
{ "owner", NULL, KEYWORD_TYPE_STRING }, \
/* Columns specific to overrides_trash. */ \
{ "overrides_trash.nvt", "oid", KEYWORD_TYPE_STRING }, \
{ "overrides_trash.text", "text", KEYWORD_TYPE_STRING }, \
{ "overrides_trash.hosts", "hosts", KEYWORD_TYPE_STRING }, \
{ "overrides_trash.port", "port", KEYWORD_TYPE_STRING }, \
{ "severity_to_level (overrides_trash.severity, 1)", \
"threat", \
KEYWORD_TYPE_STRING }, \
{ "severity_to_level (overrides_trash.new_severity, 0)", \
"new_threat", \
KEYWORD_TYPE_STRING }, \
{ "overrides_trash.task", NULL, KEYWORD_TYPE_INTEGER }, \
{ "overrides_trash.result", "result", KEYWORD_TYPE_INTEGER }, \
{ "overrides_trash.end_time", NULL, KEYWORD_TYPE_INTEGER }, \
{ \
"CAST (((overrides_trash.end_time = 0)" \
" OR (overrides_trash.end_time >= m_now ())) AS INTEGER)", \
"active", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(CASE" \
" WHEN overrides_trash.nvt LIKE 'CVE-%%'" \
" THEN overrides_trash.nvt" \
" ELSE (SELECT name FROM nvts WHERE oid = overrides_trash.nvt)" \
" END)", \
"nvt", \
KEYWORD_TYPE_STRING \
}, \
{ "overrides_trash.nvt", "nvt_id", KEYWORD_TYPE_STRING }, \
{ \
"(SELECT uuid FROM tasks WHERE id = overrides_trash.task)", \
"task_id", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT name FROM tasks WHERE id = overrides_trash.task)", \
"task_name", \
KEYWORD_TYPE_STRING \
}, \
{ "overrides_trash.severity", NULL, KEYWORD_TYPE_DOUBLE }, \
{ "overrides_trash.new_severity", NULL, KEYWORD_TYPE_DOUBLE }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of overrides.
*
* @param[in] get GET params.
* @param[in] result Result to limit overrides to, 0 for all.
* @param[in] task If result is > 0, task whose overrides on result to
* include, otherwise task to limit overrides to. 0 for
* all tasks.
* @param[in] nvt NVT to limit overrides to, 0 for all.
*
* @return Total number of overrides in filtered set.
*/
int
override_count (const get_data_t *get, nvt_t nvt, result_t result, task_t task)
{
static const char *filter_columns[] = OVERRIDE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = OVERRIDE_ITERATOR_COLUMNS;
static column_t trash_columns[] = OVERRIDE_ITERATOR_TRASH_COLUMNS;
gchar *result_clause, *filter, *task_id;
int ret;
/* Treat the "task_id" filter keyword as if the task was given in "task". */
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
task_id = filter_term_value (filter ? filter : get->filter, "task_id");
g_free (filter);
if (task_id)
{
find_task_with_permission (task_id, &task, "get_tasks");
g_free (task_id);
}
if (result)
{
gchar *severity_sql;
if (setting_dynamic_severity_int ())
severity_sql = g_strdup_printf ("(SELECT CASE"
" WHEN results.severity"
" > " G_STRINGIFY (SEVERITY_LOG)
" THEN CAST (nvts.cvss_base AS real)"
" ELSE results.severity END"
" FROM results, nvts"
" WHERE (nvts.oid = results.nvt)"
" AND (results.id = %llu))",
result);
else
severity_sql = g_strdup_printf ("(SELECT results.severity"
" FROM results"
" WHERE results.id = %llu)",
result);
result_clause = g_strdup_printf (" AND"
" (result = %llu"
" OR (result = 0 AND nvt ="
" (SELECT results.nvt FROM results"
" WHERE results.id = %llu)))"
" AND (hosts is NULL"
" OR hosts = ''"
" OR hosts_contains (hosts,"
" (SELECT results.host FROM results"
" WHERE results.id = %llu)))"
" AND (port is NULL"
" OR port = ''"
" OR port ="
" (SELECT results.port FROM results"
" WHERE results.id = %llu))"
" AND (severity_matches_ov (%s,"
" severity))"
" AND (task = 0 OR task = %llu)",
result,
result,
result,
result,
severity_sql,
task);
g_free (severity_sql);
}
else if (task)
{
result_clause = g_strdup_printf
(" AND (overrides.task = %llu OR overrides.task = 0)"
" AND nvt IN"
" (SELECT DISTINCT nvt FROM results"
" WHERE results.task = %llu)"
" AND (overrides.result = 0"
" OR (SELECT task FROM results"
" WHERE results.id = overrides.result)"
" = %llu)",
task,
task,
task);
}
else if (nvt)
{
result_clause = g_strdup_printf
(" AND (overrides.nvt"
" = (SELECT oid FROM nvts WHERE nvts.id = %llu))",
nvt);
}
else
result_clause = NULL;
ret = count ("override",
get,
columns,
trash_columns,
filter_columns,
task || nvt,
NULL,
result_clause,
TRUE);
g_free (result_clause);
return ret;
}
/**
* @brief Initialise an override iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
* @param[in] result Result to limit overrides to, 0 for all.
* @param[in] task If result is > 0, task whose overrides on result to
* include, otherwise task to limit overrides to. 0 for
* all tasks.
* @param[in] nvt NVT to limit overrides to, 0 for all.
*
* @return 0 success, 1 failed to find target, 2 failed to find filter,
* -1 error.
*/
int
init_override_iterator (iterator_t* iterator, const get_data_t *get, nvt_t nvt,
result_t result, task_t task)
{
static const char *filter_columns[] = OVERRIDE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = OVERRIDE_ITERATOR_COLUMNS;
static column_t trash_columns[] = OVERRIDE_ITERATOR_TRASH_COLUMNS;
gchar *result_clause, *filter, *task_id;
int ret;
assert (current_credentials.uuid);
assert ((nvt && get->id) == 0);
assert ((task && get->id) == 0);
assert (result ? nvt == 0 : 1);
assert (task ? nvt == 0 : 1);
/* Treat the "task_id" filter keyword as if the task was given in "task". */
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
task_id = filter_term_value (filter ? filter : get->filter, "task_id");
g_free (filter);
if (task_id)
{
find_task_with_permission (task_id, &task, "get_tasks");
g_free (task_id);
}
if (result)
{
gchar *severity_sql;
if (setting_dynamic_severity_int ())
severity_sql = g_strdup_printf ("(SELECT CASE"
" WHEN results.severity"
" > " G_STRINGIFY (SEVERITY_LOG)
" THEN CAST (nvts.cvss_base AS real)"
" ELSE results.severity END"
" FROM results, nvts"
" WHERE (nvts.oid = results.nvt)"
" AND (results.id = %llu))",
result);
else
severity_sql = g_strdup_printf ("(SELECT results.severity"
" FROM results"
" WHERE results.id = %llu)",
result);
result_clause = g_strdup_printf (" AND"
" (result = %llu"
" OR (result = 0 AND nvt ="
" (SELECT results.nvt FROM results"
" WHERE results.id = %llu)))"
" AND (hosts is NULL"
" OR hosts = ''"
" OR hosts_contains (hosts,"
" (SELECT results.host FROM results"
" WHERE results.id = %llu)))"
" AND (port is NULL"
" OR port = ''"
" OR port ="
" (SELECT results.port FROM results"
" WHERE results.id = %llu))"
" AND (severity_matches_ov (%s,"
" severity))"
" AND (task = 0 OR task = %llu)",
result,
result,
result,
result,
severity_sql,
task);
g_free (severity_sql);
}
else if (task)
{
result_clause = g_strdup_printf
(" AND (overrides.task = %llu OR overrides.task = 0)"
" AND nvt IN"
" (SELECT DISTINCT nvt FROM results"
" WHERE results.task = %llu)"
" AND (overrides.result = 0"
" OR (SELECT task FROM results"
" WHERE results.id = overrides.result)"
" = %llu)",
task,
task,
task);
}
else if (nvt)
{
result_clause = g_strdup_printf
(" AND (overrides.nvt = (SELECT oid FROM nvts"
" WHERE nvts.id = %llu))",
nvt);
}
else
result_clause = NULL;
ret = init_get_iterator (iterator,
"override",
get,
columns,
trash_columns,
filter_columns,
task || nvt,
NULL,
result_clause,
TRUE);
g_free (result_clause);
return ret;
}
/**
* @brief Get the NVT OID from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return NVT OID, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (override_iterator_nvt_oid, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the text from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return Text, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (override_iterator_text, GET_ITERATOR_COLUMN_COUNT + 1);
/**
* @brief Get the hosts from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return Hosts, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (override_iterator_hosts, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get the port from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return Port, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (override_iterator_port, GET_ITERATOR_COLUMN_COUNT + 3);
/**
* @brief Get the threat from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return Threat.
*/
const char *
override_iterator_threat (iterator_t *iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
return ret;
}
/**
* @brief Get the threat from an override iterator.
*
* @param[in] iterator Iterator.
*
* @return Threat.
*/
const char *
override_iterator_new_threat (iterator_t *iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
return ret;
}
/**
* @brief Get the task from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return The task associated with the override, or 0 on error.
*/
task_t
override_iterator_task (iterator_t* iterator)
{
if (iterator->done) return 0;
return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
}
/**
* @brief Get the result from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return The result associated with the override, or 0 on error.
*/
result_t
override_iterator_result (iterator_t* iterator)
{
if (iterator->done) return 0;
return (result_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
}
/**
* @brief Get the end time from an override iterator.
*
* @param[in] iterator Iterator.
*
* @return Time until which override applies. 0 for always. 1 means the
* override has been explicitly turned off.
*/
time_t
override_iterator_end_time (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = (time_t) iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
return ret;
}
/**
* @brief Get the active status from an override iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if active, else 0.
*/
int
override_iterator_active (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
return ret;
}
/**
* @brief Get the NVT name from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return NVT name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (override_iterator_nvt_name, GET_ITERATOR_COLUMN_COUNT + 10);
/**
* @brief Get the NVT type from a override iterator.
*
* @param[in] iterator Iterator.
*
* @return NVT type, or NULL. Static string.
*/
const char *
override_iterator_nvt_type (iterator_t *iterator)
{
const char *oid;
oid = override_iterator_nvt_oid (iterator);
if (oid == NULL)
return NULL;
if (g_str_has_prefix (oid, "CVE-"))
return "cve";
if (g_str_has_prefix (oid, "oval:"))
return "ovaldef";
return "nvt";
}
/**
* @brief Get the severity from an override iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity score to which the override applies or NULL if
* iteration is complete, Freed by cleanup_iterator.
*/
DEF_ACCESS (override_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 14);
/**
* @brief Get the new severity from an override iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity score to override to or NULL if
* iteration is complete, Freed by cleanup_iterator.
*/
DEF_ACCESS (override_iterator_new_severity, GET_ITERATOR_COLUMN_COUNT + 15);
/* Scanners */
/**
* @brief Create the given scanner.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] name Name of scanner.
* @param[in] host Host of scanner.
* @param[in] port Port of scanner.
* @param[in] type Type of scanner.
* @param[in] ca_pub_path CA Certificate path.
* @param[in] credential_id UUID of credential to use or NULL to create.
* @param[in] key_pub_path Certificate path.
* @param[in] key_priv_path Private key path.
*
* @return 0 success, -1 error, -2 database is wrong version, -3 database needs
* to be initialised from server.
*/
int
manage_create_scanner (GSList *log_config, const db_conn_info_t *database,
const char *name, const char *host, const char *port,
const char *type, const char *ca_pub_path,
const char *credential_id,
const char *key_pub_path, const char *key_priv_path)
{
int ret;
char *ca_pub, *key_pub, *key_priv;
GError *error = NULL;
credential_t new_credential;
gchar *used_credential_id;
gchar *name_for_credential;
scanner_t scanner;
g_info (" Creating scanner.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
current_credentials.uuid = "";
if (!g_file_get_contents (ca_pub_path, &ca_pub, NULL, &error))
{
fprintf (stderr, "%s.\n", error->message);
g_error_free (error);
manage_option_cleanup ();
return -1;
}
if (credential_id)
{
key_pub = NULL;
key_priv = NULL;
used_credential_id = g_strdup (credential_id);
}
else
{
if (!g_file_get_contents (key_pub_path, &key_pub, NULL, &error))
{
fprintf (stderr, "%s.\n", error->message);
g_error_free (error);
g_free (ca_pub);
manage_option_cleanup ();
return -1;
}
if (!g_file_get_contents (key_priv_path, &key_priv, NULL, &error))
{
fprintf (stderr, "%s.\n", error->message);
g_error_free (error);
g_free (ca_pub);
g_free (key_pub);
manage_option_cleanup ();
return -1;
}
name_for_credential = sql_quote (name);
if (sql_int ("SELECT count(*) FROM credentials"
" WHERE name = 'Credential for Scanner %s'"
" AND owner = NULL;",
name_for_credential))
sql ("INSERT INTO credentials"
" (uuid, name, owner, comment, type,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (),"
" uniquify ('scanner', 'Credential for Scanner %s',"
" NULL, ''),"
" NULL, 'Autogenerated', 'cc',"
" m_now (), m_now ());",
name_for_credential);
else
sql ("INSERT INTO credentials"
" (uuid, name, owner, comment, type,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (), 'Credential for Scanner %s',"
" NULL, 'Autogenerated', 'cc',"
" m_now (), m_now ());",
name_for_credential);
g_free (name_for_credential);
new_credential = sql_last_insert_id();
set_credential_data (new_credential, "certificate", key_pub);
if (disable_encrypted_credentials)
{
set_credential_data (new_credential, "private_key", key_priv);
}
else
{
lsc_crypt_ctx_t crypt_ctx;
char *secret;
crypt_ctx = lsc_crypt_new ();
secret = lsc_crypt_encrypt (crypt_ctx,
"private_key", key_priv, NULL);
if (!secret)
{
fprintf (stderr, "Failed to encrypt private key.\n");
g_free (ca_pub);
g_free (key_pub);
g_free (key_priv);
manage_option_cleanup ();
return -1;
}
set_credential_data (new_credential, "secret", secret);
}
used_credential_id = credential_uuid (new_credential);
}
ret = create_scanner (name, NULL, host, port, type, &scanner, ca_pub,
used_credential_id);
g_free (ca_pub);
g_free (key_pub);
g_free (key_priv);
g_free (used_credential_id);
switch (ret)
{
case 0:
{
gchar *uuid;
uuid = sql_string ("SELECT uuid FROM scanners WHERE id = %llu;",
scanner);
add_role_permission_resource (ROLE_UUID_ADMIN, "GET_SCANNERS",
"scanner", uuid);
add_role_permission_resource (ROLE_UUID_GUEST, "GET_SCANNERS",
"scanner", uuid);
add_role_permission_resource (ROLE_UUID_OBSERVER, "GET_SCANNERS",
"scanner", uuid);
add_role_permission_resource (ROLE_UUID_USER, "GET_SCANNERS",
"scanner", uuid);
g_free (uuid);
}
printf ("Scanner created.\n");
break;
case 1:
fprintf (stderr, "Scanner exists already.\n");
break;
case 2:
fprintf (stderr, "Invalid value provided.\n");
break;
case 3:
fprintf (stderr, "Credential not found.\n");
break;
case 4:
fprintf (stderr, "Credential should be 'up'.\n");
break;
case 5:
fprintf (stderr, "Credential should be 'cc'.\n");
break;
case 6:
fprintf (stderr, "Credential required.\n");
break;
default:
fprintf (stderr, "Failed to create scanner.\n");
break;
}
manage_option_cleanup ();
return ret;
}
/**
* @brief Delete the given scanner.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] uuid UUID of scanner.
*
* @return 0 success, 2 failed to find scanner, 3 scanner can't be deleted,
* -1 error. -2 database is wrong version, -3 database needs to be
* initialised from server.
*/
int
manage_delete_scanner (GSList *log_config, const db_conn_info_t *database,
const gchar *uuid)
{
int ret;
assert (uuid);
g_info (" Deleting scanner.");
if (!strcmp (uuid, SCANNER_UUID_CVE))
{
fprintf (stderr, "Default CVE Scanner can't be deleted.\n");
return 3;
}
if (!strcmp (uuid, SCANNER_UUID_DEFAULT))
{
fprintf (stderr, "Default OpenVAS Scanner can't be deleted.\n");
return 3;
}
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
current_credentials.uuid = "";
switch ((ret = delete_scanner (uuid, 1)))
{
case 0:
printf ("Scanner deleted.\n");
break;
case 1:
fprintf (stderr, "Scanner in use.\n");
break;
case 2:
fprintf (stderr, "Failed to find scanner.\n");
break;
case 3:
fprintf (stderr, "Scanner is predefined.\n");
break;
default:
fprintf (stderr, "Internal Error.\n");
break;
}
current_credentials.uuid = NULL;
manage_option_cleanup ();
return ret;
}
/**
* @brief Modify the given scanner.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] scanner_id ID of scanner.
* @param[in] name Name of scanner.
* @param[in] host Host of scanner.
* @param[in] port Port of scanner.
* @param[in] type Type of scanner.
* @param[in] ca_pub_path CA Certificate path. NULL to leave it as is.
* "" to use the default.
* @param[in] credential_id UUID of credential to use or NULL to create.
* @param[in] key_pub_path Certificate path.
* @param[in] key_priv_path Private key path.
*
* @return 0 success, , 1 failed to find scanner, 2 scanner with new name
* exists, 3 scanner_id required, 4 invalid value, 99 permission
* denied, -1 error, -2 database is wrong version, -3 database needs
* to be initialised from server.
*/
int
manage_modify_scanner (GSList *log_config, const db_conn_info_t *database,
const char *scanner_id, const char *name,
const char *host, const char *port,
const char *type, const char *ca_pub_path,
const char *credential_id,
const char *key_pub_path, const char *key_priv_path)
{
int ret;
char *ca_pub, *key_pub, *key_priv;
GError *error = NULL;
scanner_t scanner;
credential_t new_credential;
gchar *used_credential_id;
gchar *name_for_credential;
g_info (" Modifying scanner.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
current_credentials.uuid = "";
if (scanner_id)
{
/* Because the credentials are empty this will find the scanner regardless
* of permissions and ownership, but that's the intention. */
if (find_scanner_with_permission (scanner_id, &scanner, "get_scanners"))
{
fprintf (stderr, "Error finding scanner.\n");
manage_option_cleanup ();
return -1;
}
if (scanner == 0)
{
fprintf (stderr, "Failed to find scanner %s.\n", scanner_id);
manage_option_cleanup ();
return 1;
}
}
else
{
fprintf (stderr, "Scanner UUID required.\n");
manage_option_cleanup ();
return 3;
}
if (name)
name_for_credential = sql_quote (name);
else
{
gchar *current_scanner_name = scanner_name (scanner);
name_for_credential = sql_quote (current_scanner_name);
g_free (current_scanner_name);
}
if (ca_pub_path)
{
if (*ca_pub_path == '\0')
ca_pub = g_strdup ("");
else if (g_file_get_contents (ca_pub_path, &ca_pub, NULL, &error) == 0)
{
fprintf (stderr, "%s.\n", error->message);
g_error_free (error);
manage_option_cleanup ();
return -1;
}
}
else
ca_pub = NULL;
if (credential_id)
{
key_pub = NULL;
key_priv = NULL;
used_credential_id = g_strdup (credential_id);
}
else
{
if (key_pub_path)
{
if (g_file_get_contents (key_pub_path, &key_pub, NULL, &error) == 0)
{
fprintf (stderr, "%s.\n", error->message);
g_error_free (error);
g_free (ca_pub);
manage_option_cleanup ();
return -1;
}
}
else
key_pub = scanner_key_pub (scanner);
if (key_priv_path)
{
if (!g_file_get_contents (key_priv_path, &key_priv, NULL, &error))
{
fprintf (stderr, "%s.\n", error->message);
g_error_free (error);
g_free (ca_pub);
g_free (key_pub);
manage_option_cleanup ();
return -1;
}
}
else
key_priv = scanner_key_priv (scanner);
if (key_priv || key_pub)
{
if (sql_int ("SELECT count(*) FROM credentials"
" WHERE name = 'Credential for Scanner %s'"
" AND owner IS NULL;",
name_for_credential))
sql ("INSERT INTO credentials"
" (uuid, name, owner, comment, type,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (),"
" uniquify ('scanner', 'Credential for Scanner %s',"
" NULL, ''),"
" NULL, 'Autogenerated', 'cc',"
" m_now (), m_now ());",
name_for_credential);
else
sql ("INSERT INTO credentials"
" (uuid, name, owner, comment, type,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (), 'Credential for Scanner %s',"
" NULL, 'Autogenerated', 'cc',"
" m_now (), m_now ());",
name_for_credential);
g_free (name_for_credential);
new_credential = sql_last_insert_id();
set_credential_data (new_credential, "certificate", key_pub);
if (disable_encrypted_credentials)
{
set_credential_data (new_credential, "private_key", key_priv);
}
else
{
lsc_crypt_ctx_t crypt_ctx;
char *secret;
crypt_ctx = lsc_crypt_new ();
secret = lsc_crypt_encrypt (crypt_ctx,
"private_key", key_priv, NULL);
if (!secret)
{
fprintf (stderr, "Failed to encrypt private key.\n");
g_free (ca_pub);
g_free (key_pub);
g_free (key_priv);
manage_option_cleanup ();
return -1;
}
set_credential_data (new_credential, "secret", secret);
}
used_credential_id = credential_uuid (new_credential);
}
else
used_credential_id = NULL;
}
ret = modify_scanner (scanner_id, name, NULL, host, port, type, ca_pub,
used_credential_id);
g_free (ca_pub);
g_free (key_pub);
g_free (key_priv);
g_free (used_credential_id);
switch (ret)
{
case 0:
printf ("Scanner modified.\n");
break;
case 2:
fprintf (stderr, "Scanner with new name exists already.\n");
break;
case 3:
fprintf (stderr, "Scanner ID required.\n");
break;
case 4:
fprintf (stderr, "Invalid value.\n");
break;
case 5:
fprintf (stderr, "Credential not found.\n");
break;
case 6:
fprintf (stderr, "Credential should be 'cc'.\n");
break;
case 7:
fprintf (stderr, "Credential should be 'up'.\n");
break;
case 8:
fprintf (stderr, "Credential missing.\n");
break;
case 99:
fprintf (stderr, "Permission denied.\n");
break;
default:
fprintf (stderr, "Failed to modify scanner.\n");
break;
}
manage_option_cleanup ();
return ret;
}
/**
* @brief Verify the given scanner.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] uuid UUID of scanner.
*
* @return 0 success, 1 failed to find scanner, 2 failed to verify scanner,
* -1 error. -2 database is wrong version, -3 database needs to be
* initialised from server.
*/
int
manage_verify_scanner (GSList *log_config, const db_conn_info_t *database,
const gchar *uuid)
{
int ret;
char *version;
assert (uuid);
g_info (" Verifying scanner.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
current_credentials.uuid = "";
switch ((ret = verify_scanner (uuid, &version)))
{
case 0:
printf ("Scanner version: %s.\n", version);
g_free (version);
break;
case 1:
fprintf (stderr, "Failed to find scanner.\n");
break;
case 2:
fprintf (stderr, "Failed to verify scanner.\n");
break;
case 3:
fprintf (stderr, "Failed to authenticate. Scanner version: %s\n",
version);
break;
default:
fprintf (stderr, "Internal Error.\n");
break;
}
current_credentials.uuid = NULL;
manage_option_cleanup ();
return ret;
}
/**
* @brief Find a scanner for a specific permission, given a UUID.
*
* @param[in] uuid UUID of scanner.
* @param[out] scanner Scanner return, 0 if successfully failed to find
* scanner.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find scanner),
* TRUE on error.
*/
gboolean
find_scanner_with_permission (const char* uuid, scanner_t* scanner,
const char *permission)
{
return find_resource_with_permission ("scanner", uuid, scanner, permission,
0);
}
/**
* @brief Insert a scanner for create_scanner.
*
* @param[in] name Name of scanner.
* @param[in] comment Comment on scanner.
* @param[in] host Host of scanner.
* @param[in] ca_pub CA Certificate for scanner.
* @param[in] iport Port of scanner.
* @param[in] itype Type of scanner.
* @param[out] new_scanner The created scanner.
*/
static void
insert_scanner (const char* name, const char *comment, const char *host,
const char *ca_pub, int iport, int itype,
scanner_t *new_scanner)
{
char *quoted_name, *quoted_comment, *quoted_host, *quoted_ca_pub;
assert (current_credentials.uuid);
quoted_name = sql_quote (name ?: "");
quoted_comment = sql_quote (comment ?: "");
quoted_host = sql_quote (host ?: "");
quoted_ca_pub = sql_quote (ca_pub ?: "");
sql ("INSERT INTO scanners (uuid, name, owner, comment, host, port, type,"
" ca_pub,creation_time, modification_time)"
" VALUES (make_uuid (), '%s',"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', '%s', %d, %d, %s%s%s, m_now (), m_now ());",
quoted_name, current_credentials.uuid, quoted_comment, quoted_host,
iport, itype,
ca_pub ? "'" : "",
ca_pub ? quoted_ca_pub : "NULL",
ca_pub ? "'" : "");
g_free (quoted_host);
g_free (quoted_comment);
g_free (quoted_name);
g_free (quoted_ca_pub);
if (new_scanner)
*new_scanner = sql_last_insert_id ();
}
/**
* @brief Create a scanner.
*
* @param[in] name Name of scanner.
* @param[in] comment Comment on scanner.
* @param[in] host Host of scanner.
* @param[in] port Port of scanner.
* @param[in] type Type of scanner.
* @param[out] new_scanner The created scanner.
* @param[in] ca_pub CA Certificate for scanner.
* @param[in] credential_id ID of credential for scanner.
*
* @return 0 success, 1 scanner exists already, 2 Invalid value,
* 3 credential not found, 4 credential should be 'up',
* 5 credential should be 'cc', 6 credential required,
* 99 permission denied.
*/
int
create_scanner (const char* name, const char *comment, const char *host,
const char *port, const char *type, scanner_t *new_scanner,
const char *ca_pub, const char *credential_id)
{
int iport, itype, unix_socket = 0;
credential_t credential;
assert (name);
sql_begin_immediate ();
if (acl_user_may ("create_scanner") == 0)
{
sql_rollback ();
return 99;
}
if (!host || !port || !type)
return 2;
if (*host == '/')
{
unix_socket = 1;
ca_pub = NULL;
}
iport = atoi (port);
itype = atoi (type);
if (iport <= 0 || iport > 65535)
return 2;
if (scanner_type_valid (itype) == 0)
return 2;
/* XXX: Workaround for unix socket case. */
if (gvm_get_host_type (host) == -1 && !unix_socket)
return 2;
if (resource_with_name_exists (name, "scanner", 0))
{
sql_rollback ();
return 1;
}
if (unix_socket)
insert_scanner (name, comment, host, ca_pub, iport, itype, new_scanner);
else
{
credential = 0;
if (credential_id
&& strcmp (credential_id, "")
&& strcmp (credential_id, "0"))
{
if (find_credential_with_permission
(credential_id, &credential, "get_credentials"))
{
sql_rollback ();
return -1;
}
if (credential == 0)
{
sql_rollback ();
return 3;
}
if (sql_int ("SELECT type != 'cc' FROM credentials"
" WHERE id = %llu;",
credential))
{
sql_rollback ();
return 5;
}
}
insert_scanner (name, comment, host, ca_pub, iport, itype, new_scanner);
if (credential)
{
sql ("UPDATE scanners SET credential = %llu WHERE id = %llu;",
credential, sql_last_insert_id ());
}
}
sql_commit ();
return 0;
}
/**
* @brief Create a scanner from an existing scanner.
*
* @param[in] name Name of new scanner. NULL to copy from existing.
* @param[in] comment Comment on new scanner. NULL to copy from
* existing.
* @param[in] scanner_id UUID of existing scanner.
* @param[out] new_scanner New scanner.
*
* @return 0 success, 1 scanner exists already, 2 failed to find existing
* scanner, -1 error, 98 not allowed to copy cve scanner,
* 99 permission denied.
*/
int
copy_scanner (const char* name, const char* comment, const char *scanner_id,
scanner_t* new_scanner)
{
if (strcmp (scanner_id, SCANNER_UUID_CVE) == 0)
return 98;
return copy_resource ("scanner", name, comment, scanner_id,
"host, port, type, ca_pub, credential", 1,
new_scanner, NULL);
}
/**
* @brief Modify an scanner.
*
* @param[in] scanner_id UUID of scanner.
* @param[in] name Name of scanner.
* @param[in] comment Comment on scanner.
* @param[in] host Host of scanner.
* @param[in] port Port of scanner.
* @param[in] type Type of scanner.
* @param[in] ca_pub CA Certificate of scanner, or "" for default, or
* to keep existing value.
* @param[in] credential_id UUID of credential or NULL.
*
* @return 0 success, 1 failed to find scanner, 2 scanner with new name exists,
* 3 scanner_id required, 4 invalid value, 5 credential not found,
* 6 credential should be 'cc', 7 credential should be 'up',
* 8 credential missing, 99 permission denied, -1 internal error.
*/
int
modify_scanner (const char *scanner_id, const char *name, const char *comment,
const char *host, const char *port, const char *type,
const char *ca_pub, const char *credential_id)
{
gchar *quoted_name, *quoted_comment, *quoted_host, *new_port, *new_type;
scanner_t scanner = 0;
credential_t credential = 0;
int iport, itype, unix_socket, credential_given;
assert (current_credentials.uuid);
if (scanner_id == NULL)
return 3;
if (port)
{
iport = atoi (port);
if (iport <= 0 || iport > 65535)
return 4;
}
else
/* Keep compiler quiet. */
iport = 0;
if (type)
{
itype = atoi (type);
if (scanner_type_valid (itype) == 0)
return 4;
}
else
itype = 0;
sql_begin_immediate ();
if (acl_user_may ("modify_scanner") == 0)
{
sql_rollback ();
return 99;
}
if (find_scanner_with_permission (scanner_id, &scanner, "modify_scanner"))
{
sql_rollback ();
return -1;
}
if (scanner == 0)
{
sql_rollback ();
return 1;
}
if (host)
{
unix_socket = (*host == '/');
if ((unix_socket == 0) && (gvm_get_host_type (host) == -1))
return 4;
}
else
{
char *old_host = scanner_host (scanner);
unix_socket = (*old_host == '/');
g_free (old_host);
}
if (itype == 0)
itype = sql_int ("SELECT type FROM scanners WHERE id = %llu;", scanner);
if (credential_id
&& (strcmp (credential_id, "") == 0 || strcmp (credential_id, "0") == 0))
{
credential = 0;
credential_given = 1;
}
else if (credential_id && !unix_socket)
{
if (find_credential_with_permission (credential_id, &credential,
"get_credentials"))
{
sql_rollback ();
return -1;
}
if (credential == 0)
{
sql_rollback ();
return 5;
}
credential_given = 1;
}
else
{
credential = 0;
credential_given = 1;
sql_int64 (&credential,
"SELECT credential FROM scanners WHERE id = %llu;",
scanner);
}
if (credential)
{
if (sql_int ("SELECT type != 'cc' FROM credentials WHERE id = %llu;",
credential))
{
sql_rollback ();
return 6;
}
}
/* Check whether a scanner with the same name exists already. */
if (name)
{
if (resource_with_name_exists (name, "scanner", scanner))
{
sql_rollback ();
return 2;
}
}
quoted_name = name ? sql_quote (name) : NULL;
quoted_comment = sql_quote (comment ?: "");
quoted_host = host ? sql_quote (host) : NULL;
new_port = port ? g_strdup_printf ("%d", iport) : g_strdup ("port");
new_type = type ? g_strdup_printf ("%d", itype) : g_strdup ("type");
sql ("UPDATE scanners SET name = %s%s%s, comment = %s%s%s, host = %s%s%s,"
" port = %s, type = %s, modification_time = m_now () WHERE id = %llu;",
quoted_name ? "'" : "",
quoted_name ? quoted_name : "name",
quoted_name ? "'" : "",
quoted_comment ? "'" : "",
quoted_comment ? quoted_comment : "comment",
quoted_comment ? "'" : "",
quoted_host ? "'" : "",
quoted_host ? quoted_host : "host",
quoted_host ? "'" : "",
new_port,
new_type,
scanner);
g_free (new_type);
g_free (new_port);
g_free (quoted_host);
g_free (quoted_comment);
g_free (quoted_name);
if (ca_pub && !unix_socket)
{
if (*ca_pub)
{
char *quoted_ca_pub = sql_quote (ca_pub);
sql ("UPDATE scanners SET ca_pub = '%s' WHERE id = %llu;", quoted_ca_pub,
scanner);
g_free (quoted_ca_pub);
}
else
/* Use default CA cert. */
sql ("UPDATE scanners SET ca_pub = NULL WHERE id = %llu;", scanner);
}
if (credential_given)
{
if (credential)
sql ("UPDATE scanners SET credential = %llu WHERE id = %llu;",
credential, scanner);
else
sql ("UPDATE scanners SET credential = NULL WHERE id = %llu;",
scanner);
}
sql_commit ();
return 0;
}
/**
* @brief Delete a scanner.
*
* @param[in] scanner_id UUID of scanner.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 scanner in use, 2 failed to find scanner,
* 3 predefined scanner, 99 permission denied, -1 error.
*/
int
delete_scanner (const char *scanner_id, int ultimate)
{
scanner_t scanner = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_scanner") == 0)
{
sql_rollback ();
return 99;
}
if (strcmp (scanner_id, SCANNER_UUID_CVE) == 0
|| strcmp (scanner_id, SCANNER_UUID_DEFAULT) == 0)
return 3;
if (find_scanner_with_permission (scanner_id, &scanner, "delete_scanner"))
{
sql_rollback ();
return -1;
}
if (scanner == 0)
{
if (find_trash ("scanner", scanner_id, &scanner))
{
sql_rollback ();
return -1;
}
if (scanner == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
/* Check if it's in use by a config or task in the trashcan. */
if (sql_int ("SELECT count(*) FROM tasks"
" WHERE scanner = %llu"
" AND scanner_location = " G_STRINGIFY (LOCATION_TRASH) ";",
scanner)
|| sql_int ("SELECT count(*) FROM configs_trash"
" WHERE scanner = %llu"
" AND scanner_location"
" = " G_STRINGIFY (LOCATION_TRASH) ";",
scanner))
{
sql_rollback ();
return 1;
}
permissions_set_orphans ("scanner", scanner, LOCATION_TRASH);
tags_remove_resource ("scanner", scanner, LOCATION_TRASH);
sql ("DELETE FROM scanners_trash WHERE id = %llu;", scanner);
sql_commit ();
return 0;
}
if (ultimate == 0)
{
scanner_t trash_scanner;
if (sql_int ("SELECT count(*) FROM tasks"
" WHERE scanner = %llu"
" AND scanner_location = " G_STRINGIFY (LOCATION_TABLE)
" AND hidden = 0;",
scanner)
|| sql_int ("SELECT count(*) FROM configs"
" WHERE scanner = %llu;",
scanner))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO scanners_trash"
" (uuid, owner, name, comment, host, port, type, ca_pub,"
" credential, credential_location,"
" creation_time, modification_time)"
" SELECT uuid, owner, name, comment, host, port, type, ca_pub,"
" credential, " G_STRINGIFY (LOCATION_TABLE) ","
" creation_time, modification_time"
" FROM scanners WHERE id = %llu;", scanner);
trash_scanner = sql_last_insert_id ();
/* Update the location of the scanner in any trashcan configs & tasks. */
sql ("UPDATE configs_trash"
" SET scanner = %llu,"
" scanner_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE scanner = %llu;",
trash_scanner,
scanner);
sql ("UPDATE tasks"
" SET scanner = %llu,"
" scanner_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE scanner = %llu"
" AND scanner_location = " G_STRINGIFY (LOCATION_TABLE) ";",
trash_scanner,
scanner);
permissions_set_locations ("scanner", scanner, sql_last_insert_id (),
LOCATION_TRASH);
tags_set_locations ("scanner", scanner,
sql_last_insert_id (), LOCATION_TRASH);
}
else
{
permissions_set_orphans ("scanner", scanner, LOCATION_TABLE);
tags_remove_resource ("scanner", scanner, LOCATION_TABLE);
}
sql ("DELETE FROM scanners WHERE id = %llu;", scanner);
sql_commit ();
return 0;
}
/**
* @brief Filter columns for scanner iterator.
*/
#define SCANNER_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "host", "port", "type", NULL }
/**
* @brief Scanner iterator columns.
*/
#define SCANNER_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (scanners), \
{ "host", NULL, KEYWORD_TYPE_STRING }, \
{ "port", NULL, KEYWORD_TYPE_INTEGER }, \
{ "type", NULL, KEYWORD_TYPE_INTEGER }, \
{ "ca_pub", NULL, KEYWORD_TYPE_STRING }, \
{ \
"(SELECT name FROM credentials WHERE id = credential)", \
"credential", \
KEYWORD_TYPE_STRING \
}, \
{ "credential", NULL, KEYWORD_TYPE_INTEGER }, \
{ "0", NULL, KEYWORD_TYPE_INTEGER }, \
{ "credential_value (credential, 0, CAST ('certificate' AS TEXT))", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "credential_value (credential, 0, CAST ('private_key' AS TEXT))", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "credential_value (credential, 0, CAST ('secret' AS TEXT))", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ \
"(SELECT type FROM credentials WHERE id = credential)", \
"credential_type", \
KEYWORD_TYPE_STRING \
}, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Scanner iterator columns for trash case.
*/
#define SCANNER_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (scanners_trash), \
{ "host" , NULL, KEYWORD_TYPE_STRING }, \
{ "port" , NULL, KEYWORD_TYPE_INTEGER }, \
{ "type", NULL, KEYWORD_TYPE_INTEGER }, \
{ "ca_pub", NULL, KEYWORD_TYPE_STRING }, \
{ \
"(SELECT CASE" \
" WHEN credential_location = " G_STRINGIFY (LOCATION_TABLE) \
" THEN (SELECT name FROM credentials WHERE id = credential)" \
" ELSE (SELECT name FROM credentials_trash WHERE id = credential)" \
" END)", \
"credential", \
KEYWORD_TYPE_STRING \
}, \
{ "credential", NULL, KEYWORD_TYPE_INTEGER }, \
{ "credential_location", NULL, KEYWORD_TYPE_INTEGER }, \
{ "credential_value (credential, 1, CAST ('certificate' AS TEXT))", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "credential_value (credential, 1, CAST ('private_key' AS TEXT))", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ "credential_value (credential, 1, CAST ('secret' AS TEXT))", \
NULL, \
KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Initialise an scanner iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find scanner, 2 failed to find filter, -1 error.
*/
int
init_scanner_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = SCANNER_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = SCANNER_ITERATOR_COLUMNS;
static column_t trash_columns[] = SCANNER_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator, "scanner", get, columns, trash_columns,
filter_columns, 0, NULL, NULL, TRUE);
}
/**
* @brief Get the host from an scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Host, or NULL if iteration is complete. Freed
* by cleanup_iterator.
*/
DEF_ACCESS (scanner_iterator_host, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the port from an scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Port, or -1 if iteration is complete.
*/
int
scanner_iterator_port (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
return ret;
}
/**
* @brief Get the type from an scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Type, or SCANNER_TYPE_NONE if iteration is complete.
*/
int
scanner_iterator_type (iterator_t* iterator)
{
int ret;
if (iterator->done) return SCANNER_TYPE_NONE;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
return ret;
}
/**
* @brief Get the CA Certificate from a scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return CA Certificate, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (scanner_iterator_ca_pub, GET_ITERATOR_COLUMN_COUNT + 3);
/**
* @brief Get the Credential name from a scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Credential name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (scanner_iterator_credential_name, GET_ITERATOR_COLUMN_COUNT + 4);
/**
* @brief Get the credential of the scanner from a scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Credential of the scanner or 0 if iteration is complete.
*/
credential_t
scanner_iterator_credential (iterator_t *iterator)
{
if (iterator->done)
return 0;
else
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
}
/**
* @brief Get the credential location of the scanner from a scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Location of the credential or NULL if iteration is complete.
*/
int
scanner_iterator_credential_trash (iterator_t *iterator)
{
if (iterator->done)
return 0;
else
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
}
/**
* @brief Get the Scanner Certificate from a scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Scanner Certificate, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (scanner_iterator_key_pub, GET_ITERATOR_COLUMN_COUNT + 7);
/**
* @brief Get the Scanner private key from a scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Scanner private key, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
static const char*
scanner_iterator_key_priv (iterator_t* iterator)
{
const char *private_key;
private_key = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
if (private_key == NULL)
{
const char *secret;
if (!iterator->crypt_ctx)
iterator->crypt_ctx = lsc_crypt_new ();
secret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
private_key = lsc_crypt_get_private_key (iterator->crypt_ctx, secret);
}
return private_key;
}
/**
* @brief Get the Credential type from a scanner iterator.
*
* @param[in] iterator Iterator.
*
* @return Credential type, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (scanner_iterator_credential_type, GET_ITERATOR_COLUMN_COUNT + 10);
/**
* @brief Initialise a scanner config iterator.
*
* @param[in] iterator Iterator.
* @param[in] scanner Scanner.
*/
void
init_scanner_config_iterator (iterator_t* iterator, scanner_t scanner)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (scanner);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_configs"));
available = acl_where_owned ("config", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT id, uuid, name, %s FROM configs"
" WHERE scanner = %llu"
" ORDER BY name ASC;",
with_clause ? with_clause : "",
available,
scanner);
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the UUID from a scanner config iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID, or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (scanner_config_iterator_uuid, 1);
/**
* @brief Get the name from a scanner config iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (scanner_config_iterator_name, 2);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
scanner_config_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 3);
}
/**
* @brief Initialise a scanner task iterator.
*
* @param[in] iterator Iterator.
* @param[in] scanner Scanner.
*/
void
init_scanner_task_iterator (iterator_t* iterator, scanner_t scanner)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (scanner);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_tasks"));
available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT id, uuid, name, %s FROM tasks"
" WHERE scanner = %llu AND hidden = 0"
" ORDER BY name ASC;",
with_clause ? with_clause : "",
available,
scanner);
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the UUID from a scanner task iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID, or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (scanner_task_iterator_uuid, 1);
/**
* @brief Get the name from a scanner task iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (scanner_task_iterator_name, 2);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
scanner_task_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 3);
}
/**
* @brief Check whether an scanner is in use.
*
* @param[in] scanner Scanner.
*
* @return 1 yes, 0 no.
*/
int
scanner_in_use (scanner_t scanner)
{
return !!(sql_int ("SELECT count(*) FROM tasks WHERE scanner = %llu"
" AND hidden = 0;", scanner)
|| sql_int ("SELECT count(*) FROM configs WHERE scanner = %llu",
scanner));
}
/**
* @brief Check whether a trashcan scanner is writable.
*
* @param[in] scanner Scanner.
*
* @return 1 yes, 0 no.
*/
int
trash_scanner_in_use (scanner_t scanner)
{
return !!(sql_int ("SELECT count(*) FROM tasks"
" WHERE scanner = %llu"
" AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
scanner)
|| sql_int ("SELECT count(*) FROM configs_trash"
" WHERE scanner = %llu"
" AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
scanner));
}
/**
* @brief Check whether a scanner is writable.
*
* @param[in] scanner Scanner.
*
* @return 1 yes, 0 no.
*/
int
scanner_writable (scanner_t scanner)
{
return 1;
}
/**
* @brief Check whether a trashcan scanner is writable.
*
* @param[in] scanner Scanner.
*
* @return 1 yes, 0 no.
*/
int
trash_scanner_writable (scanner_t scanner)
{
return trash_scanner_in_use (scanner) == 0;
}
/**
* @brief Return whether a trashcan scanner is readable.
*
* @param[in] scanner Scanner.
*
* @return 1 if readable, else 0.
*/
int
trash_scanner_readable (scanner_t scanner)
{
char *uuid;
scanner_t found = 0;
if (scanner == 0)
return 0;
uuid = scanner_uuid (scanner);
if (find_trash ("scanner", uuid, &found))
{
g_free (uuid);
return 0;
}
g_free (uuid);
return found > 0;
}
/**
* @brief Return the name of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated name if available, else NULL.
*/
char*
scanner_name (scanner_t scanner)
{
return sql_string ("SELECT name FROM scanners WHERE id = %llu;",
scanner);
}
/**
* @brief Return the UUID of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated UUID.
*/
char *
scanner_uuid (scanner_t scanner)
{
return sql_string ("SELECT uuid FROM scanners WHERE id = %llu;",
scanner);
}
/**
* @brief Return the UUID of the default scanner.
*
* @return UUID.
*/
const char *
scanner_uuid_default ()
{
return SCANNER_UUID_DEFAULT;
}
/**
* @brief Return the host of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated host.
*/
char *
scanner_host (scanner_t scanner)
{
return sql_string ("SELECT host FROM scanners WHERE id = %llu;", scanner);
}
/**
* @brief Return the port of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Scanner port, -1 if not found;
*/
int
scanner_port (scanner_t scanner)
{
int port;
char *str;
str = sql_string ("SELECT port FROM scanners WHERE id = %llu;", scanner);
if (!str)
return -1;
port = atoi (str);
g_free (str);
return port;
}
/**
* @brief Return the type of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Scanner type, -1 if not found;
*/
int
scanner_type (scanner_t scanner)
{
int type;
char *str;
str = sql_string ("SELECT type FROM scanners WHERE id = %llu;", scanner);
if (!str)
return -1;
type = atoi (str);
g_free (str);
return type;
}
/**
* @brief Return the CA Certificate of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated CA Certificate.
*/
char *
scanner_ca_pub (scanner_t scanner)
{
return sql_string ("SELECT ca_pub FROM scanners WHERE id = %llu;", scanner);
}
/**
* @brief Return the Certificate of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated Certificate.
*/
char *
scanner_key_pub (scanner_t scanner)
{
if (scanner == 0)
return NULL;
return sql_string ("SELECT value FROM credentials_data"
" WHERE credential = (SELECT credential FROM scanners"
" WHERE id = %llu)"
" AND type = 'certificate';",
scanner);
}
/**
* @brief Return the private key of a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated private key.
*/
char *
scanner_key_priv (scanner_t scanner)
{
gchar *key;
key = sql_string ("SELECT value FROM credentials_data"
" WHERE credential = (SELECT credential FROM scanners"
" WHERE id = %llu)"
" AND type = 'private_key';",
scanner);
if (key == NULL)
{
gchar *secret;
lsc_crypt_ctx_t crypt_ctx;
crypt_ctx = lsc_crypt_new ();
secret = sql_string ("SELECT value FROM credentials_data"
" WHERE credential"
" = (SELECT credential FROM scanners"
" WHERE id = %llu)"
" AND type = 'secret';",
scanner);
key = g_strdup (lsc_crypt_get_private_key (crypt_ctx, secret));
lsc_crypt_release (crypt_ctx);
g_free (secret);
}
return key;
}
/**
* @brief Return the login associated with a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated login if available, else NULL.
*/
char*
scanner_login (scanner_t scanner)
{
return sql_string ("SELECT credentials_data.value"
" FROM scanners, credentials_data"
" WHERE scanners.id = %llu"
" AND credentials_data.credential = scanners.credential"
" AND credentials_data.type = 'username';",
scanner);
}
/**
* @brief Return the password associated with a scanner.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated password if available, else NULL.
*/
char*
scanner_password (scanner_t scanner)
{
gchar *password;
password = sql_string ("SELECT credentials_data.value"
" FROM scanners, credentials_data"
" WHERE scanners.id = %llu"
" AND credentials_data.credential"
" = scanners.credential"
" AND credentials_data.type = 'password';",
scanner);
if (password == NULL)
{
gchar *secret;
lsc_crypt_ctx_t crypt_ctx;
crypt_ctx = lsc_crypt_new ();
secret = sql_string ("SELECT credentials_data.value"
" FROM scanners, credentials_data"
" WHERE scanners.id = %llu"
" AND credentials_data.credential"
" = scanners.credential"
" AND credentials_data.type = 'secret';",
scanner);
password = g_strdup (lsc_crypt_get_password (crypt_ctx, secret));
lsc_crypt_release (crypt_ctx);
g_free (secret);
}
return password;
}
/**
* @brief Return the name of a scanner in the trashcan.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated name if available, else NULL.
*/
char*
trash_scanner_name (scanner_t scanner)
{
return sql_string ("SELECT name FROM scanners_trash WHERE id = %llu;",
scanner);
}
/**
* @brief Return the UUID of a scanner in the trashcan.
*
* @param[in] scanner Scanner.
*
* @return Newly allocated UUID.
*/
char *
trash_scanner_uuid (scanner_t scanner)
{
return sql_string ("SELECT uuid FROM scanners_trash WHERE id = %llu;",
scanner);
}
/**
* @brief Count number of scanners.
*
* @param[in] get GET params.
*
* @return Total number of scanners in filtered set.
*/
int
scanner_count (const get_data_t *get)
{
static const char *extra_columns[] = SCANNER_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = SCANNER_ITERATOR_COLUMNS;
static column_t trash_columns[] = SCANNER_ITERATOR_TRASH_COLUMNS;
return count ("scanner", get, columns, trash_columns, extra_columns,
0, 0, 0, TRUE);
}
/**
* @brief Get the default scanner path or host.
*
* @return Newly allocated scanner path or host.
*/
char *
openvas_default_scanner_host ()
{
return sql_string ("SELECT host FROM scanners WHERE uuid = '%s'",
SCANNER_UUID_DEFAULT);
}
/**
* @brief Create a new connection to an OSP scanner relay.
*
* @param[in] host Original host name or IP address.
* @param[in] port Original port.
* @param[in] ca_pub Original CA certificate.
* @param[in] key_pub Public key for authentication.
* @param[in] key_priv Private key for authentication.
*
* @return New connection if success, NULL otherwise.
*/
static osp_connection_t *
osp_scanner_relay_connect (const char *host, int port, const char *ca_pub,
const char *key_pub, const char *key_priv)
{
int ret, new_port;
gchar *new_host, *new_ca_pub;
osp_connection_t *connection;
new_host = NULL;
new_ca_pub = NULL;
new_port = 0;
ret = slave_get_relay (host,
port,
ca_pub,
"OSP",
&new_host,
&new_port,
&new_ca_pub);
switch (ret)
{
case 0:
break;
case 1:
g_warning ("No relay found for Scanner at %s:%d", host, port);
return NULL;
default:
g_warning ("%s: Error getting relay for Scanner at %s:%d",
__func__, host, port);
return NULL;
}
connection
= osp_connection_new (new_host, new_port, new_ca_pub, key_pub, key_priv);
if (connection == NULL)
{
if (new_port)
g_warning ("Could not connect to relay at %s:%d"
" for Scanner at %s:%d",
new_host, new_port, host, port);
else
g_warning ("Could not connect to relay at %s"
" for Scanner at %s:%d",
new_host, host, port);
}
g_free (new_host);
g_free (new_ca_pub);
return connection;
}
/**
* @brief Create a new connection to an OSP scanner using the scanner data.
*
* @param[in] host Host name or IP address.
* @param[in] port Port.
* @param[in] ca_pub CA certificate.
* @param[in] key_pub Public key.
* @param[in] key_priv Private key.
*
* @return New connection if success, NULL otherwise.
*/
osp_connection_t *
osp_connect_with_data (const char *host,
int port,
const char *ca_pub,
const char *key_pub,
const char *key_priv)
{
osp_connection_t *connection;
int is_unix_socket = (host && *host == '/') ? 1 : 0;
if (is_unix_socket == 0
&& get_relay_mapper_path ())
{
connection
= osp_scanner_relay_connect (host, port, ca_pub, key_pub, key_priv);
}
else
{
connection = osp_connection_new (host, port, ca_pub, key_pub, key_priv);
if (connection == NULL)
{
if (is_unix_socket)
g_warning ("Could not connect to Scanner at %s", host);
else
g_warning ("Could not connect to Scanner at %s:%d", host, port);
}
}
return connection;
}
/**
* @brief Create a new connection to an OSP scanner.
*
* @param[in] scanner Scanner.
*
* @return New connection if success, NULL otherwise.
*/
osp_connection_t *
osp_scanner_connect (scanner_t scanner)
{
int port;
osp_connection_t *connection;
char *host, *ca_pub, *key_pub, *key_priv;
assert (scanner);
host = scanner_host (scanner);
if (host && *host == '/')
{
port = 0;
ca_pub = NULL;
key_pub = NULL;
key_priv = NULL;
}
else
{
port = scanner_port (scanner);
ca_pub = scanner_ca_pub (scanner);
key_pub = scanner_key_pub (scanner);
key_priv = scanner_key_priv (scanner);
}
connection = osp_connect_with_data (host, port, ca_pub, key_pub, key_priv);
g_free (host);
g_free (ca_pub);
g_free (key_pub);
g_free (key_priv);
return connection;
}
/**
* @brief Get an OSP Scanner's get_version info.
*
* @param[in] iterator Scanner object iterator.
* @param[out] s_name Scanner name.
* @param[out] s_ver Scanner version.
* @param[out] d_name Daemon name.
* @param[out] d_ver Daemon version.
* @param[out] p_name Protocol name.
* @param[out] p_ver Protocol version.
*
* @return 0 success, 1 for failure.
*/
int
osp_get_version_from_iterator (iterator_t *iterator, char **s_name,
char **s_ver, char **d_name, char **d_ver,
char **p_name, char **p_ver)
{
osp_connection_t *connection;
assert (iterator);
connection = osp_connect_with_data (scanner_iterator_host (iterator),
scanner_iterator_port (iterator),
scanner_iterator_ca_pub (iterator),
scanner_iterator_key_pub (iterator),
scanner_iterator_key_priv (iterator));
if (!connection)
return 1;
if (osp_get_version (connection, s_name, s_ver, d_name, d_ver, p_name, p_ver))
return 1;
osp_connection_close (connection);
return 0;
}
/**
* @brief Get an OSP Scanner's get_scanner_details info.
*
* @param[in] iterator Scanner object iterator.
* @param[out] desc Scanner description.
* @param[out] params Scanner parameters.
*
* @return 0 success, 1 for failure.
*/
int
osp_get_details_from_iterator (iterator_t *iterator, char **desc,
GSList **params)
{
osp_connection_t *connection;
assert (iterator);
connection = osp_connect_with_data (scanner_iterator_host (iterator),
scanner_iterator_port (iterator),
scanner_iterator_ca_pub (iterator),
scanner_iterator_key_pub (iterator),
scanner_iterator_key_priv (iterator));
if (!connection)
return 1;
if (osp_get_scanner_details (connection, desc, params))
return 1;
osp_connection_close (connection);
return 0;
}
/**
* @brief Verify a scanner.
*
* @param[in] scanner_id Scanner UUID.
* @param[out] version Version returned by the scanner.
*
* @return 0 success, 1 failed to find scanner, 2 failed to get version,
* 3 authentication failed, 99 if permission denied, -1 error.
*/
int
verify_scanner (const char *scanner_id, char **version)
{
get_data_t get;
iterator_t scanner;
if (acl_user_may ("verify_scanner") == 0)
return 99;
memset (&get, '\0', sizeof (get));
get.id = g_strdup (scanner_id);
if (init_scanner_iterator (&scanner, &get) || !next (&scanner))
{
g_free (get.id);
return 1;
}
g_free (get.id);
if (scanner_iterator_type (&scanner) == SCANNER_TYPE_OSP
|| scanner_iterator_type (&scanner) == SCANNER_TYPE_OPENVAS
|| scanner_iterator_type (&scanner) == SCANNER_TYPE_OSP_SENSOR)
{
int ret = osp_get_version_from_iterator (&scanner, NULL, version, NULL,
NULL, NULL, NULL);
cleanup_iterator (&scanner);
if (ret)
return 2;
return 0;
}
else if (scanner_iterator_type (&scanner) == SCANNER_TYPE_CVE)
{
if (version)
*version = g_strdup ("GVM/" GVMD_VERSION);
cleanup_iterator (&scanner);
return 0;
}
assert (0);
return -1;
}
/**
* @brief List scanners.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
*
* @return 0 success, -1 error.
*/
int
manage_get_scanners (GSList *log_config, const db_conn_info_t *database)
{
iterator_t scanners;
int ret;
g_info (" Getting scanners.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
init_iterator (&scanners,
"SELECT uuid, type, host, port, name FROM scanners;");
while (next (&scanners))
{
const char *scanner_id, *scanner_host, *scanner_port, *scanner_name;
scanner_type_t scanner_type;
const char *scanner_type_str;
scanner_id = iterator_string (&scanners, 0);
scanner_type = iterator_int (&scanners, 1);
scanner_host = iterator_string (&scanners, 2);
scanner_port = iterator_string (&scanners, 3);
scanner_name = iterator_string (&scanners, 4);
switch (scanner_type)
{
case SCANNER_TYPE_OSP:
scanner_type_str = "OSP";
break;
case SCANNER_TYPE_OPENVAS:
scanner_type_str = "OpenVAS";
break;
case SCANNER_TYPE_CVE:
scanner_type_str = "CVE";
break;
case SCANNER_TYPE_OSP_SENSOR:
scanner_type_str = "OSP-Sensor";
break;
default:
scanner_type_str = NULL;
}
if (scanner_type_str)
printf ("%s %s %s %s %s\n",
scanner_id,
scanner_type_str,
scanner_host,
scanner_port,
scanner_name);
else
printf ("%s Unknown-%d %s %s %s\n",
scanner_id,
scanner_type,
scanner_host,
scanner_port,
scanner_name);
}
cleanup_iterator (&scanners);
manage_option_cleanup ();
return 0;
}
/* Schedules. */
/**
* @brief Find a schedule for a specific permission, given a UUID.
*
* @param[in] uuid UUID of schedule.
* @param[out] schedule Schedule return, 0 if successfully failed to find schedule.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find schedule), TRUE on error.
*/
gboolean
find_schedule_with_permission (const char* uuid, schedule_t* schedule,
const char *permission)
{
return find_resource_with_permission ("schedule", uuid, schedule, permission, 0);
}
/**
* @brief Create a schedule.
*
* @param[in] name Name of schedule.
* @param[in] comment Comment on schedule.
* @param[in] ical_string iCalendar string. Overrides first_time, period,
* period_months, byday and duration.
* @param[in] zone Timezone.
* @param[out] schedule Created schedule.
* @param[out] error_out Output for iCalendar errors and warnings.
*
* @return 0 success, 1 schedule exists already,
* 3 error in iCal string, 4 error in timezone, 99 permission denied.
*/
int
create_schedule (const char* name, const char *comment,
const char *ical_string, const char* zone,
schedule_t *schedule, gchar **error_out)
{
gchar *quoted_comment, *quoted_name, *quoted_timezone;
gchar *insert_timezone;
int byday_mask;
icalcomponent *ical_component;
icaltimezone *ical_timezone;
gchar *quoted_ical;
time_t first_time, period, period_months, duration;
assert (current_credentials.uuid);
assert (ical_string && strcmp (ical_string, ""));
sql_begin_immediate ();
if (acl_user_may ("create_schedule") == 0)
{
sql_rollback ();
return 99;
}
if (resource_with_name_exists (name, "schedule", 0))
{
sql_rollback ();
return 1;
}
quoted_name = sql_quote (name);
if (zone && strcmp (zone, ""))
insert_timezone = g_strdup (zone);
else
insert_timezone = sql_string ("SELECT timezone FROM users"
" WHERE users.uuid = '%s';",
current_credentials.uuid);
if (insert_timezone == NULL)
insert_timezone = g_strdup ("UTC");
else
{
insert_timezone = g_strstrip (insert_timezone);
if (strcmp (insert_timezone, "") == 0)
{
g_free (insert_timezone);
insert_timezone = g_strdup ("UTC");
}
}
ical_timezone = icalendar_timezone_from_string (insert_timezone);
if (ical_timezone == NULL)
{
g_free (insert_timezone);
return 4;
}
quoted_comment = sql_quote (comment ? comment : "");
quoted_timezone = sql_quote (insert_timezone);
ical_component = icalendar_from_string (ical_string, ical_timezone,
error_out);
if (ical_component == NULL)
{
g_free (quoted_name);
g_free (quoted_comment);
g_free (insert_timezone);
g_free (quoted_timezone);
return 3;
}
quoted_ical = sql_quote (icalcomponent_as_ical_string (ical_component));
first_time = icalendar_first_time_from_vcalendar (ical_component,
ical_timezone);
duration = icalendar_duration_from_vcalendar (ical_component);
icalendar_approximate_rrule_from_vcalendar (ical_component,
&period,
&period_months,
&byday_mask);
sql ("INSERT INTO schedules"
" (uuid, name, owner, comment, first_time, period, period_months,"
" byday, duration, timezone, icalendar,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (), '%s',"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', %i, %i, %i, %i, %i,"
" '%s', '%s',"
" m_now (), m_now ());",
quoted_name, current_credentials.uuid, quoted_comment, first_time,
period, period_months, byday_mask, duration, quoted_timezone,
quoted_ical);
if (schedule)
*schedule = sql_last_insert_id ();
g_free (quoted_name);
g_free (quoted_comment);
g_free (insert_timezone);
g_free (quoted_timezone);
g_free (quoted_ical);
sql_commit ();
return 0;
}
/**
* @brief Create a schedule from an existing schedule.
*
* @param[in] name Name of new schedule. NULL to copy from existing.
* @param[in] comment Comment on new schedule. NULL to copy from
* existing.
* @param[in] schedule_id UUID of existing schedule.
* @param[out] new_schedule New schedule.
*
* @return 0 success, 1 schedule exists already, 2 failed to find existing
* schedule, -1 error.
*/
int
copy_schedule (const char* name, const char* comment, const char *schedule_id,
schedule_t* new_schedule)
{
return copy_resource ("schedule", name, comment, schedule_id,
"first_time, period, period_months, byday, duration,"
" timezone, icalendar",
1, new_schedule, NULL);
}
/**
* @brief Delete a schedule.
*
* @param[in] schedule_id Schedule.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 fail because a task refers to the schedule,
* 2 failed to find schedule, 99 permission denied, -1 error.
*/
int
delete_schedule (const char *schedule_id, int ultimate)
{
schedule_t schedule = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_schedule") == 0)
{
sql_rollback ();
return 99;
}
if (find_schedule_with_permission (schedule_id, &schedule, "delete_schedule"))
{
sql_rollback ();
return -1;
}
if (schedule == 0)
{
if (find_trash ("schedule", schedule_id, &schedule))
{
sql_rollback ();
return -1;
}
if (schedule == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
/* Check if it's in use by a task in the trashcan. */
if (sql_int ("SELECT count(*) FROM tasks"
" WHERE schedule = %llu"
" AND schedule_location = " G_STRINGIFY (LOCATION_TRASH) ";",
schedule))
{
sql_rollback ();
return 1;
}
permissions_set_orphans ("schedule", schedule, LOCATION_TRASH);
tags_remove_resource ("schedule", schedule, LOCATION_TRASH);
sql ("DELETE FROM schedules_trash WHERE id = %llu;", schedule);
sql_commit ();
return 0;
}
if (ultimate == 0)
{
if (sql_int ("SELECT count(*) FROM tasks"
" WHERE schedule = %llu"
" AND schedule_location = " G_STRINGIFY (LOCATION_TABLE)
" AND hidden = 0;",
schedule))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO schedules_trash"
" (uuid, owner, name, comment, first_time, period, period_months,"
" byday, duration, timezone, creation_time,"
" modification_time, icalendar)"
" SELECT uuid, owner, name, comment, first_time, period, period_months,"
" byday, duration, timezone, creation_time,"
" modification_time, icalendar"
" FROM schedules WHERE id = %llu;",
schedule);
/* Update the location of the schedule in any trashcan tasks. */
sql ("UPDATE tasks"
" SET schedule = %llu,"
" schedule_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE schedule = %llu"
" AND schedule_location = " G_STRINGIFY (LOCATION_TABLE) ";",
sql_last_insert_id (),
schedule);
permissions_set_locations ("schedule", schedule,
sql_last_insert_id (),
LOCATION_TRASH);
tags_set_locations ("schedule", schedule,
sql_last_insert_id (),
LOCATION_TRASH);
}
else if (sql_int ("SELECT count(*) FROM tasks"
" WHERE schedule = %llu"
" AND schedule_location = " G_STRINGIFY (LOCATION_TABLE),
schedule))
{
sql_rollback ();
return 1;
}
else
{
permissions_set_orphans ("schedule", schedule, LOCATION_TABLE);
tags_remove_resource ("schedule", schedule, LOCATION_TABLE);
}
sql ("DELETE FROM schedules WHERE id = %llu;", schedule);
sql_commit ();
return 0;
}
/**
* @brief Return whether a schedule is in use by a task.
*
* @param[in] schedule Schedule.
*
* @return 1 if in use, else 0.
*/
int
schedule_in_use (schedule_t schedule)
{
return !!sql_int ("SELECT count(*) FROM tasks WHERE schedule = %llu"
" AND hidden = 0;", schedule);
}
/**
* @brief Return whether a trashcan schedule is in use by a task.
*
* @param[in] schedule schedule.
*
* @return 1 if in use, else 0.
*/
int
trash_schedule_in_use (schedule_t schedule)
{
return !!sql_int ("SELECT count(*) FROM tasks"
" WHERE schedule = %llu"
" AND schedule_location = " G_STRINGIFY (LOCATION_TRASH),
schedule);
}
/**
* @brief Return whether a schedule is writable.
*
* @param[in] schedule Schedule.
*
* @return 1 if writable, else 0.
*/
int
schedule_writable (schedule_t schedule)
{
return 1;
}
/**
* @brief Return whether a trashcan schedule is writable.
*
* @param[in] schedule Schedule.
*
* @return 1 if writable, else 0.
*/
int
trash_schedule_writable (schedule_t schedule)
{
return trash_schedule_in_use (schedule) == 0;
}
/**
* @brief Return whether a trashcan schedule is readable.
*
* @param[in] schedule Schedule.
*
* @return 1 if readable, else 0.
*/
int
trash_schedule_readable (schedule_t schedule)
{
char *uuid;
schedule_t found = 0;
if (schedule == 0)
return 0;
uuid = schedule_uuid (schedule);
if (find_trash ("schedule", uuid, &found))
{
g_free (uuid);
return 0;
}
g_free (uuid);
return found > 0;
}
/**
* @brief Return the UUID of a schedule.
*
* @param[in] schedule Schedule.
*
* @return Newly allocated UUID.
*/
char *
schedule_uuid (schedule_t schedule)
{
return sql_string ("SELECT uuid FROM schedules WHERE id = %llu;",
schedule);
}
/**
* @brief Return the UUID of a trash schedule.
*
* @param[in] schedule Schedule.
*
* @return Newly allocated UUID.
*/
char *
trash_schedule_uuid (schedule_t schedule)
{
return sql_string ("SELECT uuid FROM schedules_trash WHERE id = %llu;",
schedule);
}
/**
* @brief Return the name of a schedule.
*
* @param[in] schedule Schedule.
*
* @return Newly allocated name.
*/
char *
schedule_name (schedule_t schedule)
{
return sql_string ("SELECT name FROM schedules WHERE id = %llu;",
schedule);
}
/**
* @brief Return the name of a trash schedule.
*
* @param[in] schedule Schedule.
*
* @return Newly allocated name.
*/
char *
trash_schedule_name (schedule_t schedule)
{
return sql_string ("SELECT name FROM schedules_trash WHERE id = %llu;",
schedule);
}
/**
* @brief Return the period of a schedule.
*
* @param[in] schedule Schedule.
*
* @return Period in seconds.
*/
int
schedule_period (schedule_t schedule)
{
return sql_int ("SELECT period FROM schedules WHERE id = %llu;",
schedule);
}
/**
* @brief Return the duration of a schedule.
*
* @param[in] schedule Schedule.
*
* @return Duration in seconds.
*/
int
schedule_duration (schedule_t schedule)
{
return sql_int ("SELECT duration FROM schedules WHERE id = %llu;",
schedule);
}
/**
* @brief Return info about a schedule.
*
* @param[in] schedule Schedule.
* @param[in] trash Whether to get schedule from trash.
* @param[out] icalendar iCalendar string.
* @param[out] zone Timezone string.
*
* @return 0 success, -1 error.
*/
int
schedule_info (schedule_t schedule, int trash, gchar **icalendar, gchar **zone)
{
iterator_t schedules;
init_iterator (&schedules,
"SELECT icalendar, timezone FROM schedules%s"
" WHERE id = %llu;",
trash ? "_trash" : "",
schedule);
if (next (&schedules))
{
*icalendar = g_strdup (iterator_string (&schedules, 0));
*zone = g_strdup (iterator_string (&schedules, 1));
cleanup_iterator (&schedules);
return 0;
}
cleanup_iterator (&schedules);
return -1;
}
/**
* @brief Filter columns for schedule iterator.
*/
#define SCHEDULE_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "first_time", "period", "period_months", \
"duration", "timezone", "first_run", "next_run", NULL }
/**
* @brief Schedule iterator columns.
*/
#define SCHEDULE_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (schedules), \
{ "first_time", NULL, KEYWORD_TYPE_INTEGER }, \
{ "period", NULL, KEYWORD_TYPE_INTEGER }, \
{ "period_months", NULL, KEYWORD_TYPE_INTEGER }, \
{ "duration", NULL, KEYWORD_TYPE_INTEGER }, \
{ "timezone", NULL, KEYWORD_TYPE_STRING }, \
{ "icalendar", NULL, KEYWORD_TYPE_STRING }, \
{ "next_time_ical (icalendar, timezone)", \
"next_run", \
KEYWORD_TYPE_INTEGER }, \
{ "first_time", "first_run", KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Schedule iterator columns for trash case.
*/
#define SCHEDULE_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (schedules_trash), \
{ "first_time", NULL, KEYWORD_TYPE_INTEGER }, \
{ "period", NULL, KEYWORD_TYPE_INTEGER }, \
{ "period_months", NULL, KEYWORD_TYPE_INTEGER }, \
{ "duration", NULL, KEYWORD_TYPE_INTEGER }, \
{ "timezone", NULL, KEYWORD_TYPE_STRING }, \
{ "icalendar", NULL, KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count the number of schedules.
*
* @param[in] get GET params.
*
* @return Total number of schedules filtered set.
*/
int
schedule_count (const get_data_t *get)
{
static const char *filter_columns[] = SCHEDULE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = SCHEDULE_ITERATOR_COLUMNS;
static column_t trash_columns[] = SCHEDULE_ITERATOR_TRASH_COLUMNS;
return count ("schedule", get, columns, trash_columns, filter_columns,
0, 0, 0, TRUE);
}
/**
* @brief Initialise a schedule iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find filter, 2 failed to find"
* filter (filt_id), -1 error.
*/
int
init_schedule_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = SCHEDULE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = SCHEDULE_ITERATOR_COLUMNS;
static column_t trash_columns[] = SCHEDULE_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"schedule",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Get the timezone from a schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Timezone, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (schedule_iterator_timezone, GET_ITERATOR_COLUMN_COUNT + 4);
/**
* @brief Get the iCalendar string from a schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return The iCalendar string or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (schedule_iterator_icalendar, GET_ITERATOR_COLUMN_COUNT + 5);
/**
* @brief Initialise a task schedule iterator.
*
* Lock the database before initialising.
*
* @param[in] iterator Iterator.
*
* @return 0 success, 1 failed to get lock, -1 error.
*/
int
init_task_schedule_iterator (iterator_t* iterator)
{
int ret;
ret = sql_begin_immediate_giveup ();
if (ret)
return ret;
init_iterator (iterator,
"SELECT tasks.id, tasks.uuid,"
" schedules.id, tasks.schedule_next_time,"
" schedules.icalendar, schedules.timezone,"
" schedules.duration,"
" users.uuid, users.name"
" FROM tasks, schedules, users"
" WHERE tasks.schedule = schedules.id"
" AND tasks.hidden = 0"
" AND (tasks.owner = (users.id))"
/* Sort by task and prefer owner of task or schedule as user */
" ORDER BY tasks.id,"
" (users.id = tasks.owner) DESC,"
" (users.id = schedules.owner) DESC;");
return 0;
}
/**
* @brief Cleanup a task schedule iterator.
*
* @param[in] iterator Iterator.
*/
void
cleanup_task_schedule_iterator (iterator_t* iterator)
{
cleanup_iterator (iterator);
sql_commit ();
}
/**
* @brief Get the task from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return task.
*/
task_t
task_schedule_iterator_task (iterator_t* iterator)
{
if (iterator->done) return 0;
return (task_t) iterator_int64 (iterator, 0);
}
/**
* @brief Get the task UUID from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Task UUID, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (task_schedule_iterator_task_uuid, 1);
/**
* @brief Get the next time from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Next time.
*/
static time_t
task_schedule_iterator_next_time (iterator_t* iterator)
{
if (iterator->done) return 0;
return (time_t) iterator_int64 (iterator, 3);
}
/**
* @brief Get the iCalendar string from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return period.
*/
DEF_ACCESS (task_schedule_iterator_icalendar, 4);
/**
* @brief Get the timezone from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Timezone, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (task_schedule_iterator_timezone, 5);
/**
* @brief Get the next time from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Next time.
*/
static time_t
task_schedule_iterator_duration (iterator_t* iterator)
{
if (iterator->done) return 0;
return (time_t) iterator_int64 (iterator, 6);
}
/**
* @brief Get the task owner uuid from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Owner UUID, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (task_schedule_iterator_owner_uuid, 7);
/**
* @brief Get the task owner name from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Owner name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (task_schedule_iterator_owner_name, 8);
/**
* @brief Get the start due state from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Start due flag.
*/
gboolean
task_schedule_iterator_start_due (iterator_t* iterator)
{
task_status_t run_status;
time_t start_time;
if (iterator->done) return FALSE;
if (task_schedule_iterator_next_time (iterator) == 0)
return FALSE;
run_status = task_run_status (task_schedule_iterator_task (iterator));
start_time = task_schedule_iterator_next_time (iterator);
if ((run_status == TASK_STATUS_DONE
|| run_status == TASK_STATUS_INTERRUPTED
|| run_status == TASK_STATUS_NEW
|| run_status == TASK_STATUS_STOPPED)
&& (start_time > 0)
&& (start_time <= time (NULL)))
return TRUE;
return FALSE;
}
/**
* @brief Get the stop due state from a task schedule iterator.
*
* @param[in] iterator Iterator.
*
* @return Stop due flag.
*/
gboolean
task_schedule_iterator_stop_due (iterator_t* iterator)
{
const char *icalendar, *zone;
time_t duration;
if (iterator->done) return FALSE;
icalendar = task_schedule_iterator_icalendar (iterator);
zone = task_schedule_iterator_timezone (iterator);
duration = task_schedule_iterator_duration (iterator);
if (duration)
{
report_t report;
task_status_t run_status;
report = task_running_report (task_schedule_iterator_task (iterator));
if (report && (report_scheduled (report) == 0))
return FALSE;
run_status = task_run_status (task_schedule_iterator_task (iterator));
if (run_status == TASK_STATUS_RUNNING
|| run_status == TASK_STATUS_REQUESTED
|| run_status == TASK_STATUS_QUEUED)
{
time_t now, start;
now = time (NULL);
start = icalendar_next_time_from_string (icalendar, zone, -1);
if ((start + duration) < now)
return TRUE;
}
}
return FALSE;
}
/**
* @brief Get if schedule of task in iterator is timed out.
*
* @param[in] iterator Iterator.
*
* @return Whether task schedule is timed out.
*/
gboolean
task_schedule_iterator_timed_out (iterator_t* iterator)
{
time_t schedule_timeout_secs;
int duration;
task_status_t run_status;
time_t start_time, timeout_time;
if (get_schedule_timeout () < 0)
return FALSE;
if (iterator->done) return FALSE;
start_time = task_schedule_iterator_next_time (iterator);
if (start_time == 0)
return FALSE;
schedule_timeout_secs = get_schedule_timeout () * 60;
if (schedule_timeout_secs < SCHEDULE_TIMEOUT_MIN_SECS)
schedule_timeout_secs = SCHEDULE_TIMEOUT_MIN_SECS;
run_status = task_run_status (task_schedule_iterator_task (iterator));
duration = task_schedule_iterator_duration (iterator);
if (duration && (duration < schedule_timeout_secs))
timeout_time = start_time + duration;
else
timeout_time = start_time + schedule_timeout_secs;
if ((run_status == TASK_STATUS_DONE
|| run_status == TASK_STATUS_INTERRUPTED
|| run_status == TASK_STATUS_NEW
|| run_status == TASK_STATUS_STOPPED)
&& (timeout_time <= time (NULL)))
return TRUE;
return FALSE;
}
/**
* @brief Initialise a schedule task iterator.
*
* @param[in] iterator Iterator.
* @param[in] schedule Schedule.
*/
void
init_schedule_task_iterator (iterator_t* iterator, schedule_t schedule)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (current_credentials.uuid);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_tasks"));
available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT id, uuid, name, %s FROM tasks"
" WHERE schedule = %llu AND hidden = 0"
" ORDER BY name ASC;",
with_clause ? with_clause : "",
available,
schedule,
current_credentials.uuid);
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the UUID from a schedule task iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (schedule_task_iterator_uuid, 1);
/**
* @brief Get the name from a schedule task iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (schedule_task_iterator_name, 2);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
schedule_task_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 3);
}
/**
* @brief Modify a schedule.
*
* @param[in] schedule_id UUID of schedule.
* @param[in] name Name of schedule.
* @param[in] comment Comment on schedule.
* @param[in] ical_string iCalendar string. Overrides first_time, period,
* period_months, byday and duration.
* @param[in] zone Timezone.
* @param[out] error_out Output for iCalendar errors and warnings.
*
* @return 0 success, 1 failed to find schedule, 2 schedule with new name exists,
* 3 error in type name, 4 schedule_id required,
* 6 error in iCalendar, 7 error in zone,
* 99 permission denied, -1 internal error.
*/
int
modify_schedule (const char *schedule_id, const char *name, const char *comment,
const char *ical_string, const char *zone, gchar **error_out)
{
gchar *quoted_name, *quoted_comment, *quoted_timezone, *quoted_icalendar;
icalcomponent *ical_component;
icaltimezone *ical_timezone;
schedule_t schedule;
time_t new_next_time, ical_first_time, ical_duration;
gchar *real_timezone;
assert (ical_string && strcmp (ical_string, ""));
if (schedule_id == NULL)
return 4;
sql_begin_immediate ();
assert (current_credentials.uuid);
if (acl_user_may ("modify_schedule") == 0)
{
sql_rollback ();
return 99;
}
schedule = 0;
if (find_schedule_with_permission (schedule_id, &schedule, "modify_schedule"))
{
sql_rollback ();
return -1;
}
if (schedule == 0)
{
sql_rollback ();
return 1;
}
/* Check whether a schedule with the same name exists already. */
if (name)
{
if (resource_with_name_exists (name, "schedule", schedule))
{
sql_rollback ();
return 2;
}
quoted_name = sql_quote (name);
}
else
quoted_name = NULL;
/* Update basic data */
quoted_comment = comment ? sql_quote (comment) : NULL;
if (zone == NULL)
quoted_timezone = NULL;
else
{
quoted_timezone = g_strstrip (sql_quote (zone));
if (strcmp (quoted_timezone, "") == 0)
{
g_free (quoted_timezone);
quoted_timezone = NULL;
}
}
sql ("UPDATE schedules SET"
" name = %s%s%s,"
" comment = %s%s%s,"
" timezone = %s%s%s"
" WHERE id = %llu;",
quoted_name ? "'" : "",
quoted_name ? quoted_name : "name",
quoted_name ? "'" : "",
quoted_comment ? "'" : "",
quoted_comment ? quoted_comment : "comment",
quoted_comment ? "'" : "",
quoted_timezone ? "'" : "",
quoted_timezone ? quoted_timezone : "timezone",
quoted_timezone ? "'" : "",
schedule);
real_timezone
= sql_string ("SELECT timezone FROM schedules WHERE id = %llu;",
schedule);
/* Update times */
ical_timezone = icalendar_timezone_from_string (real_timezone);
if (ical_timezone == NULL)
{
g_free (real_timezone);
sql_rollback ();
return 7;
}
ical_component = icalendar_from_string (ical_string, ical_timezone,
error_out);
if (ical_component == NULL)
{
g_free (real_timezone);
sql_rollback ();
return 6;
}
quoted_icalendar = sql_quote (icalcomponent_as_ical_string
(ical_component));
ical_first_time = icalendar_first_time_from_vcalendar (ical_component,
ical_timezone);
ical_duration = icalendar_duration_from_vcalendar (ical_component);
sql ("UPDATE schedules SET"
" icalendar = '%s',"
" first_time = %ld,"
" period = 0,"
" period_months = 0,"
" byday = 0,"
" duration = %d,"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_icalendar,
ical_first_time,
ical_duration,
schedule);
// Update scheduled next times for tasks
new_next_time = icalendar_next_time_from_vcalendar (ical_component,
real_timezone, 0);
sql ("UPDATE tasks SET schedule_next_time = %ld"
" WHERE schedule = %llu;",
new_next_time,
schedule);
g_free (quoted_comment);
g_free (quoted_name);
g_free (quoted_timezone);
g_free (quoted_icalendar);
free (real_timezone);
icalcomponent_free (ical_component);
sql_commit ();
return 0;
}
/* Groups. */
/**
* @brief Find a group for a specific permission, given a UUID.
*
* @param[in] uuid UUID of group.
* @param[out] group Group return, 0 if successfully failed to find group.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find group), TRUE on error.
*/
static gboolean
find_group_with_permission (const char* uuid, group_t* group,
const char *permission)
{
return find_resource_with_permission ("group", uuid, group, permission, 0);
}
/**
* @brief Create a group from an existing group.
*
* @param[in] name Name of new group. NULL to copy from existing.
* @param[in] comment Comment on new group. NULL to copy from existing.
* @param[in] group_id UUID of existing group.
* @param[out] new_group_return New group.
*
* @return 0 success, 1 group exists already, 2 failed to find existing
* group, 99 permission denied, -1 error.
*/
int
copy_group (const char *name, const char *comment, const char *group_id,
group_t *new_group_return)
{
int ret;
group_t new, old;
sql_begin_immediate ();
ret = copy_resource_lock ("group", name, comment, group_id, NULL, 1, &new,
&old);
if (ret)
{
sql_rollback ();
return ret;
}
sql ("INSERT INTO group_users (\"group\", \"user\")"
" SELECT %llu, \"user\" FROM group_users"
" WHERE \"group\" = %llu;",
new,
old);
sql_commit ();
if (new_group_return)
*new_group_return = new;
return 0;
}
/**
* @brief Add users to a group.
*
* Caller must take care of transaction.
*
* @param[in] type Type.
* @param[in] resource Group or role.
* @param[in] users List of users.
*
* @return 0 success, 2 failed to find user, 4 user name validation failed,
* 99 permission denied, -1 error.
*/
static int
add_users (const gchar *type, resource_t resource, const char *users)
{
if (users)
{
gchar **split, **point;
GList *added;
/* Add each user. */
added = NULL;
split = g_strsplit_set (users, " ,", 0);
point = split;
while (*point)
{
user_t user;
gchar *name;
name = *point;
g_strstrip (name);
if (strcmp (name, "") == 0)
{
point++;
continue;
}
if (g_list_find_custom (added, name, (GCompareFunc) strcmp))
{
point++;
continue;
}
added = g_list_prepend (added, name);
if (user_exists (name) == 0)
{
g_list_free (added);
g_strfreev (split);
return 2;
}
if (find_user_by_name (name, &user))
{
g_list_free (added);
g_strfreev (split);
return -1;
}
if (user == 0)
{
gchar *uuid;
if (validate_username (name))
{
g_list_free (added);
g_strfreev (split);
return 4;
}
uuid = user_uuid_any_method (name);
if (uuid == NULL)
{
g_list_free (added);
g_strfreev (split);
return -1;
}
if (sql_int ("SELECT count(*) FROM users WHERE uuid = '%s';",
uuid)
== 0)
{
gchar *quoted_name;
quoted_name = sql_quote (name);
sql ("INSERT INTO users"
" (uuid, name, creation_time, modification_time)"
" VALUES"
" ('%s', '%s', m_now (), m_now ());",
uuid,
quoted_name);
g_free (quoted_name);
user = sql_last_insert_id ();
}
else
{
/* find_user_by_name should have found it. */
assert (0);
g_free (uuid);
g_list_free (added);
g_strfreev (split);
return -1;
}
g_free (uuid);
}
if (find_user_by_name_with_permission (name, &user, "get_users"))
{
g_list_free (added);
g_strfreev (split);
return -1;
}
if (user == 0)
{
g_list_free (added);
g_strfreev (split);
return 99;
}
sql ("INSERT INTO %s_users (\"%s\", \"user\") VALUES (%llu, %llu);",
type,
type,
resource,
user);
point++;
}
g_list_free (added);
g_strfreev (split);
}
return 0;
}
/**
* @brief Create a group.
*
* @param[in] group_name Group name.
* @param[in] comment Comment on group.
* @param[in] users Users group applies to.
* @param[in] special_full Whether to give group super on itself (full
* sharing between members).
* @param[out] group Group return.
*
* @return 0 success, 1 group exists already, 2 failed to find user, 4 user
* name validation failed, 99 permission denied, -1 error.
*/
int
create_group (const char *group_name, const char *comment, const char *users,
int special_full, group_t* group)
{
int ret;
gchar *quoted_group_name, *quoted_comment;
assert (current_credentials.uuid);
assert (group_name);
assert (group);
sql_begin_immediate ();
if (acl_user_may ("create_group") == 0)
{
sql_rollback ();
return 99;
}
if (resource_with_name_exists (group_name, "group", 0))
{
sql_rollback ();
return 1;
}
quoted_group_name = sql_quote (group_name);
quoted_comment = comment ? sql_quote (comment) : g_strdup ("");
sql ("INSERT INTO groups"
" (uuid, name, owner, comment, creation_time, modification_time)"
" VALUES"
" (make_uuid (), '%s',"
" (SELECT id FROM users WHERE uuid = '%s'),"
" '%s', m_now (), m_now ());",
quoted_group_name,
current_credentials.uuid,
quoted_comment);
g_free (quoted_comment);
g_free (quoted_group_name);
*group = sql_last_insert_id ();
ret = add_users ("group", *group, users);
if (ret)
sql_rollback ();
else
{
if (special_full)
{
char *group_id;
group_id = group_uuid (*group);
ret = create_permission_internal (1, "Super", NULL, "group", group_id,
"group", group_id, NULL);
g_free (group_id);
if (ret)
{
sql_rollback ();
return ret;
}
}
sql_commit ();
}
return ret;
}
/**
* @brief Delete a group.
*
* @param[in] group_id UUID of group.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 fail because a permission refers to the group, 2 failed
* to find group, 3 predefined group, 99 permission denied, -1 error.
*/
int
delete_group (const char *group_id, int ultimate)
{
group_t group = 0;
GArray *affected_users;
iterator_t users_iter;
sql_begin_immediate ();
if (acl_user_may ("delete_group") == 0)
{
sql_rollback ();
return 99;
}
if (find_group_with_permission (group_id, &group, "delete_group"))
{
sql_rollback ();
return -1;
}
if (group == 0)
{
if (find_trash ("group", group_id, &group))
{
sql_rollback ();
return -1;
}
if (group == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
if (trash_group_in_use (group))
{
sql_rollback ();
return 1;
}
sql ("DELETE FROM permissions"
" WHERE resource_type = 'group'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
group);
sql ("DELETE FROM permissions_trash"
" WHERE resource_type = 'group'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
group);
sql ("DELETE FROM permissions"
" WHERE subject_type = 'group'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
group);
sql ("DELETE FROM permissions_trash"
" WHERE subject_type = 'group'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
group);
tags_remove_resource ("group", group, LOCATION_TRASH);
sql ("DELETE FROM group_users_trash WHERE \"group\" = %llu;", group);
sql ("DELETE FROM groups_trash WHERE id = %llu;", group);
sql_commit ();
return 0;
}
if (group_in_use (group))
{
sql_rollback ();
return 1;
}
if (ultimate == 0)
{
group_t trash_group;
sql ("INSERT INTO groups_trash"
" (uuid, owner, name, comment, creation_time, modification_time)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time"
" FROM groups WHERE id = %llu;",
group);
trash_group = sql_last_insert_id ();
sql ("INSERT INTO group_users_trash"
" (\"group\", \"user\")"
" SELECT %llu, \"user\""
" FROM group_users WHERE \"group\" = %llu;",
trash_group,
group);
permissions_set_locations ("group", group, trash_group, LOCATION_TRASH);
tags_set_locations ("group", group, trash_group, LOCATION_TRASH);
permissions_set_subjects ("group", group, trash_group, LOCATION_TRASH);
}
else
{
sql ("DELETE FROM permissions"
" WHERE resource_type = 'group'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
group);
sql ("DELETE FROM permissions_trash"
" WHERE resource_type = 'group'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
group);
sql ("DELETE FROM permissions"
" WHERE subject_type = 'group'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
group);
sql ("DELETE FROM permissions_trash"
" WHERE subject_type = 'group'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
group);
}
tags_remove_resource ("group", group, LOCATION_TABLE);
affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
init_iterator (&users_iter,
"SELECT \"user\" FROM group_users"
" WHERE \"group\" = %llu",
group);
while (next (&users_iter))
{
user_t user = iterator_int64 (&users_iter, 0);
g_array_append_val (affected_users, user);
}
cleanup_iterator (&users_iter);
sql ("DELETE FROM group_users WHERE \"group\" = %llu;", group);
sql ("DELETE FROM groups WHERE id = %llu;", group);
cache_all_permissions_for_users (affected_users);
g_array_free (affected_users, TRUE);
sql_commit ();
return 0;
}
/**
* @brief Return the UUID of a group.
*
* @param[in] group Group.
*
* @return Newly allocated UUID if available, else NULL.
*/
char*
group_uuid (group_t group)
{
return sql_string ("SELECT uuid FROM groups WHERE id = %llu;",
group);
}
/**
* @brief Gets users of group as a string.
*
* @param[in] group Group.
*
* @return Users.
*/
gchar *
group_users (group_t group)
{
return sql_string ("SELECT group_concat (name, ', ')"
" FROM (SELECT users.name FROM users, group_users"
" WHERE group_users.\"group\" = %llu"
" AND group_users.user = users.id"
" GROUP BY users.name)"
" AS sub;",
group);
}
/**
* @brief Check whether a group is writable.
*
* @param[in] group Group.
*
* @return 1 yes, 0 no.
*/
int
group_writable (group_t group)
{
return 1;
}
/**
* @brief Check whether a trashcan group is writable.
*
* @param[in] group Group.
*
* @return 1 yes, 0 no.
*/
int
trash_group_writable (group_t group)
{
return 1;
}
/**
* @brief Check whether a group is in use.
*
* @param[in] group Group.
*
* @return 1 yes, 0 no.
*/
int
group_in_use (group_t group)
{
return 0;
}
/**
* @brief Check whether a trashcan group is in use.
*
* @param[in] group Group.
*
* @return 1 yes, 0 no.
*/
int
trash_group_in_use (group_t group)
{
return 0;
}
/**
* @brief Filter columns for group iterator.
*/
#define GROUP_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, NULL }
/**
* @brief Group iterator columns.
*/
#define GROUP_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (groups), \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Group iterator columns for trash case.
*/
#define GROUP_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (groups_trash), \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of groups.
*
* @param[in] get GET params.
*
* @return Total number of groups in grouped set.
*/
int
group_count (const get_data_t *get)
{
static const char *filter_columns[] = GROUP_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = GROUP_ITERATOR_COLUMNS;
static column_t trash_columns[] = GROUP_ITERATOR_TRASH_COLUMNS;
return count ("group", get, columns, trash_columns, filter_columns,
0, 0, 0, TRUE);
}
/**
* @brief Initialise a group iterator, including observed groups.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find group, 2 failed to find group (filt_id),
* -1 error.
*/
int
init_group_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = GROUP_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = GROUP_ITERATOR_COLUMNS;
static column_t trash_columns[] = GROUP_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"group",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Modify a group.
*
* @param[in] group_id UUID of group.
* @param[in] name Name of group.
* @param[in] comment Comment on group.
* @param[in] users Group users.
*
* @return 0 success, 1 failed to find group, 2 failed to find user, 3 group_id
* required, 4 user name validation failed, 5 group with new name
* exists, 99 permission denied, -1 internal error.
*/
int
modify_group (const char *group_id, const char *name, const char *comment,
const char *users)
{
int ret;
gchar *quoted_name, *quoted_comment;
group_t group;
GArray *affected_users;
iterator_t users_iter;
assert (current_credentials.uuid);
if (group_id == NULL)
return 3;
sql_begin_immediate ();
if (acl_user_may ("modify_group") == 0)
{
sql_rollback ();
return 99;
}
group = 0;
if (find_group_with_permission (group_id, &group, "modify_group"))
{
sql_rollback ();
return -1;
}
if (group == 0)
{
sql_rollback ();
return 1;
}
/* Check whether a group with the same name exists already. */
if (name)
{
if (resource_with_name_exists (name, "group", group))
{
sql_rollback ();
return 5;
}
}
quoted_name = sql_quote(name ?: "");
quoted_comment = sql_quote (comment ? comment : "");
sql ("UPDATE groups SET"
" name = '%s',"
" comment = '%s',"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_name,
quoted_comment,
group);
g_free (quoted_comment);
g_free (quoted_name);
affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
init_iterator (&users_iter,
"SELECT \"user\" FROM group_users"
" WHERE \"group\" = %llu",
group);
while (next (&users_iter))
{
user_t user = iterator_int64 (&users_iter, 0);
g_array_append_val (affected_users, user);
}
cleanup_iterator (&users_iter);
sql ("DELETE FROM group_users WHERE \"group\" = %llu;", group);
ret = add_users ("group", group, users);
init_iterator (&users_iter,
"SELECT \"user\" FROM group_users"
" WHERE \"group\" = %llu",
group);
// users not looked for in this above loop were removed
// -> possible permissions change
while (next (&users_iter))
{
int index, found_user;
user_t user = iterator_int64 (&users_iter, 0);
found_user = 0;
for (index = 0; index < affected_users->len && found_user == 0; index++)
{
if (g_array_index (affected_users, user_t, index) == user)
{
found_user = 1;
break;
}
}
if (found_user)
{
// users found here stay in the group -> no change in permissions
g_array_remove_index_fast (affected_users, index);
}
else
{
// user added to group -> possible permissions change
g_array_append_val (affected_users, user);
}
}
cleanup_iterator (&users_iter);
cache_all_permissions_for_users (affected_users);
g_array_free (affected_users, TRUE);
if (ret)
sql_rollback ();
else
sql_commit ();
return ret;
}
/* Permissions. */
/**
* @brief Adjust location of resource in permissions.
*
* @param[in] type Type.
* @param[in] old Resource ID in old table.
* @param[in] new Resource ID in new table.
* @param[in] to Destination, trash or table.
*/
void
permissions_set_locations (const char *type, resource_t old, resource_t new,
int to)
{
sql ("UPDATE permissions SET resource_location = %i, resource = %llu"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = %i;",
to,
new,
type,
old,
to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
sql ("UPDATE permissions_trash SET resource_location = %i, resource = %llu"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = %i;",
to,
new,
type,
old,
to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
}
/**
* @brief Set permissions to orphan.
*
* @param[in] type Type.
* @param[in] resource Resource ID.
* @param[in] location Location: table or trash.
*/
void
permissions_set_orphans (const char *type, resource_t resource, int location)
{
sql ("UPDATE permissions SET resource = -1"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = %i;",
type,
resource,
location);
sql ("UPDATE permissions_trash SET resource = -1"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = %i;",
type,
resource,
location);
}
/**
* @brief Adjust subject in permissions.
*
* @param[in] type Subject type.
* @param[in] old Resource ID in old table.
* @param[in] new Resource ID in new table.
* @param[in] to Destination, trash or table.
*/
static void
permissions_set_subjects (const char *type, resource_t old, resource_t new,
int to)
{
assert (type && (strcmp (type, "group") == 0 || strcmp (type, "role") == 0));
sql ("UPDATE permissions"
" SET subject_location = %i, subject = %llu"
" WHERE subject_location = %i"
" AND subject_type = '%s'"
" AND subject = %llu;",
to,
new,
to == LOCATION_TRASH ? LOCATION_TABLE : LOCATION_TRASH,
type,
old);
sql ("UPDATE permissions_trash"
" SET subject_location = %i, subject = %llu"
" WHERE subject_location = %i"
" AND subject_type = '%s'"
" AND subject = %llu;",
to,
new,
to == LOCATION_TRASH ? LOCATION_TABLE : LOCATION_TRASH,
type,
old);
}
/**
* @brief Find a permission given a UUID.
*
* @param[in] uuid UUID of permission.
* @param[out] permission Permission return, 0 if successfully failed to find
* permission.
*
* @return FALSE on success (including if failed to find permission), TRUE on
* error.
*/
static gboolean
find_permission (const char* uuid, permission_t* permission)
{
return find_resource ("permission", uuid, permission);
}
/**
* @brief Check args for create_permission or modify_permission.
*
* @param[in] name_arg Name of permission.
* @param[in] resource_type_arg Type of resource, for special permissions.
* @param[in] resource_id_arg UUID of resource.
* @param[in] subject_type Type of subject.
* @param[in] subject_id UUID of subject.
* @param[out] name Name return.
* @param[out] resource Resource return.
* @param[out] resource_type Resource type return.
* @param[out] resource_id Resource ID return.
* @param[out] subject Subject return.
*
* @return 0 success, 2 failed to find subject, 3 failed to find resource,
* 5 error in resource, 6 error in subject, 7 error in name,
* 8 permission on permission, 9 permission does not accept resource,
* 99 permission denied, -1 error.
*/
static int
check_permission_args (const char *name_arg, const char *resource_type_arg,
const char *resource_id_arg, const char *subject_type,
const char *subject_id, gchar **name,
resource_t *resource, char **resource_type,
const char **resource_id, resource_t *subject)
{
if ((name_arg == NULL)
|| ((valid_gmp_command (name_arg) == 0)
&& strcasecmp (name_arg, "super"))
|| (strcasecmp (name_arg, "get_version") == 0))
return 7;
if (resource_id_arg
&& strcmp (resource_id_arg, "")
&& strcmp (resource_id_arg, "0")
&& (((gmp_command_takes_resource (name_arg) == 0)
&& strcasecmp (name_arg, "super"))))
return 9;
if (resource_type_arg
&& strcasecmp (name_arg, "super") == 0
&& strcmp (resource_type_arg, "group")
&& strcmp (resource_type_arg, "role")
&& strcmp (resource_type_arg, "user"))
return 5;
if (resource_type_arg
&& strcasecmp (name_arg, "super")
&& (valid_db_resource_type (resource_type_arg) == 0
|| gmp_command_takes_resource (name_arg) == 0))
return 5;
if (subject_type
&& strcmp (subject_type, "group")
&& strcmp (subject_type, "role")
&& strcmp (subject_type, "user"))
return 6;
if (subject_id == NULL)
/* For now a permission must have a subject. */
return 6;
if (subject_id && (subject_type == NULL))
return 6;
assert (subject_type);
*name = strcasecmp (name_arg, "super")
? g_ascii_strdown (name_arg, -1)
: g_strdup ("Super");
*resource = 0;
if (resource_id_arg
&& strcmp (resource_id_arg, "")
&& strcmp (resource_id_arg, "0"))
{
*resource_type = strcasecmp (*name, "super")
? gmp_command_type (*name)
: g_strdup (resource_type_arg);
if (*resource_type == NULL)
{
g_free (*name);
return 3;
}
if (strcasecmp (*resource_type, "asset") == 0)
{
g_free (*resource_type);
*resource_type = g_strdup ("host");
if (find_resource (*resource_type, resource_id_arg, resource))
{
g_free (*name);
g_free (*resource_type);
return -1;
}
if (*resource == 0)
{
g_free (*resource_type);
*resource_type = g_strdup ("os");
if (find_resource (*resource_type, resource_id_arg, resource))
{
g_free (*name);
g_free (*resource_type);
return -1;
}
}
}
else
{
gchar *get_permission;
get_permission = g_strdup_printf ("get_%ss", *resource_type);
if (find_resource_with_permission (*resource_type, resource_id_arg,
resource, get_permission, 0))
{
g_free (*name);
g_free (*resource_type);
g_free (get_permission);
return -1;
}
g_free (get_permission);
}
if (*resource == 0)
{
g_free (*name);
g_free (*resource_type);
return 3;
}
*resource_id = resource_id_arg;
}
else
{
*resource_id = NULL;
*resource_type = NULL;
}
if (strcasecmp (*name, "super") == 0)
{
if (*resource == 0)
{
g_free (*name);
g_free (*resource_type);
return 3;
}
if ((acl_user_is_owner (*resource_type, *resource_id) == 0)
&& (acl_user_can_super_everyone (current_credentials.uuid) == 0))
{
g_free (*name);
g_free (*resource_type);
return 99;
}
}
/* For simplicity refuse to make permissions on permissions. */
if (*resource && strcasestr (*name, "permission"))
{
g_free (*name);
g_free (*resource_type);
return 8;
}
/* Ensure the user may grant this permission. */
if (((*resource == 0) || strcasecmp (*name, "super") == 0)
&& (acl_user_can_everything (current_credentials.uuid) == 0))
{
g_free (*name);
g_free (*resource_type);
return 99;
}
*subject = 0;
assert (subject_id);
if (*resource)
{
/* Permission on a particular resource. Only need read access to the
* subject. */
if (find_resource_with_permission (subject_type,
subject_id,
subject,
NULL, /* GET permission. */
0)) /* Trash. */
{
g_free (*name);
g_free (*resource_type);
return -1;
}
}
else
{
gchar *permission;
/* Command level permission. Must have write access to the subject. */
/* However, modification of the predefined roles is forbidden. */
if (subject_id
&& strcmp (subject_type, "role") == 0
&& role_is_predefined_id (subject_id))
return 99;
permission = g_strdup_printf ("modify_%s", subject_type);
if (find_resource_with_permission (subject_type,
subject_id,
subject,
permission,
0)) /* Trash. */
{
g_free (*name);
g_free (*resource_type);
g_free (permission);
return -1;
}
g_free (permission);
}
if (*subject == 0)
{
g_free (*name);
g_free (*resource_type);
return 2;
}
return 0;
}
/**
* @brief Create a SQL clause to select the subject users.
*
* @param[in] subject_type Subject type.
* @param[in] subject The subject.
*
* @return Newly allocated string containing the SQL clause.
*/
static gchar*
subject_where_clause (const char* subject_type, resource_t subject)
{
gchar *subject_where = NULL;
if (subject && subject_type)
{
if (strcmp (subject_type, "user") == 0)
{
subject_where
= g_strdup_printf ("id = %llu", subject);
}
else if (strcmp (subject_type, "group") == 0)
{
subject_where
= g_strdup_printf ("id IN (SELECT \"user\" FROM group_users"
" WHERE \"group\" = %llu)",
subject);
}
else if (strcmp (subject_type, "role") == 0)
{
subject_where
= g_strdup_printf ("id IN (SELECT \"user\" FROM role_users"
" WHERE \"role\" = %llu)",
subject);
}
else
{
subject_where = strdup ("t()");
g_warning ("%s: unknown subject_type %s",
__func__, subject_type);
}
}
return subject_where;
}
/**
* @brief Create a permission.
*
* Caller must organise the transaction.
*
* @param[in] check_access Whether to check if user may CREATE_PERMISSION.
* @param[in] name_arg Name of permission.
* @param[in] comment Comment on permission.
* @param[in] resource_type_arg Type of resource, for special permissions.
* @param[in] resource_id_arg UUID of resource.
* @param[in] subject_type Type of subject.
* @param[in] subject_id UUID of subject.
* @param[out] permission Permission.
*
* @return 0 success, 2 failed to find subject, 3 failed to find resource,
* 5 error in resource, 6 error in subject, 7 error in name,
* 8 permission on permission, 9 permission does not accept resource,
* 99 permission denied, -1 internal error.
*/
int
create_permission_internal (int check_access, const char *name_arg,
const char *comment, const char *resource_type_arg,
const char *resource_id_arg,
const char *subject_type, const char *subject_id,
permission_t *permission)
{
int ret;
gchar *name, *quoted_name, *quoted_comment, *resource_type;
resource_t resource, subject;
const char *resource_id;
GHashTable *reports = NULL;
int clear_original = 0;
gchar *subject_where;
assert (current_credentials.uuid);
if (check_access && (acl_user_may ("create_permission") == 0))
return 99;
ret = check_permission_args (name_arg, resource_type_arg, resource_id_arg,
subject_type, subject_id, &name, &resource,
&resource_type, &resource_id, &subject);
if (ret)
return ret;
assert (subject);
assert ((resource_id == resource_id_arg) || (resource_id == NULL));
quoted_name = sql_quote (name);
g_free (name);
quoted_comment = sql_quote (comment ? comment : "");
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource_uuid, resource,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" VALUES"
" (make_uuid (),"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', '%s', '%s', '%s', %llu, " G_STRINGIFY (LOCATION_TABLE) ","
" %s%s%s, %llu, " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
current_credentials.uuid,
quoted_name,
quoted_comment,
resource_id ? resource_type : "",
resource_id ? resource_id : "",
resource,
subject_id ? "'" : "",
subject_id ? subject_type : "NULL",
subject_id ? "'" : "",
subject);
subject_where = subject_where_clause (subject_type, subject);
if (permission)
*permission = sql_last_insert_id ();
/* Update Permissions cache */
if (strcasecmp (quoted_name, "super") == 0)
cache_all_permissions_for_users (NULL);
else if (resource_type && resource)
cache_permissions_for_resource (resource_type, resource, NULL);
/* Update Reports cache */
if (resource_type && resource_id && strcmp (resource_type, "override") == 0)
{
reports = reports_for_override (resource);
}
else if (strcasecmp (quoted_name, "super") == 0)
{
reports = reports_hashtable ();
clear_original = 1;
}
if (reports && g_hash_table_size (reports))
{
GHashTableIter reports_iter;
report_t *reports_ptr;
int auto_cache_rebuild;
reports_ptr = NULL;
g_hash_table_iter_init (&reports_iter, reports);
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, clear_original, 1,
subject_where);
else
report_clear_count_cache (*reports_ptr, clear_original, 1,
subject_where);
}
}
if (reports)
g_hash_table_destroy (reports);
g_free (quoted_comment);
g_free (quoted_name);
g_free (resource_type);
g_free (subject_where);
return 0;
}
/**
* @brief Create a permission.
*
* @param[in] name_arg Name of permission.
* @param[in] comment Comment on permission.
* @param[in] resource_type_arg Type of resource, for special permissions.
* @param[in] resource_id_arg UUID of resource.
* @param[in] subject_type Type of subject.
* @param[in] subject_id UUID of subject.
* @param[out] permission Permission.
*
* @return 0 success, 2 failed to find subject, 3 failed to find resource,
* 5 error in resource, 6 error in subject, 7 error in name,
* 8 permission on permission, 9 permission does not accept resource,
* 99 permission denied, -1 internal error.
*/
int
create_permission (const char *name_arg, const char *comment,
const char *resource_type_arg, const char *resource_id_arg,
const char *subject_type, const char *subject_id,
permission_t *permission)
{
int ret;
sql_begin_immediate ();
ret = create_permission_internal (1, name_arg, comment, resource_type_arg,
resource_id_arg, subject_type, subject_id,
permission);
if (ret)
sql_rollback ();
else
sql_commit ();
return ret;
}
/**
* @brief Create a permission.
*
* Does not require current user to have CREATE_PERMISSION access.
*
* @param[in] name_arg Name of permission.
* @param[in] comment Comment on permission.
* @param[in] resource_type_arg Type of resource, for special permissions.
* @param[in] resource_id_arg UUID of resource.
* @param[in] subject_type Type of subject.
* @param[in] subject_id UUID of subject.
* @param[out] permission Permission.
*
* @return 0 success, 2 failed to find subject, 3 failed to find resource,
* 5 error in resource, 6 error in subject, 7 error in name,
* 8 permission on permission, 9 permission does not accept resource,
* 99 permission denied, -1 internal error.
*/
int
create_permission_no_acl (const char *name_arg, const char *comment,
const char *resource_type_arg,
const char *resource_id_arg,
const char *subject_type, const char *subject_id,
permission_t *permission)
{
return create_permission_internal (0, name_arg, comment, resource_type_arg,
resource_id_arg, subject_type, subject_id,
permission);
}
/**
* @brief Create a permission from an existing permission.
*
* @param[in] comment Comment on new permission. NULL to copy from existing.
* @param[in] permission_id UUID of existing permission.
* @param[out] new_permission New permission.
*
* @return 0 success, 1 permission exists already, 2 failed to find existing
* permission, 99 permission denied, -1 error.
*/
int
copy_permission (const char* comment, const char *permission_id,
permission_t* new_permission)
{
int ret;
permission_t permission, new, old;
char *subject_type, *name;
resource_t subject;
sql_begin_immediate ();
permission = 0;
/* There are no permissions on permissions, so no need for the
* "_with_permission" version. */
if (find_permission (permission_id, &permission))
{
sql_rollback ();
return -1;
}
if (permission == 0)
{
sql_rollback ();
return 2;
}
/* Prevent copying of command level permissions for predefined roles. */
subject_type = permission_subject_type (permission);
subject = permission_subject (permission);
if (permission_resource (permission) == 0
&& subject_type
&& strcmp (subject_type, "role") == 0
&& subject
&& role_is_predefined (subject))
{
free (subject_type);
sql_rollback ();
return 99;
}
free (subject_type);
/* Refuse to copy Super On Everyone. */
name = permission_name (permission);
if ((strcmp (name, "Super") == 0)
&& (permission_resource (permission) == 0))
{
free (name);
sql_rollback ();
return 99;
}
free (name);
ret = copy_resource_lock ("permission", NULL, comment, permission_id,
"resource_type, resource, resource_uuid,"
" resource_location, subject_type, subject,"
" subject_location",
0, &new, &old);
if (ret)
{
sql_rollback ();
return ret;
}
sql_commit ();
if (new_permission) *new_permission = new;
return 0;
}
/**
* @brief Return the UUID of a permission.
*
* @param[in] permission Permission.
*
* @return Newly allocated UUID if available, else NULL.
*/
char*
permission_uuid (permission_t permission)
{
return sql_string ("SELECT uuid FROM permissions WHERE id = %llu;",
permission);
}
/**
* @brief Return the resource of a permission.
*
* @param[in] permission Permission.
*
* @return Resource if there is one, else 0.
*/
static resource_t
permission_resource (permission_t permission)
{
resource_t resource;
sql_int64 (&resource,
"SELECT resource FROM permissions WHERE id = %llu;",
permission);
return resource;
}
/**
* @brief Return the name of a permission.
*
* @param[in] permission Permission.
*
* @return Newly allocated name if available, else NULL.
*/
static char *
permission_name (permission_t permission)
{
return sql_string ("SELECT name FROM permissions WHERE id = %llu;",
permission);
}
/**
* @brief Return the subject type of a permission.
*
* @param[in] permission Permission.
*
* @return Newly allocated subject type if available, else NULL.
*/
static char *
permission_subject_type (permission_t permission)
{
return sql_string ("SELECT subject_type FROM permissions WHERE id = %llu;",
permission);
}
/**
* @brief Return the subject of a permission.
*
* @param[in] permission Permission.
*
* @return Subject if there is one, else 0.
*/
static resource_t
permission_subject (permission_t permission)
{
resource_t subject;
sql_int64 (&subject,
"SELECT subject FROM permissions WHERE id = %llu;",
permission);
return subject;
}
/**
* @brief Return the UUID of the subject of a permission.
*
* @param[in] permission Permission.
*
* @return Newly allocated subject ID if available, else NULL.
*/
static char *
permission_subject_id (permission_t permission)
{
return sql_string ("SELECT subject_id FROM permissions WHERE id = %llu;",
permission);
}
/**
* @brief Return the resource type of a permission.
*
* @param[in] permission Permission.
*
* @return Newly allocated resource type if available, else NULL.
*/
static char *
permission_resource_type (permission_t permission)
{
return sql_string ("SELECT resource_type FROM permissions WHERE id = %llu;",
permission);
}
/**
* @brief Return the UUID of the resource of a permission.
*
* @param[in] permission Permission.
*
* @return Newly allocated resource ID if available, else NULL.
*/
static char *
permission_resource_id (permission_t permission)
{
return sql_string ("SELECT resource_id FROM permissions WHERE id = %llu;",
permission);
}
/**
* @brief Return whether a permission is predefined.
*
* @param[in] permission Permission.
*
* @return 1 if predefined, else 0.
*/
static int
permission_is_predefined (permission_t permission)
{
return !!sql_int ("SELECT COUNT (*) FROM permissions"
" WHERE id = %llu"
" AND (uuid = '" PERMISSION_UUID_ADMIN_EVERYTHING "'"
" OR (subject_type = 'role'"
" AND resource = 0"
" AND subject"
" IN (SELECT id FROM roles"
" WHERE uuid = '" ROLE_UUID_ADMIN "'"
" OR uuid = '" ROLE_UUID_GUEST "'"
" OR uuid = '" ROLE_UUID_INFO "'"
" OR uuid = '" ROLE_UUID_MONITOR "'"
" OR uuid = '" ROLE_UUID_USER "'"
" OR uuid = '" ROLE_UUID_SUPER_ADMIN "'"
" OR uuid = '" ROLE_UUID_OBSERVER "')))",
permission);
}
/**
* @brief Test whether a permission is the special Admin permission.
*
* @param[in] permission_id UUID of permission.
*
* @return 1 permission is Admin, else 0.
*/
int
permission_is_admin (const char *permission_id)
{
if (permission_id)
return strcmp (permission_id, PERMISSION_UUID_ADMIN_EVERYTHING);
return 0;
}
/**
* @brief Return whether a permission is in use.
*
* @param[in] permission Permission.
*
* @return 1 if in use, else 0.
*/
int
permission_in_use (permission_t permission)
{
return 0;
}
/**
* @brief Return whether a trashcan permission is referenced by a task.
*
* @param[in] permission Permission.
*
* @return 1 if in use, else 0.
*/
int
trash_permission_in_use (permission_t permission)
{
return 0;
}
/**
* @brief Return whether a permission is writable.
*
* @param[in] permission Permission.
*
* @return 1 if writable, else 0.
*/
int
permission_writable (permission_t permission)
{
if (permission_is_predefined (permission))
return 0;
return 1;
}
/**
* @brief Return whether a trashcan permission is writable.
*
* @param[in] permission Permission.
*
* @return 1 if writable, else 0.
*/
int
trash_permission_writable (permission_t permission)
{
return 1;
}
/**
* @brief Filter columns for permission iterator.
*/
#define PERMISSION_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "type", "resource_uuid", "subject_type", \
"_subject", "_resource", "subject_uuid", "orphan", NULL }
/**
* @brief Permission iterator columns.
*/
#define PERMISSION_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (permissions), \
{ "resource_type", "type", KEYWORD_TYPE_STRING }, \
{ "resource_uuid", NULL, KEYWORD_TYPE_STRING }, \
{ \
"(CASE" \
" WHEN resource_type = '' OR resource_type IS NULL" \
" THEN ''" \
" ELSE resource_name (resource_type, resource_uuid, resource_location)" \
" END)", \
"_resource", \
KEYWORD_TYPE_STRING \
}, \
{ "CAST ((resource_location = " G_STRINGIFY (LOCATION_TRASH) ")" \
" AS INTEGER)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ \
"(CASE" \
" WHEN resource = -1" \
" THEN 1" \
" ELSE 0" \
" END)", \
"orphan", \
KEYWORD_TYPE_INTEGER \
}, \
{ "subject_type", NULL, KEYWORD_TYPE_STRING }, \
{ \
"(CASE" \
" WHEN subject_type = 'user'" \
" THEN (SELECT uuid FROM users WHERE users.id = subject)" \
" WHEN subject_type = 'group'" \
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT uuid FROM groups_trash" \
" WHERE groups_trash.id = subject)" \
" WHEN subject_type = 'group'" \
" THEN (SELECT uuid FROM groups WHERE groups.id = subject)" \
" WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT uuid FROM roles_trash" \
" WHERE roles_trash.id = subject)" \
" ELSE (SELECT uuid FROM roles WHERE roles.id = subject)" \
" END)", \
"subject_uuid", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(CASE" \
" WHEN subject_type = 'user'" \
" THEN (SELECT name FROM users WHERE users.id = subject)" \
" WHEN subject_type = 'group'" \
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT name FROM groups_trash" \
" WHERE groups_trash.id = subject)" \
" WHEN subject_type = 'group'" \
" THEN (SELECT name FROM groups WHERE groups.id = subject)" \
" WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT name FROM roles_trash" \
" WHERE roles_trash.id = subject)" \
" ELSE (SELECT name FROM roles WHERE roles.id = subject)" \
" END)", \
"_subject", \
KEYWORD_TYPE_STRING \
}, \
{ "CAST ((subject_location = " G_STRINGIFY (LOCATION_TRASH) ")" \
" AS INTEGER)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Permission iterator columns for trash case.
*/
#define PERMISSION_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (permissions_trash), \
{ "resource_type", "type", KEYWORD_TYPE_STRING }, \
{ "resource_uuid", NULL, KEYWORD_TYPE_STRING }, \
{ \
"(CASE" \
" WHEN resource_type = '' OR resource_type IS NULL" \
" THEN ''" \
" ELSE resource_name (resource_type, resource_uuid, resource_location)" \
" END)", \
"_resource", \
KEYWORD_TYPE_STRING \
}, \
{ "CAST ((resource_location = " G_STRINGIFY (LOCATION_TRASH) ")" \
" AS INTEGER)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ "resource = -1", NULL, KEYWORD_TYPE_INTEGER }, \
{ "subject_type", NULL, KEYWORD_TYPE_STRING }, \
{ \
"(CASE" \
" WHEN subject_type = 'user'" \
" THEN (SELECT uuid FROM users WHERE users.id = subject)" \
" WHEN subject_type = 'group'" \
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT uuid FROM groups_trash" \
" WHERE groups_trash.id = subject)" \
" WHEN subject_type = 'group'" \
" THEN (SELECT uuid FROM groups WHERE groups.id = subject)" \
" WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT uuid FROM roles_trash" \
" WHERE roles_trash.id = subject)" \
" ELSE (SELECT uuid FROM roles WHERE roles.id = subject)" \
" END)", \
"subject_uuid", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(CASE" \
" WHEN subject_type = 'user'" \
" THEN (SELECT name FROM users WHERE users.id = subject)" \
" WHEN subject_type = 'group'" \
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT name FROM groups_trash" \
" WHERE groups_trash.id = subject)" \
" WHEN subject_type = 'group'" \
" THEN (SELECT name FROM groups WHERE groups.id = subject)" \
" WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH) \
" THEN (SELECT name FROM roles_trash" \
" WHERE roles_trash.id = subject)" \
" ELSE (SELECT name FROM roles WHERE roles.id = subject)" \
" END)", \
"_subject", \
KEYWORD_TYPE_STRING \
}, \
{ "CAST ((subject_location = " G_STRINGIFY (LOCATION_TRASH) ")" \
" AS INTEGER)", \
NULL, \
KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of permissions.
*
* @param[in] get GET params.
*
* @return Total number of permissions in filtered set.
*/
int
permission_count (const get_data_t *get)
{
static const char *filter_columns[] = PERMISSION_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = PERMISSION_ITERATOR_COLUMNS;
static column_t trash_columns[] = PERMISSION_ITERATOR_TRASH_COLUMNS;
return count ("permission", get, columns, trash_columns, filter_columns,
0, 0, 0, TRUE);
}
/**
* @brief Initialise a permission iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find target, 2 failed to find filter,
* -1 error.
*/
int
init_permission_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = PERMISSION_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = PERMISSION_ITERATOR_COLUMNS;
static column_t trash_columns[] = PERMISSION_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"permission",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Get the type of resource from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return Type, or NULL if iteration is complete.
*/
DEF_ACCESS (permission_iterator_resource_type, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the UUID of the resource from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID, or NULL if iteration is complete.
*/
DEF_ACCESS (permission_iterator_resource_uuid, GET_ITERATOR_COLUMN_COUNT + 1);
/**
* @brief Get the name of the resource from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete.
*/
DEF_ACCESS (permission_iterator_resource_name, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Return the permission resource location.
*
* @param[in] iterator Iterator.
*
* @return Whether the resource is in the trashcan
*/
int
permission_iterator_resource_in_trash (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
}
/**
* @brief Check if the permission resource has been deleted.
*
* @param[in] iterator Iterator.
*
* @return Whether the resource has been deleted.
*/
int
permission_iterator_resource_orphan (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
}
/**
* @brief Get the readable status of a resource from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if readable, otherwise 0.
*/
int
permission_iterator_resource_readable (iterator_t* iterator)
{
resource_t found;
const char *type, *uuid;
gchar *permission;
if (iterator->done) return 0;
type = permission_iterator_resource_type (iterator);
uuid = permission_iterator_resource_uuid (iterator);
if (type == NULL || uuid == NULL)
return 0;
if (type_is_info_subtype (type))
permission = g_strdup ("get_info");
else if (type_is_asset_subtype (type))
permission = g_strdup ("get_assets");
else
permission = g_strdup_printf ("get_%ss", type);
found = 0;
find_resource_with_permission (type,
uuid,
&found,
permission,
permission_iterator_resource_in_trash
(iterator));
g_free (permission);
return found > 0;
}
/**
* @brief Get the type of subject from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return Type, or NULL if iteration is complete.
*/
DEF_ACCESS (permission_iterator_subject_type, GET_ITERATOR_COLUMN_COUNT + 5);
/**
* @brief Get the subject UUID from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID, or NULL if iteration is complete.
*/
DEF_ACCESS (permission_iterator_subject_uuid, GET_ITERATOR_COLUMN_COUNT + 6);
/**
* @brief Get the subject name from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return Name, or NULL if iteration is complete.
*/
DEF_ACCESS (permission_iterator_subject_name, GET_ITERATOR_COLUMN_COUNT + 7);
/**
* @brief Return the permission subject location.
*
* @param[in] iterator Iterator.
*
* @return Whether the subject is in the trashcan
*/
int
permission_iterator_subject_in_trash (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
}
/**
* @brief Get the readable status of a subject from a permission iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if readable, otherwise 0.
*/
int
permission_iterator_subject_readable (iterator_t* iterator)
{
resource_t found;
const char *type, *uuid;
gchar *permission;
if (iterator->done) return 0;
type = permission_iterator_subject_type (iterator);
uuid = permission_iterator_subject_uuid (iterator);
if (type == NULL || uuid == NULL)
return 0;
if ((strcmp (type, "user") == 0)
|| (strcmp (type, "role") == 0)
|| (strcmp (type, "group") == 0))
permission = g_strdup_printf ("get_%ss", type);
else
return 0;
found = 0;
find_resource_with_permission (type,
uuid,
&found,
permission,
permission_iterator_subject_in_trash
(iterator));
g_free (permission);
return found > 0;
}
/**
* @brief Find a permission with a given permission, given a UUID.
*
* @param[in] uuid UUID of permission.
* @param[out] resource Permission return, 0 if successfully failed to find
* permission.
* @param[in] permission Required permission, for example "delete".
*
* @return FALSE on success (including if failed to find permission), TRUE on
* error.
*/
static gboolean
find_permission_with_permission (const char *uuid, permission_t *resource,
const char *permission)
{
gchar *quoted_uuid = sql_quote (uuid);
if (acl_user_has_access_uuid ("permission", quoted_uuid, permission, 0) == 0)
{
g_free (quoted_uuid);
*resource = 0;
return FALSE;
}
switch (sql_int64 (resource,
"SELECT id FROM permissions WHERE uuid = '%s';",
quoted_uuid))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
*resource = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_uuid);
return TRUE;
break;
}
g_free (quoted_uuid);
return FALSE;
}
/**
* @brief Delete a permission.
*
* @param[in] permission_id UUID of permission.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 2 failed to find permission, 3 predefined permission,
* 99 permission denied, -1 error.
*/
int
delete_permission (const char *permission_id, int ultimate)
{
permission_t permission = 0;
char *name, *subject_type, *resource_type;
resource_t subject, resource;
GHashTable *reports = NULL;
int clear_original = 0;
gchar *subject_where;
if (strcasecmp (permission_id, PERMISSION_UUID_ADMIN_EVERYTHING) == 0)
return 3;
sql_begin_immediate ();
if (acl_user_may ("delete_permission") == 0)
{
sql_rollback ();
return 99;
}
if (find_permission_with_permission (permission_id, &permission,
"delete_permission"))
{
sql_rollback ();
return -1;
}
if (permission == 0)
{
if (find_trash ("permission", permission_id, &permission))
{
sql_rollback ();
return -1;
}
if (permission == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
tags_remove_resource ("permission", permission, LOCATION_TRASH);
sql ("DELETE FROM permissions_trash WHERE id = %llu;", permission);
sql_commit ();
return 0;
}
/* Prevent deletion of command level permissions for predefined roles. */
subject_type = permission_subject_type (permission);
subject = permission_subject (permission);
resource = permission_resource (permission);
if (resource == 0
&& subject_type
&& strcmp (subject_type, "role") == 0
&& subject
&& role_is_predefined (subject))
{
free (subject_type);
sql_rollback ();
return 99;
}
subject_where = subject_where_clause (subject_type, subject);
free (subject_type);
if (ultimate == 0)
{
sql ("INSERT INTO permissions_trash"
" (uuid, owner, name, comment, resource_type, resource,"
" resource_uuid, resource_location, subject_type, subject,"
" subject_location, creation_time, modification_time)"
" SELECT uuid, owner, name, comment, resource_type, resource,"
" resource_uuid, resource_location, subject_type, subject,"
" subject_location, creation_time, modification_time"
" FROM permissions"
" WHERE id = %llu;",
permission);
tags_set_locations ("permission", permission,
sql_last_insert_id (),
LOCATION_TRASH);
}
else
tags_remove_resource ("permission", permission, LOCATION_TABLE);
name = permission_name (permission);
resource_type = permission_resource_type (permission);
sql ("DELETE FROM permissions WHERE id = %llu;", permission);
/* Update Permissions cache */
if (strcasecmp (name, "super") == 0)
cache_all_permissions_for_users (NULL);
else if (resource_type && resource)
cache_permissions_for_resource (resource_type, resource, NULL);
/* Update Reports cache */
if (resource_type && (resource > 0) && strcmp (resource_type, "override")
== 0)
{
reports = reports_for_override (resource);
}
else if (strcasecmp (name, "super") == 0)
{
reports = reports_hashtable ();
clear_original = 1;
}
free (name);
free (resource_type);
if (reports && g_hash_table_size (reports))
{
GHashTableIter reports_iter;
report_t *reports_ptr;
int auto_cache_rebuild;
reports_ptr = NULL;
g_hash_table_iter_init (&reports_iter, reports);
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, clear_original, 1,
subject_where);
else
report_clear_count_cache (*reports_ptr, clear_original, 1,
subject_where);
}
}
g_free (subject_where);
if (reports)
g_hash_table_destroy (reports);
sql_commit ();
return 0;
}
/**
* @brief Modify a permission.
*
* @param[in] permission_id UUID of permission.
* @param[in] name_arg Name of permission.
* @param[in] comment Comment on permission.
* @param[in] resource_id_arg UUID of resource.
* @param[in] resource_type_arg Type of resource, for Super permissions.
* @param[in] subject_type Type of subject.
* @param[in] subject_id UUID of subject.
*
* @return 0 success, 1 failed to find permission, 2 failed to find subject,
* 3 failed to find resource, 4 permission_id required, 5 error in
* resource, 6 error in subject, 7 error in name, 8 name required to
* find resource, 9 permission does not accept resource, 99 permission
* denied, -1 internal error.
*/
int
modify_permission (const char *permission_id, const char *name_arg,
const char *comment, const char *resource_id_arg,
const char *resource_type_arg, const char *subject_type,
const char *subject_id)
{
int ret;
permission_t permission;
resource_t resource, subject;
char *existing_subject_type, *new_name, *new_resource_type;
char *new_resource_id, *new_subject_type, *new_subject_id;
gchar *name, *quoted_name, *resource_type;
const char *resource_id;
char *old_name, *old_resource_type;
resource_t old_resource;
GHashTable *reports = NULL;
int clear_original = 0;
gchar *subject_where_old, *subject_where_new, *subject_where;
if (permission_id == NULL)
return 4;
sql_begin_immediate ();
if (acl_user_may ("modify_permission") == 0)
{
sql_rollback ();
return 99;
}
/* Find the permission. */
permission = 0;
/* There are no permissions on permissions, so no need for the
* "_with_permission" version. */
if (find_permission (permission_id, &permission))
{
sql_rollback ();
return -1;
}
if (permission == 0)
{
sql_rollback ();
return 1;
}
/* Refuse to modify command-level permissions on predefined roles. */
existing_subject_type = permission_subject_type (permission);
resource = permission_resource (permission);
subject = permission_subject (permission);
if (resource == 0
&& existing_subject_type
&& strcmp (existing_subject_type, "role") == 0
&& subject
&& role_is_predefined (subject))
{
free (existing_subject_type);
sql_rollback ();
return 99;
}
/* Get old subject clause */
subject_where_old = subject_where_clause (existing_subject_type, subject);
/* Set the comment first, to make things easier. */
if (comment)
{
gchar *quoted_comment;
quoted_comment = sql_quote (comment);
sql ("UPDATE permissions SET"
" comment = '%s',"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_comment,
permission);
g_free (quoted_comment);
}
/* Check the arguments. */
new_name = name_arg ? NULL : permission_name (permission);
if (resource_type_arg && resource_id_arg && strcmp (resource_id_arg, "0"))
/* Given a resource. */
new_resource_type = NULL;
else if (resource_id_arg && (strcmp (resource_id_arg, "0") == 0))
/* User wants to clear the resource. */
new_resource_type = NULL;
else
{
new_resource_type = permission_resource_type (permission);
if (new_resource_type
&& strcmp (new_resource_type, "group")
&& strcmp (new_resource_type, "role")
&& strcmp (new_resource_type, "user"))
/* Type will come from command name. */
new_resource_type = NULL;
}
new_resource_id = (resource_id_arg && strcmp (resource_id_arg, ""))
? NULL
: permission_resource_id (permission);
new_subject_type = subject_type ? NULL : existing_subject_type;
new_subject_id = subject_id ? NULL : permission_subject_id (permission);
ret = check_permission_args
(new_name ? new_name : name_arg,
new_resource_type ? new_resource_type : resource_type_arg,
new_resource_id ? new_resource_id : resource_id_arg,
new_subject_type ? new_subject_type : subject_type,
new_subject_id ? new_subject_id : subject_id,
&name,
&resource,
&resource_type,
&resource_id,
&subject);
free (new_name);
if (ret)
{
free (new_resource_type);
free (new_resource_id);
free (existing_subject_type);
free (new_subject_id);
g_free (subject_where_old);
sql_rollback ();
return ret;
}
subject_where_new = subject_where_clause (new_subject_type
? new_subject_type
: subject_type,
subject);
if (strcmp (subject_where_new, subject_where_old))
{
subject_where = g_strdup_printf ("(%s) OR (%s)",
subject_where_new, subject_where_old);
g_free (subject_where_new);
g_free (subject_where_old);
}
else
{
subject_where = subject_where_old;
g_free (subject_where_new);
}
/* Get old values and check if caches are affected by previous resource. */
old_name = permission_name (permission);
old_resource_type = permission_resource_type (permission);
old_resource = permission_resource (permission);
if (old_resource
&& strcmp (old_resource_type, "override") == 0)
{
reports = reports_for_override (resource);
}
else if (strcasecmp (old_name, "super"))
{
reports = reports_hashtable ();
clear_original = 1;
}
else
{
reports = new_resources_hashtable ();
}
/* Modify the permission. */
assert (subject);
assert ((resource_id == new_resource_id)
|| (resource_id == resource_id_arg)
|| (resource_id == NULL));
quoted_name = sql_quote (name);
sql ("UPDATE permissions SET"
" name = '%s',"
" resource_type = '%s',"
" resource_uuid = '%s',"
" resource = %llu,"
" resource_location = " G_STRINGIFY (LOCATION_TABLE) ","
" subject_type = '%s',"
" subject = %llu,"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_name,
(resource_id && resource_type) ? resource_type : "",
resource_id ? resource_id : "",
resource,
new_subject_type ? new_subject_type : subject_type,
subject,
permission);
/* Update permission caches according to the modifications. */
if (strcasecmp (name, "super") == 0 || strcasecmp (old_name, "super") == 0)
cache_all_permissions_for_users (NULL);
else
{
if (resource_type && resource_id && strcmp (resource_id, ""))
cache_permissions_for_resource (resource_type, resource, NULL);
if (old_resource
&& old_resource_type
&& ((resource != old_resource)
|| (resource_type
&& strcmp (old_resource_type, resource_type))))
cache_permissions_for_resource (old_resource_type, old_resource, NULL);
}
/* Check if caches are affected by the permission and update reports cache */
if (resource_type
&& resource
&& (resource != old_resource
|| strcmp (old_resource_type, "override"))
&& strcmp (resource_type, "override") == 0)
{
reports_add_for_override (reports, resource);
}
else if (strcasecmp (quoted_name, "super") == 0
&& strcasecmp (old_name, quoted_name))
{
reports_add_all (reports);
clear_original = 1;
}
if (reports)
{
GHashTableIter reports_iter;
report_t *reports_ptr;
int auto_cache_rebuild;
g_hash_table_iter_init (&reports_iter, reports);
reports_ptr = NULL;
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, clear_original, 1,
subject_where);
else
report_clear_count_cache (*reports_ptr, clear_original, 1,
subject_where);
}
g_hash_table_destroy (reports);
reports = NULL;
}
/* Cleanup. */
g_free (quoted_name);
free (new_resource_type);
free (new_resource_id);
free (existing_subject_type);
free (new_subject_id);
g_free (name);
free (old_name);
free (old_resource_type);
g_free (subject_where);
sql_commit ();
return 0;
}
/**
* @brief Add role permissions to feed objects according to the
* 'Feed Import Roles' setting.
*
* @param[in] type The object type, e.g. report_format.
* @param[in] type_cap Capitalized type, e.g. "Report Format"
* @param[out] permission_count Number of permissions added.
* @param[out] object_count Number of data objects affected.
*/
static void
add_feed_role_permissions (const char *type,
const char *type_cap,
int *permission_count,
int *object_count)
{
char *roles_str;
gchar **roles;
iterator_t resources;
roles_str = NULL;
setting_value (SETTING_UUID_FEED_IMPORT_ROLES, &roles_str);
if (roles_str == NULL || strlen (roles_str) == 0)
{
g_message ("%s: No feed import roles defined", __func__);
g_free (roles_str);
return;
}
roles = g_strsplit (roles_str, ",", 0);
free (roles_str);
init_iterator (&resources,
"SELECT id, uuid, name, owner FROM %ss"
" WHERE predefined = 1",
type);
while (next (&resources))
{
gboolean added_permission = FALSE;
resource_t permission_resource = iterator_int64 (&resources, 0);
const char *permission_resource_id = iterator_string (&resources, 1);
const char *permission_resource_name = iterator_string (&resources, 2);
user_t owner = iterator_int64 (&resources, 3);
gchar **role = roles;
while (*role)
{
char *role_name = NULL;
resource_name ("role", *role, LOCATION_TABLE, &role_name);
if (sql_int ("SELECT count(*) FROM permissions"
" WHERE name = 'get_%ss'"
" AND subject_type = 'role'"
" AND subject"
" = (SELECT id FROM roles WHERE uuid='%s')"
" AND resource = %llu",
type,
*role,
permission_resource))
{
g_debug ("Role %s (%s) already has read permission"
" for %s %s (%s).",
role_name,
*role,
type_cap,
permission_resource_name,
permission_resource_id);
}
else
{
gchar *permission_name;
g_info ("Creating read permission for role %s (%s)"
" on %s %s (%s).",
role_name,
*role,
type_cap,
permission_resource_name,
permission_resource_id);
added_permission = TRUE;
if (permission_count)
*permission_count = *permission_count + 1;
permission_name = g_strdup_printf ("get_%ss", type);
current_credentials.uuid = user_uuid (owner);
switch (create_permission_internal
(0,
permission_name,
"Automatically created by"
" --optimize",
type,
permission_resource_id,
"role",
*role,
NULL))
{
case 0:
// success
break;
case 2:
g_warning ("%s: failed to find role %s for permission",
__func__, *role);
break;
case 3:
g_warning ("%s: failed to find %s %s for permission",
__func__, type_cap, permission_resource_id);
break;
case 5:
g_warning ("%s: error in resource when creating permission"
" for %s %s",
__func__, type_cap, permission_resource_id);
break;
case 6:
g_warning ("%s: error in subject (Role %s)",
__func__, *role);
break;
case 7:
g_warning ("%s: error in name %s",
__func__, permission_name);
break;
case 8:
g_warning ("%s: permission on permission", __func__);
break;
case 9:
g_warning ("%s: permission %s does not accept resource",
__func__, permission_name);
break;
case 99:
g_warning ("%s: permission denied to create %s permission"
" for role %s on %s %s",
__func__, permission_name, *role, type_cap,
permission_resource_id);
break;
default:
g_warning ("%s: internal error creating %s permission"
" for role %s on %s %s",
__func__, permission_name, *role, type_cap,
permission_resource_id);
break;
}
free (current_credentials.uuid);
current_credentials.uuid = NULL;
}
free (role_name);
role ++;
}
if (object_count && added_permission)
*object_count = *object_count + 1;
}
cleanup_iterator (&resources);
g_strfreev (roles);
return;
}
/**
* @brief Delete permissions to feed objects for roles that are not set
* in the 'Feed Import Roles' setting.
*
* @param[in] type The object type, e.g. report_format.
* @param[in] type_cap Capitalized type, e.g. "Report Format"
* @param[out] permission_count Number of permissions added.
* @param[out] object_count Number of data objects affected.
*/
static void
clean_feed_role_permissions (const char *type,
const char *type_cap,
int *permission_count,
int *object_count)
{
char *roles_str;
gchar **roles, **role;
GString *sql_roles;
iterator_t resources;
roles_str = NULL;
setting_value (SETTING_UUID_FEED_IMPORT_ROLES, &roles_str);
if (roles_str == NULL || strlen (roles_str) == 0)
{
g_message ("%s: No feed import roles defined", __func__);
g_free (roles_str);
return;
}
sql_roles = g_string_new ("(");
if (roles_str)
{
roles = g_strsplit (roles_str, ",", 0);
role = roles;
while (*role)
{
gchar *quoted_role = sql_insert (*role);
g_string_append (sql_roles, quoted_role);
role ++;
if (*role)
g_string_append (sql_roles, ", ");
}
}
g_string_append (sql_roles, ")");
g_debug ("%s: Keeping permissions for roles %s\n", __func__, sql_roles->str);
init_iterator (&resources,
"SELECT id, uuid, name FROM %ss"
" WHERE predefined = 1",
type);
while (next (&resources))
{
gboolean removed_permission = FALSE;
resource_t permission_resource = iterator_int64 (&resources, 0);
const char *permission_resource_id = iterator_string (&resources, 1);
const char *permission_resource_name = iterator_string (&resources, 2);
iterator_t permissions;
roles = NULL;
init_iterator (&permissions,
"DELETE FROM permissions"
" WHERE name = 'get_%ss'"
" AND resource = %llu"
" AND subject_type = 'role'"
" AND subject NOT IN"
" (SELECT id FROM roles WHERE uuid IN %s)"
" RETURNING"
" (SELECT uuid FROM roles WHERE id = subject),"
" (SELECT name FROM roles WHERE id = subject)",
type,
permission_resource,
sql_roles->str);
while (next (&permissions))
{
const char *role_id = iterator_string (&permissions, 0);
const char *role_name = iterator_string (&permissions, 1);
g_info ("Removed permission on %s %s (%s) for role %s (%s)",
type_cap,
permission_resource_name,
permission_resource_id,
role_name,
role_id);
if (permission_count)
*permission_count = *permission_count + 1;
removed_permission = TRUE;
}
if (object_count && removed_permission)
*object_count = *object_count + 1;
}
cleanup_iterator (&resources);
g_strfreev (roles);
return;
}
/* Roles. */
/**
* @brief List roles.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] verbose Whether to print UUID.
*
* @return 0 success, -1 error.
*/
int
manage_get_roles (GSList *log_config, const db_conn_info_t *database,
int verbose)
{
iterator_t roles;
int ret;
g_info (" Getting roles.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
init_iterator (&roles, "SELECT name, uuid FROM roles;");
while (next (&roles))
if (verbose)
printf ("%s %s\n", iterator_string (&roles, 0), iterator_string (&roles, 1));
else
printf ("%s\n", iterator_string (&roles, 0));
cleanup_iterator (&roles);
manage_option_cleanup ();
return 0;
}
/**
* @brief Create a role from an existing role.
*
* @param[in] name Name of new role. NULL to copy from existing.
* @param[in] comment Comment on new role. NULL to copy from existing.
* @param[in] role_id UUID of existing role.
* @param[out] new_role_return New role.
*
* @return 0 success, 1 role exists already, 2 failed to find existing
* role, 99 permission denied, -1 error.
*/
int
copy_role (const char *name, const char *comment, const char *role_id,
role_t *new_role_return)
{
int ret;
role_t new_role, old_role;
sql_begin_immediate ();
if (acl_user_may ("create_role") == 0)
return 99;
if (acl_role_can_super_everyone (role_id))
return 99;
ret = copy_resource_lock ("role", name, comment, role_id, NULL, 1, &new_role,
&old_role);
if (ret)
{
sql_rollback ();
return ret;
}
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource_uuid, resource,"
" resource_location, subject_type, subject, subject_location,"
" creation_time, modification_time)"
" SELECT make_uuid (),"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" name, comment, resource_type,"
" resource_uuid, resource, resource_location, subject_type, %llu,"
" subject_location, m_now (), m_now ()"
" FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
" AND (resource = 0 OR owner IS NULL);",
current_credentials.uuid,
new_role,
old_role);
sql_commit ();
if (new_role_return)
*new_role_return = new_role;
return 0;
}
/**
* @brief Create a role.
*
* @param[in] role_name Role name.
* @param[in] comment Comment on role.
* @param[in] users Users role applies to.
* @param[in] role Role return.
*
* @return 0 success, 1 role exists already, 2 failed to find user, 4 user
* name validation failed, 99 permission denied, -1 error.
*/
int
create_role (const char *role_name, const char *comment, const char *users,
role_t* role)
{
int ret;
gchar *quoted_role_name, *quoted_comment;
assert (current_credentials.uuid);
assert (role_name);
assert (role);
sql_begin_immediate ();
if (acl_user_may ("create_role") == 0)
{
sql_rollback ();
return 99;
}
if (resource_with_name_exists (role_name, "role", 0))
{
sql_rollback ();
return 1;
}
quoted_role_name = sql_quote (role_name);
quoted_comment = comment ? sql_quote (comment) : g_strdup ("");
sql ("INSERT INTO roles"
" (uuid, name, owner, comment, creation_time, modification_time)"
" VALUES"
" (make_uuid (), '%s',"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', m_now (), m_now ());",
quoted_role_name,
current_credentials.uuid,
quoted_comment);
g_free (quoted_comment);
g_free (quoted_role_name);
*role = sql_last_insert_id ();
ret = add_users ("role", *role, users);
if (ret)
sql_rollback ();
else
sql_commit ();
return ret;
}
/**
* @brief Return whether a role is predefined.
*
* @param[in] role Role.
*
* @return 1 if predefined, else 0.
*/
static int
role_is_predefined (role_t role)
{
return sql_int ("SELECT COUNT (*) FROM roles"
" WHERE id = %llu"
" AND (uuid = '" ROLE_UUID_ADMIN "'"
" OR uuid = '" ROLE_UUID_GUEST "'"
" OR uuid = '" ROLE_UUID_MONITOR "'"
" OR uuid = '" ROLE_UUID_INFO "'"
" OR uuid = '" ROLE_UUID_USER "'"
" OR uuid = '" ROLE_UUID_SUPER_ADMIN "'"
" OR uuid = '" ROLE_UUID_OBSERVER "');",
role)
!= 0;
}
/**
* @brief Return whether a role is predefined.
*
* @param[in] uuid UUID of role.
*
* @return 1 if predefined, else 0.
*/
static int
role_is_predefined_id (const char *uuid)
{
return uuid && ((strcmp (uuid, ROLE_UUID_ADMIN) == 0)
|| (strcmp (uuid, ROLE_UUID_GUEST) == 0)
|| (strcmp (uuid, ROLE_UUID_MONITOR) == 0)
|| (strcmp (uuid, ROLE_UUID_INFO) == 0)
|| (strcmp (uuid, ROLE_UUID_USER) == 0)
|| (strcmp (uuid, ROLE_UUID_SUPER_ADMIN) == 0)
|| (strcmp (uuid, ROLE_UUID_OBSERVER) == 0));
}
/**
* @brief Delete a role.
*
* @param[in] role_id UUID of role.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 fail because a task refers to the role, 2 failed
* to find role, 3 predefined role, -1 error.
*/
int
delete_role (const char *role_id, int ultimate)
{
role_t role = 0;
GArray *affected_users;
iterator_t users_iter;
sql_begin_immediate ();
if (acl_user_may ("delete_role") == 0)
{
sql_rollback ();
return 99;
}
if (find_role_with_permission (role_id, &role, "delete_role"))
{
sql_rollback ();
return -1;
}
if (role == 0)
{
if (find_trash ("role", role_id, &role))
{
sql_rollback ();
return -1;
}
if (role == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
if (trash_role_in_use (role))
{
sql_rollback ();
return 1;
}
sql ("DELETE FROM permissions"
" WHERE resource_type = 'role'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
role);
sql ("DELETE FROM permissions_trash"
" WHERE resource_type = 'role'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
role);
sql ("DELETE FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
role);
sql ("DELETE FROM permissions_trash"
" WHERE subject_type = 'role'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
role);
tags_remove_resource ("role", role, LOCATION_TRASH);
sql ("DELETE FROM role_users_trash WHERE role = %llu;", role);
sql ("DELETE FROM roles_trash WHERE id = %llu;", role);
sql_commit ();
return 0;
}
if (role_is_predefined (role))
{
sql_rollback ();
return 3;
}
if (role_in_use (role))
{
sql_rollback ();
return 1;
}
if (ultimate == 0)
{
role_t trash_role;
sql ("INSERT INTO roles_trash"
" (uuid, owner, name, comment, creation_time, modification_time)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time"
" FROM roles WHERE id = %llu;",
role);
trash_role = sql_last_insert_id ();
sql ("INSERT INTO role_users_trash"
" (\"role\", \"user\")"
" SELECT %llu, \"user\""
" FROM role_users WHERE \"role\" = %llu;",
trash_role,
role);
permissions_set_locations ("role", role, trash_role, LOCATION_TRASH);
tags_set_locations ("role", role, trash_role, LOCATION_TRASH);
permissions_set_subjects ("role", role, trash_role, LOCATION_TRASH);
}
else
{
sql ("DELETE FROM permissions"
" WHERE resource_type = 'role'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
role);
sql ("DELETE FROM permissions_trash"
" WHERE resource_type = 'role'"
" AND resource = %llu"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
role);
sql ("DELETE FROM permissions"
" WHERE subject_type = 'role'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
role);
sql ("DELETE FROM permissions_trash"
" WHERE subject_type = 'role'"
" AND subject = %llu"
" AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
role);
tags_remove_resource ("role", role, LOCATION_TABLE);
}
affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
init_iterator (&users_iter,
"SELECT \"user\" FROM role_users"
" WHERE \"role\" = %llu",
role);
while (next (&users_iter))
{
user_t user = iterator_int64 (&users_iter, 0);
g_array_append_val (affected_users, user);
}
cleanup_iterator (&users_iter);
sql ("DELETE FROM role_users WHERE \"role\" = %llu;", role);
sql ("DELETE FROM roles WHERE id = %llu;", role);
cache_all_permissions_for_users (affected_users);
g_array_free (affected_users, TRUE);
sql_commit ();
return 0;
}
/**
* @brief Find a role for a specific permission, given a UUID.
*
* @param[in] uuid UUID of role.
* @param[out] role Role return, 0 if successfully failed to find role.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find role), TRUE on error.
*/
static gboolean
find_role_with_permission (const char* uuid, role_t* role,
const char *permission)
{
return find_resource_with_permission ("role", uuid, role, permission, 0);
}
/**
* @brief Find a role given a name.
*
* @param[in] name A role name.
* @param[out] role Role return, 0 if successfully failed to find role.
*
* @return FALSE on success (including if failed to find role), TRUE on error.
*/
static gboolean
find_role_by_name (const char* name, role_t *role)
{
return find_resource_by_name ("role", name, role);
}
/**
* @brief Gets UUID of role.
*
* @param[in] role Role.
*
* @return Users.
*/
gchar *
role_uuid (role_t role)
{
return sql_string ("SELECT uuid FROM roles WHERE id = %llu;",
role);
}
/**
* @brief Gets users of role as a string.
*
* @param[in] role Role.
*
* @return Users.
*/
gchar *
role_users (role_t role)
{
return sql_string ("SELECT group_concat (name, ', ')"
" FROM (SELECT users.name FROM users, role_users"
" WHERE role_users.role = %llu"
" AND role_users.user = users.id"
" GROUP BY users.name)"
" AS sub;",
role);
}
/**
* @brief Check whether a role is writable.
*
* @param[in] role Role.
*
* @return 1 yes, 0 no.
*/
int
role_writable (role_t role)
{
if (role_is_predefined (role))
return 0;
return 1;
}
/**
* @brief Check whether a trashcan role is writable.
*
* @param[in] role Role.
*
* @return 1 yes, 0 no.
*/
int
trash_role_writable (role_t role)
{
return 1;
}
/**
* @brief Check whether a role is in use.
*
* @param[in] role Role.
*
* @return 1 yes, 0 no.
*/
int
role_in_use (role_t role)
{
return 0;
}
/**
* @brief Check whether a trashcan role is in use.
*
* @param[in] role Role.
*
* @return 1 yes, 0 no.
*/
int
trash_role_in_use (role_t role)
{
return 0;
}
/**
* @brief Modify a role.
*
* @param[in] role_id UUID of role.
* @param[in] name Name of role.
* @param[in] comment Comment on role.
* @param[in] users Role users.
*
* @return 0 success, 1 failed to find role, 2 failed to find user, 3 role_id
* required, 4 user name validation failed, 5 role with new name
* exists, 99 permission denied, -1 internal error.
*/
int
modify_role (const char *role_id, const char *name, const char *comment,
const char *users)
{
int ret;
gchar *quoted_name, *quoted_comment;
role_t role;
GArray *affected_users;
iterator_t users_iter;
assert (current_credentials.uuid);
if (role_id == NULL)
return 3;
sql_begin_immediate ();
if (acl_user_may ("modify_role") == 0)
{
sql_rollback ();
return 99;
}
role = 0;
if (find_role_with_permission (role_id, &role, "modify_role"))
{
sql_rollback ();
return -1;
}
if (role == 0)
{
sql_rollback ();
return 1;
}
/* Check whether a role with the same name exists already. */
if (name)
{
if (resource_with_name_exists (name, "role", role))
{
sql_rollback ();
return 5;
}
}
quoted_name = sql_quote (name ?: "");
quoted_comment = sql_quote (comment ?: "");
sql ("UPDATE roles SET"
" name = '%s',"
" comment = '%s',"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_name,
quoted_comment,
role);
g_free (quoted_comment);
g_free (quoted_name);
affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
init_iterator (&users_iter,
"SELECT \"user\" FROM role_users"
" WHERE \"role\" = %llu",
role);
while (next (&users_iter))
{
user_t user = iterator_int64 (&users_iter, 0);
g_array_append_val (affected_users, user);
}
cleanup_iterator (&users_iter);
sql ("DELETE FROM role_users WHERE \"role\" = %llu;", role);
ret = add_users ("role", role, users);
init_iterator (&users_iter,
"SELECT \"user\" FROM role_users"
" WHERE \"role\" = %llu",
role);
// users not looked for in this loop were removed
// -> possible permissions change
while (next (&users_iter))
{
int index, found_user;
user_t user = iterator_int64 (&users_iter, 0);
found_user = 0;
for (index = 0; index < affected_users->len && found_user == 0; index++)
{
if (g_array_index (affected_users, user_t, index) == user)
{
found_user = 1;
break;
}
}
if (found_user)
{
// users found here stay in the role -> no change in permissions
g_array_remove_index_fast (affected_users, index);
}
else
{
// user added to role -> possible permissions change
g_array_append_val (affected_users, user);
}
}
cleanup_iterator (&users_iter);
cache_all_permissions_for_users (affected_users);
g_array_free (affected_users, TRUE);
if (ret)
sql_rollback ();
else
sql_commit ();
return ret;
}
/**
* @brief Filter columns for role iterator.
*/
#define ROLE_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, NULL }
/**
* @brief Role iterator columns.
*/
#define ROLE_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (roles), \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Role iterator columns for trash case.
*/
#define ROLE_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (roles_trash), \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of roles.
*
* @param[in] get GET params.
*
* @return Total number of roles in roleed set.
*/
int
role_count (const get_data_t *get)
{
static const char *extra_columns[] = ROLE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = ROLE_ITERATOR_COLUMNS;
static column_t trash_columns[] = ROLE_ITERATOR_TRASH_COLUMNS;
return count ("role", get, columns, trash_columns, extra_columns,
0, 0, 0, TRUE);
}
/**
* @brief Initialise a role iterator, including observed roles.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find role, 2 failed to find role (filt_id),
* -1 error.
*/
int
init_role_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = ROLE_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = ROLE_ITERATOR_COLUMNS;
static column_t trash_columns[] = ROLE_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"role",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/* Filters. */
/**
* @brief Find a filter for a specific permission, given a UUID.
*
* @param[in] uuid UUID of filter.
* @param[out] filter Filter return, 0 if successfully failed to find
* filter.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find filter), TRUE on error.
*/
gboolean
find_filter_with_permission (const char* uuid, filter_t* filter,
const char *permission)
{
return find_resource_with_permission ("filter", uuid, filter, permission, 0);
}
/**
* @brief Return the UUID of a filter.
*
* @param[in] filter Filter.
*
* @return Newly allocated UUID if available, else NULL.
*/
char*
filter_uuid (filter_t filter)
{
return sql_string ("SELECT uuid FROM filters WHERE id = %llu;",
filter);
}
/**
* @brief Return the UUID of a trashcan filter.
*
* @param[in] filter Filter.
*
* @return Newly allocated UUID if available, else NULL.
*/
static char*
trash_filter_uuid (filter_t filter)
{
return sql_string ("SELECT uuid FROM filters_trash WHERE id = %llu;",
filter);
}
/**
* @brief Return the name of a filter.
*
* @param[in] filter Filter.
*
* @return name of filter.
*/
char*
filter_name (filter_t filter)
{
return sql_string ("SELECT name FROM filters WHERE id = %llu;",
filter);
}
/**
* @brief Return the name of a trashcan filter.
*
* @param[in] filter Filter.
*
* @return name of filter.
*/
static char*
trash_filter_name (filter_t filter)
{
return sql_string ("SELECT name FROM filters_trash WHERE id = %llu;",
filter);
}
/**
* @brief Return the term of a filter.
*
* @param[in] uuid Filter UUID.
*
* @return Newly allocated term if available, else NULL.
*/
gchar*
filter_term (const char *uuid)
{
gchar *quoted_uuid, *ret;
quoted_uuid = sql_quote (uuid);
ret = sql_string ("SELECT term FROM filters WHERE uuid = '%s';",
quoted_uuid);
g_free (quoted_uuid);
return ret;
}
/**
* @brief Return the value of a column keyword of a filter term.
*
* @param[in] term Filter term.
* @param[in] column Column name.
*
* @return Value of column keyword if one exists, else NULL.
*/
gchar*
filter_term_value (const char *term, const char *column)
{
keyword_t **point;
array_t *split;
if (term == NULL)
return NULL;
split = split_filter (term);
point = (keyword_t**) split->pdata;
while (*point)
{
keyword_t *keyword;
keyword = *point;
if (keyword->column
&& ((strcasecmp (keyword->column, column) == 0)
|| (keyword->column[0] == '_'
&& (strcasecmp (keyword->column + 1, column) == 0))))
{
gchar *ret = g_strdup (keyword->string);
filter_free (split);
return ret;
}
point++;
}
filter_free (split);
return NULL;
}
/**
* @brief Return the value of the apply_overrides keyword of a filter term.
*
* @param[in] term Filter term.
*
* @return Value of apply_overrides if it exists, else APPLY_OVERRIDES_DEFAULT.
*/
int
filter_term_apply_overrides (const char *term)
{
if (term)
{
int ret;
gchar *apply_overrides_str;
apply_overrides_str = filter_term_value (term, "apply_overrides");
ret = apply_overrides_str
? (strcmp (apply_overrides_str, "0") ? 1 : 0)
: APPLY_OVERRIDES_DEFAULT;
g_free (apply_overrides_str);
return ret;
}
else
return APPLY_OVERRIDES_DEFAULT;
}
/**
* @brief Return the value of the min_qod keyword of a filter term.
*
* @param[in] term Filter term.
*
* @return Value of min_qod if it exists, else MIN_QOD_DEFAULT.
*/
int
filter_term_min_qod (const char *term)
{
if (term)
{
int ret;
gchar *min_qod_str;
min_qod_str = filter_term_value (term, "min_qod");
ret = (min_qod_str && strcmp (min_qod_str, ""))
? atoi (min_qod_str) : MIN_QOD_DEFAULT;
g_free (min_qod_str);
return ret;
}
else
return MIN_QOD_DEFAULT;
}
/**
* @brief Create a filter.
*
* @param[in] name Name of filter.
* @param[in] comment Comment on filter.
* @param[in] type Type of resource.
* @param[in] term Filter term.
* @param[out] filter Created filter.
*
* @return 0 success, 1 filter exists already, 2 error in type, 99 permission
* denied.
*/
int
create_filter (const char *name, const char *comment, const char *type,
const char *term, filter_t* filter)
{
gchar *quoted_name, *quoted_comment, *quoted_term, *clean_term;
assert (current_credentials.uuid);
if (type && strlen (type))
{
type = type_db_name (type);
if (type == NULL || !valid_type (type))
return 2;
}
sql_begin_immediate ();
if (acl_user_may ("create_filter") == 0)
{
sql_rollback ();
return 99;
}
if (resource_with_name_exists (name, "filter", 0))
{
sql_rollback ();
return 1;
}
quoted_name = sql_quote (name ?: "");
clean_term = manage_clean_filter (term ? term : "");
quoted_term = sql_quote (clean_term);
g_free (clean_term);
if (comment)
{
quoted_comment = sql_quote (comment);
sql ("INSERT INTO filters"
" (uuid, name, owner, comment, type, term, creation_time,"
" modification_time)"
" VALUES (make_uuid (), '%s',"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '%s', %s%s%s, '%s', m_now (), m_now ());",
quoted_name,
current_credentials.uuid,
quoted_comment,
type ? "lower ('" : "",
type ? type : "''",
type ? "')" : "",
quoted_term);
g_free (quoted_comment);
}
else
sql ("INSERT INTO filters"
" (uuid, name, owner, comment, type, term, creation_time,"
" modification_time)"
" VALUES (make_uuid (), '%s',"
" (SELECT id FROM users WHERE users.uuid = '%s'),"
" '', %s%s%s, '%s', m_now (), m_now ());",
quoted_name,
current_credentials.uuid,
type ? "lower ('" : "",
type ? type : "''",
type ? "')" : "",
quoted_term);
if (filter)
*filter = sql_last_insert_id ();
g_free (quoted_name);
g_free (quoted_term);
sql_commit ();
return 0;
}
/**
* @brief Create a filter from an existing filter.
*
* @param[in] name Name of new filter. NULL to copy from existing.
* @param[in] comment Comment on new filter. NULL to copy from existing.
* @param[in] filter_id UUID of existing filter.
* @param[out] new_filter New filter.
*
* @return 0 success, 1 filter exists already, 2 failed to find existing
* filter, -1 error.
*/
int
copy_filter (const char* name, const char* comment, const char *filter_id,
filter_t* new_filter)
{
return copy_resource ("filter", name, comment, filter_id, "term, type",
1, new_filter, NULL);
}
/**
* @brief Delete a filter.
*
* @param[in] filter_id UUID of filter.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 1 fail because a task refers to the filter, 2 failed
* to find filter, 99 permission denied, -1 error.
*/
int
delete_filter (const char *filter_id, int ultimate)
{
gchar *quoted_filter_id;
filter_t filter = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_filter") == 0)
{
sql_rollback ();
return 99;
}
if (find_filter_with_permission (filter_id, &filter, "delete_filter"))
{
sql_rollback ();
return -1;
}
if (filter == 0)
{
if (find_trash ("filter", filter_id, &filter))
{
sql_rollback ();
return -1;
}
if (filter == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
/* Check if it's in use by an alert in the trashcan. */
if (sql_int ("SELECT count(*) FROM alerts_trash"
" WHERE filter = %llu"
" AND filter_location = " G_STRINGIFY (LOCATION_TRASH) ";",
filter))
{
sql_rollback ();
return 1;
}
/* Check if it's in use by the condition of an alert in the trashcan. */
if (sql_int ("SELECT count(*) FROM alert_condition_data_trash"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid FROM filters_trash"
" WHERE id = %llu)"
" AND (SELECT condition = %i OR condition = %i"
" FROM alerts_trash WHERE id = alert);",
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED))
{
sql_rollback ();
return 1;
}
permissions_set_orphans ("filter", filter, LOCATION_TRASH);
tags_remove_resource ("filter", filter, LOCATION_TRASH);
sql ("DELETE FROM filters_trash WHERE id = %llu;", filter);
sql_commit ();
return 0;
}
/* Check if it's in use by an alert. */
if (sql_int ("SELECT count(*) FROM alerts"
" WHERE filter = %llu;",
filter))
{
sql_rollback ();
return 1;
}
/* Check if it's in use by the condition of an alert. */
if (sql_int ("SELECT count(*) FROM alert_condition_data"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid FROM filters"
" WHERE id = %llu)"
" AND (SELECT condition = %i OR condition = %i"
" FROM alerts WHERE id = alert);",
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED))
{
sql_rollback ();
return 1;
}
if (ultimate)
{
/* Check if it's in use by the condition of an alert in the trashcan. */
if (sql_int ("SELECT count(*) FROM alert_condition_data_trash"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid FROM filters"
" WHERE id = %llu)"
" AND (SELECT condition = %i OR condition = %i"
" FROM alerts_trash WHERE id = alert);",
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED))
{
sql_rollback ();
return 1;
}
}
quoted_filter_id = sql_quote (filter_id);
sql ("DELETE FROM settings WHERE name %s '%% Filter' AND value = '%s';",
sql_ilike_op (),
quoted_filter_id);
g_free (quoted_filter_id);
if (ultimate == 0)
{
sql ("INSERT INTO filters_trash"
" (uuid, owner, name, comment, type, term, creation_time,"
" modification_time)"
" SELECT uuid, owner, name, comment, type, term, creation_time,"
" modification_time"
" FROM filters WHERE id = %llu;",
filter);
/* Update the location of the filter in any trashcan alerts. */
sql ("UPDATE alerts_trash"
" SET filter = %llu,"
" filter_location = " G_STRINGIFY (LOCATION_TRASH)
" WHERE filter = %llu"
" AND filter_location = " G_STRINGIFY (LOCATION_TABLE) ";",
sql_last_insert_id (),
filter);
permissions_set_locations ("filter", filter,
sql_last_insert_id (),
LOCATION_TRASH);
tags_set_locations ("filter", filter,
sql_last_insert_id (),
LOCATION_TRASH);
}
else
{
permissions_set_orphans ("filter", filter, LOCATION_TABLE);
tags_remove_resource ("filter", filter, LOCATION_TABLE);
}
sql ("DELETE FROM filters WHERE id = %llu;", filter);
sql_commit ();
return 0;
}
/**
* @brief Check whether a filter is in use.
*
* @param[in] filter Filter.
*
* @return 1 yes, 0 no.
*/
int
filter_in_use (filter_t filter)
{
return !!sql_int ("SELECT count (*) FROM alerts"
/* Filter applied to results passed to alert's "generate". */
" WHERE filter = %llu"
/* Filter applied to check alert condition. */
" OR (EXISTS (SELECT * FROM alert_condition_data"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid FROM filters"
" WHERE id = %llu)"
" AND alert = alerts.id)"
" AND (condition = %i OR condition = %i))",
filter,
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED);
}
/**
* @brief Check whether a filter is in use for the output of any alert.
*
* @param[in] filter Filter.
*
* @return 1 yes, 0 no.
*/
static int
filter_in_use_for_output (filter_t filter)
{
return !!sql_int ("SELECT count (*) FROM alerts"
" WHERE filter = %llu;",
filter);
}
/**
* @brief Check whether a filter is in use by any result alert conditions.
*
* @param[in] filter Filter.
*
* @return 1 yes, 0 no.
*/
static int
filter_in_use_for_result_event (filter_t filter)
{
return !!sql_int ("SELECT count (*) FROM alerts"
" WHERE event = %llu"
" AND (EXISTS (SELECT * FROM alert_condition_data"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid FROM filters"
" WHERE id = %llu)"
" AND alert = alerts.id)"
" AND (condition = %i OR condition = %i))",
EVENT_TASK_RUN_STATUS_CHANGED,
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED);
}
/**
* @brief Check whether a filter is in use by any secinfo alert conditions.
*
* @param[in] filter Filter.
*
* @return 1 yes, 0 no.
*/
static int
filter_in_use_for_secinfo_event (filter_t filter)
{
return !!sql_int ("SELECT count (*) FROM alerts"
" WHERE (event = %llu OR event = %llu)"
" AND (EXISTS (SELECT * FROM alert_condition_data"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid FROM filters"
" WHERE id = %llu)"
" AND alert = alerts.id)"
" AND (condition = %i OR condition = %i))",
EVENT_NEW_SECINFO,
EVENT_UPDATED_SECINFO,
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED);
}
/**
* @brief Check whether a trashcan filter is in use.
*
* @param[in] filter Filter.
*
* @return 1 yes, 0 no.
*/
int
trash_filter_in_use (filter_t filter)
{
return !!sql_int ("SELECT count (*) FROM alerts_trash"
" WHERE (filter = %llu"
" AND filter_location = "
G_STRINGIFY (LOCATION_TRASH) ")"
" OR (EXISTS (SELECT *"
" FROM alert_condition_data_trash"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid"
" FROM filters_trash"
" WHERE id = %llu)"
" AND alert = alerts_trash.id)"
" AND (condition = %i OR condition = %i))",
filter,
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED);
}
/**
* @brief Check whether a filter is writable.
*
* @param[in] filter Filter.
*
* @return 1 yes, 0 no.
*/
int
filter_writable (filter_t filter)
{
return 1;
}
/**
* @brief Check whether a trashcan filter is writable.
*
* @param[in] filter Filter.
*
* @return 1 yes, 0 no.
*/
int
trash_filter_writable (filter_t filter)
{
return 1;
}
/**
* @brief Filter columns for filter iterator.
*/
#define FILTER_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "type", "term", NULL }
/**
* @brief Filter iterator columns.
*/
#define FILTER_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (filters), \
{ "type" , NULL, KEYWORD_TYPE_STRING }, \
{ "term", NULL, KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Filter iterator columns for trash case.
*/
#define FILTER_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (filters_trash), \
{ "type" , NULL, KEYWORD_TYPE_STRING }, \
{ "term", NULL, KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of filters.
*
* @param[in] get GET params.
*
* @return Total number of filters in filtered set.
*/
int
filter_count (const get_data_t *get)
{
static const char *filter_columns[] = FILTER_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = FILTER_ITERATOR_COLUMNS;
static column_t trash_columns[] = FILTER_ITERATOR_TRASH_COLUMNS;
return count ("filter", get, columns, trash_columns, filter_columns,
0, 0, 0, TRUE);
}
/**
* @brief Initialise a filter iterator, including observed filters.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find filter, 2 failed to find filter (filt_id),
* -1 error.
*/
int
init_filter_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = FILTER_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = FILTER_ITERATOR_COLUMNS;
static column_t trash_columns[] = FILTER_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"filter",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Get the type from a filter iterator.
*
* @param[in] iterator Iterator.
*
* @return The type of the filter, or NULL if iteration is complete. Freed by
* cleanup_iterator. "" for any type.
*/
const char*
filter_iterator_type (iterator_t* iterator)
{
const char *ret;
if (iterator->done) return NULL;
ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT);
return ret ? ret : "";
}
/**
* @brief Get the term from a filter iterator.
*
* @param[in] iterator Iterator.
*
* @return The term of the filter, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (filter_iterator_term, GET_ITERATOR_COLUMN_COUNT + 1);
/**
* @brief Initialise a filter alert iterator.
*
* Iterates over all alerts that use the filter.
*
* @param[in] iterator Iterator.
* @param[in] filter Filter.
*/
void
init_filter_alert_iterator (iterator_t* iterator, filter_t filter)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (filter);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_alerts"));
available = acl_where_owned ("alert", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT name, uuid, %s FROM alerts"
" WHERE filter = %llu"
" OR (EXISTS (SELECT * FROM alert_condition_data"
" WHERE name = 'filter_id'"
" AND data = (SELECT uuid FROM filters"
" WHERE id = %llu)"
" AND alert = alerts.id)"
" AND (condition = %i OR condition = %i))"
" ORDER BY name ASC;",
with_clause ? with_clause : "",
available,
filter,
filter,
ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
ALERT_CONDITION_FILTER_COUNT_CHANGED);
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the name from a filter_alert iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the host, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (filter_alert_iterator_name, 0);
/**
* @brief Get the UUID from a filter_alert iterator.
*
* @param[in] iterator Iterator.
*
* @return The UUID of the host, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (filter_alert_iterator_uuid, 1);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
filter_alert_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 2);
}
/**
* @brief Modify a filter.
*
* @param[in] filter_id UUID of filter.
* @param[in] name Name of filter.
* @param[in] comment Comment on filter.
* @param[in] term Filter term.
* @param[in] type Type of filter.
*
* @return 0 success, 1 failed to find filter, 2 filter with new name exists,
* 3 error in type name, 4 filter_id required, 5 filter is in use so
* type must be "result", 6 filter is in use so type must be "info",
* 99 permission denied, -1 internal error.
*/
int
modify_filter (const char *filter_id, const char *name, const char *comment,
const char *term, const char *type)
{
gchar *quoted_name, *quoted_comment, *quoted_term, *quoted_type, *clean_term;
filter_t filter;
if (filter_id == NULL)
return 4;
type = type_db_name (type);
if (type && !((strcmp (type, "") == 0) || valid_type (type)))
return 3;
sql_begin_immediate ();
assert (current_credentials.uuid);
if (acl_user_may ("modify_filter") == 0)
{
sql_rollback ();
return 99;
}
filter = 0;
if (find_filter_with_permission (filter_id, &filter, "modify_filter"))
{
sql_rollback ();
return -1;
}
if (filter == 0)
{
sql_rollback ();
return 1;
}
/* If the filter is linked to an alert, check that the type is valid. */
if ((filter_in_use_for_output (filter)
|| filter_in_use_for_result_event (filter))
&& type
&& strcasecmp (type, "result"))
{
sql_rollback ();
return 5;
}
if (filter_in_use_for_secinfo_event (filter)
&& type
&& strcasecmp (type, "info"))
{
sql_rollback ();
return 6;
}
/* Check whether a filter with the same name exists already. */
if (name)
{
if (resource_with_name_exists (name, "filter", filter))
{
sql_rollback ();
return 2;
}
}
quoted_name = sql_quote(name ?: "");
clean_term = manage_clean_filter (term ? term : "");
quoted_term = sql_quote (clean_term);
g_free (clean_term);
quoted_comment = sql_quote (comment ? comment : "");
quoted_type = sql_quote (type ? type : "");
sql ("UPDATE filters SET"
" name = '%s',"
" comment = '%s',"
" term = '%s',"
" type = lower ('%s'),"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_name,
quoted_comment,
quoted_term,
quoted_type,
filter);
g_free (quoted_comment);
g_free (quoted_name);
g_free (quoted_term);
g_free (quoted_type);
sql_commit ();
return 0;
}
/* Schema. */
/**
* @brief Generate the GMP schema.
*
* @param[in] format Name of schema format, "XML" or NULL for XML.
* @param[out] output_return NULL or location for output.
* @param[out] output_length NULL or location for length of output.
* @param[out] extension NULL or location for schema extension.
* @param[out] content_type NULL or location for schema content type.
*
* @return 0 success, 1 failed to find schema format, -1 error.
*/
int
manage_schema (gchar *format, gchar **output_return, gsize *output_length,
gchar **extension, gchar **content_type)
{
/* Pass the XML file to the schema generate script, sending the output
* to a file. */
{
gchar *script, *script_dir;
gchar *uuid_format;
char output_dir[] = "/tmp/gvmd_schema_XXXXXX";
if (mkdtemp (output_dir) == NULL)
{
g_warning ("%s: mkdtemp failed", __func__);
return -1;
}
/* Setup file names. */
if (format == NULL)
{
if (extension)
*extension = g_strdup ("xml");
if (content_type)
*content_type = g_strdup ("text/xml");
uuid_format = "18e826fc-dab6-11df-b913-002264764cea";
}
else if (strcasecmp (format, "HTML") == 0)
{
if (extension)
*extension = g_strdup ("html");
if (content_type)
*content_type = g_strdup ("text/html");
uuid_format = "02052818-dab6-11df-9be4-002264764cea";
}
else if (strcasecmp (format, "RNC") == 0)
{
if (extension)
*extension = g_strdup ("rnc");
if (content_type)
*content_type = g_strdup ("text/x-rnc");
uuid_format = "787a4a18-dabc-11df-9486-002264764cea";
}
else if (strcasecmp (format, "XML") == 0)
{
if (extension)
*extension = g_strdup ("xml");
if (content_type)
*content_type = g_strdup ("text/xml");
uuid_format = "18e826fc-dab6-11df-b913-002264764cea";
}
else
return 1;
script_dir = g_build_filename (GVMD_DATA_DIR,
"global_schema_formats",
uuid_format,
NULL);
script = g_build_filename (script_dir, "generate", NULL);
if (!gvm_file_is_readable (script))
{
g_free (script);
g_free (script_dir);
if (extension) g_free (*extension);
if (content_type) g_free (*content_type);
return -1;
}
{
gchar *output_file, *command;
char *previous_dir;
int ret;
/* Change into the script directory. */
previous_dir = getcwd (NULL, 0);
if (previous_dir == NULL)
{
g_warning ("%s: Failed to getcwd: %s",
__func__,
strerror (errno));
g_free (previous_dir);
g_free (script);
g_free (script_dir);
if (extension) g_free (*extension);
if (content_type) g_free (*content_type);
return -1;
}
if (chdir (script_dir))
{
g_warning ("%s: Failed to chdir: %s",
__func__,
strerror (errno));
g_free (previous_dir);
g_free (script);
g_free (script_dir);
if (extension) g_free (*extension);
if (content_type) g_free (*content_type);
return -1;
}
g_free (script_dir);
output_file = g_strdup_printf ("%s/report.out", output_dir);
/* Call the script. */
command = g_strdup_printf ("%s " GVMD_DATA_DIR
"/global_schema_formats"
"/18e826fc-dab6-11df-b913-002264764cea/GMP.xml"
" > %s"
" 2> /dev/null",
script,
output_file);
g_free (script);
g_debug (" command: %s", command);
ret = system (command);
if ((ret == -1)
/* The schema "generate" script must exit with 0. */
|| WEXITSTATUS (ret))
{
g_warning ("%s: system failed with ret %i, %i, %s",
__func__,
ret,
WEXITSTATUS (ret),
command);
if (chdir (previous_dir))
g_warning ("%s: and chdir failed",
__func__);
g_free (previous_dir);
g_free (command);
g_free (output_file);
if (extension) g_free (*extension);
if (content_type) g_free (*content_type);
return -1;
}
{
GError *get_error;
gchar *output;
gsize output_len;
g_free (command);
/* Change back to the previous directory. */
if (chdir (previous_dir))
{
g_warning ("%s: Failed to chdir back: %s",
__func__,
strerror (errno));
g_free (previous_dir);
if (extension) g_free (*extension);
if (content_type) g_free (*content_type);
return -1;
}
g_free (previous_dir);
/* Read the script output from file. */
get_error = NULL;
g_file_get_contents (output_file,
&output,
&output_len,
&get_error);
g_free (output_file);
if (get_error)
{
g_warning ("%s: Failed to get output: %s",
__func__,
get_error->message);
g_error_free (get_error);
if (extension) g_free (*extension);
if (content_type) g_free (*content_type);
return -1;
}
/* Remove the output directory. */
gvm_file_remove_recurse (output_dir);
/* Return the output. */
if (output_length) *output_length = output_len;
if (output_return) *output_return = output;
return 0;
}
}
}
}
/* Trashcan. */
/**
* @brief Restore a resource from the trashcan.
*
* @param[in] id UUID of resource.
*
* @return 0 success, 1 fail because the resource refers to another resource
* in the trashcan, 2 failed to find resource in trashcan, 3 fail
* because resource with such name exists already, 4 fail because
* resource with UUID exists already, 99 permission denied, -1 error.
*/
int
manage_restore (const char *id)
{
resource_t resource = 0;
int ret;
assert (current_credentials.uuid);
sql_begin_immediate ();
if (acl_user_may ("restore") == 0)
{
sql_rollback ();
return 99;
}
/* Port List. */
ret = restore_port_list (id);
if (ret != 2)
return ret;
/* Report Format. */
ret = restore_report_format (id);
if (ret != 2)
return ret;
/* Ticket. */
ret = restore_ticket (id);
if (ret != 2)
return ret;
/* Config. */
if (find_trash ("config", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
config_t config;
if (sql_int ("SELECT count(*) FROM configs"
" WHERE name ="
" (SELECT name FROM configs_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
/* Check if it uses a scanner in the trashcan. */
if (sql_int ("SELECT scanner_location = " G_STRINGIFY (LOCATION_TRASH)
" FROM configs_trash WHERE id = %llu;",
resource))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO configs"
" (uuid, owner, name, nvt_selector, comment, family_count,"
" nvt_count, families_growing, nvts_growing, type, scanner,"
" predefined, creation_time, modification_time, usage_type)"
" SELECT uuid, owner, name, nvt_selector, comment, family_count,"
" nvt_count, families_growing, nvts_growing, type, scanner,"
" predefined, creation_time, modification_time, usage_type"
" FROM configs_trash WHERE id = %llu;",
resource);
config = sql_last_insert_id ();
sql ("INSERT INTO config_preferences"
" (config, type, name, value, default_value, hr_name)"
" SELECT %llu, type, name, value, default_value, hr_name"
" FROM config_preferences_trash WHERE config = %llu;",
config,
resource);
/* Update the config in any trashcan tasks. */
sql ("UPDATE tasks"
" SET config = %llu,"
" config_location = " G_STRINGIFY (LOCATION_TABLE)
" WHERE config = %llu"
" AND config_location = " G_STRINGIFY (LOCATION_TRASH),
config,
resource);
permissions_set_locations ("config", resource, config,
LOCATION_TABLE);
tags_set_locations ("config", resource, config,
LOCATION_TABLE);
sql ("DELETE FROM config_preferences_trash WHERE config = %llu;",
resource);
sql ("DELETE FROM configs_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Alert. */
if (find_trash ("alert", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
alert_t alert;
if (sql_int ("SELECT count(*) FROM alerts"
" WHERE name ="
" (SELECT name FROM alerts_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
/* Check if it uses a filter in the trashcan. */
if (sql_int ("SELECT filter_location = " G_STRINGIFY (LOCATION_TRASH)
" FROM alerts_trash WHERE id = %llu;",
resource))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO alerts"
" (uuid, owner, name, comment, event, condition, method, filter,"
" active, creation_time, modification_time)"
" SELECT uuid, owner, name, comment, event, condition, method,"
" filter, active, creation_time, modification_time"
" FROM alerts_trash WHERE id = %llu;",
resource);
alert = sql_last_insert_id ();
sql ("INSERT INTO alert_condition_data"
" (alert, name, data)"
" SELECT %llu, name, data"
" FROM alert_condition_data_trash WHERE alert = %llu;",
alert,
resource);
sql ("INSERT INTO alert_event_data"
" (alert, name, data)"
" SELECT %llu, name, data"
" FROM alert_event_data_trash WHERE alert = %llu;",
alert,
resource);
sql ("INSERT INTO alert_method_data"
" (alert, name, data)"
" SELECT %llu, name, data"
" FROM alert_method_data_trash WHERE alert = %llu;",
alert,
resource);
/* Update the alert in any trashcan tasks. */
sql ("UPDATE task_alerts"
" SET alert = %llu,"
" alert_location = " G_STRINGIFY (LOCATION_TABLE)
" WHERE alert = %llu"
" AND alert_location = " G_STRINGIFY (LOCATION_TRASH),
alert,
resource);
permissions_set_locations ("alert", resource, alert,
LOCATION_TABLE);
tags_set_locations ("alert", resource, alert,
LOCATION_TABLE);
sql ("DELETE FROM alert_condition_data_trash WHERE alert = %llu;",
resource);
sql ("DELETE FROM alert_event_data_trash WHERE alert = %llu;",
resource);
sql ("DELETE FROM alert_method_data_trash WHERE alert = %llu;",
resource);
sql ("DELETE FROM alerts_trash WHERE id = %llu;",
resource);
sql_commit ();
return 0;
}
/* Filter. */
if (find_trash ("filter", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
if (sql_int ("SELECT count(*) FROM filters"
" WHERE name ="
" (SELECT name FROM filters_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
sql ("INSERT INTO filters"
" (uuid, owner, name, comment, type, term, creation_time,"
" modification_time)"
" SELECT uuid, owner, name, comment, type, term, creation_time,"
" modification_time"
" FROM filters_trash WHERE id = %llu;",
resource);
/* Update the filter in any trashcan alerts. */
sql ("UPDATE alerts_trash"
" SET filter = %llu,"
" filter_location = " G_STRINGIFY (LOCATION_TABLE)
" WHERE filter = %llu"
" AND filter_location = " G_STRINGIFY (LOCATION_TRASH),
sql_last_insert_id (),
resource);
permissions_set_locations ("filter", resource,
sql_last_insert_id (),
LOCATION_TABLE);
tags_set_locations ("filter", resource,
sql_last_insert_id (),
LOCATION_TABLE);
sql ("DELETE FROM filters_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Group. */
if (find_trash ("group", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
group_t group;
GArray *affected_users;
iterator_t users_iter;
if (sql_int ("SELECT count(*) FROM groups"
" WHERE name ="
" (SELECT name FROM groups_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
sql ("INSERT INTO groups"
" (uuid, owner, name, comment, creation_time,"
" modification_time)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time"
" FROM groups_trash WHERE id = %llu;",
resource);
group = sql_last_insert_id ();
sql ("INSERT INTO group_users"
" (\"group\", \"user\")"
" SELECT %llu, \"user\""
" FROM group_users_trash WHERE \"group\" = %llu;",
group,
resource);
permissions_set_locations ("group", resource, group, LOCATION_TABLE);
tags_set_locations ("group", resource, group, LOCATION_TABLE);
permissions_set_subjects ("group", resource, group, LOCATION_TABLE);
sql ("DELETE FROM group_users_trash WHERE \"group\" = %llu;", resource);
sql ("DELETE FROM groups_trash WHERE id = %llu;", resource);
affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
init_iterator (&users_iter,
"SELECT \"user\" FROM group_users"
" WHERE \"group\" = %llu",
group);
while (next (&users_iter))
{
user_t user = iterator_int64 (&users_iter, 0);
g_array_append_val (affected_users, user);
}
cleanup_iterator (&users_iter);
cache_all_permissions_for_users (affected_users);
g_array_free (affected_users, TRUE);
sql_commit ();
return 0;
}
/* Credential. */
if (find_trash ("credential", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
credential_t credential;
if (sql_int ("SELECT count(*) FROM credentials"
" WHERE name ="
" (SELECT name FROM credentials_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
sql ("INSERT INTO credentials"
" (uuid, owner, name, comment, creation_time,"
" modification_time, type)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time, type"
" FROM credentials_trash WHERE id = %llu;",
resource);
credential = sql_last_insert_id ();
sql ("INSERT INTO credentials_data"
" (credential, type, value)"
" SELECT %llu, type, value"
" FROM credentials_trash_data"
" WHERE credential = %llu",
credential,
resource);
/* Update the credentials in any trashcan targets. */
sql ("UPDATE targets_trash_login_data"
" SET credential_location = " G_STRINGIFY (LOCATION_TABLE) ","
" credential = %llu"
" WHERE credential = %llu"
" AND credential_location = " G_STRINGIFY (LOCATION_TRASH) ";",
credential,
resource);
sql ("UPDATE scanners_trash"
" SET credential_location = " G_STRINGIFY (LOCATION_TABLE) ","
" credential = %llu"
" WHERE credential = %llu"
" AND credential_location = " G_STRINGIFY (LOCATION_TRASH) ";",
credential,
resource);
permissions_set_locations ("credential", resource, credential,
LOCATION_TABLE);
tags_set_locations ("credential", resource, credential,
LOCATION_TABLE);
sql ("DELETE FROM credentials_trash_data WHERE credential = %llu;",
resource);
sql ("DELETE FROM credentials_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Note. */
if (find_trash ("note", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
sql ("INSERT INTO notes"
" (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
" port, severity, task, result, end_time)"
" SELECT uuid, owner, nvt, creation_time, modification_time, text,"
" hosts, port, severity, task, result, end_time"
" FROM notes_trash WHERE id = %llu;",
resource);
permissions_set_locations ("note", resource,
sql_last_insert_id (),
LOCATION_TABLE);
tags_set_locations ("note", resource,
sql_last_insert_id (),
LOCATION_TABLE);
sql ("DELETE FROM notes_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Override. */
if (find_trash ("override", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
override_t override;
GHashTable *reports;
GHashTableIter reports_iter;
report_t *reports_ptr;
gchar *users_where;
int auto_cache_rebuild;
sql ("INSERT INTO overrides"
" (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
" port, severity, new_severity, task, result, end_time, result_nvt)"
" SELECT uuid, owner, nvt, creation_time, modification_time, text,"
" hosts, port, severity, new_severity, task,"
" result, end_time, result_nvt"
" FROM overrides_trash WHERE id = %llu;",
resource);
override = sql_last_insert_id ();
permissions_set_locations ("override", resource,
override,
LOCATION_TABLE);
tags_set_locations ("override", resource,
override,
LOCATION_TABLE);
users_where = acl_users_with_access_where ("override", id, NULL,
"id");
reports = reports_for_override (override);
g_hash_table_iter_init (&reports_iter, reports);
reports_ptr = NULL;
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, 0, 1, users_where);
else
report_clear_count_cache (*reports_ptr, 0, 1, users_where);
}
g_hash_table_destroy (reports);
g_free (users_where);
sql ("DELETE FROM overrides_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Permission. */
if (find_trash ("permission", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
permission_t permission;
char *name, *resource_type;
resource_t perm_resource;
GHashTable *reports = NULL;
int clear_original = 0;
char *subject_type;
resource_t subject;
gchar *subject_where;
sql ("INSERT INTO permissions"
" (uuid, owner, name, comment, resource_type, resource,"
" resource_uuid, resource_location, subject_type, subject,"
" subject_location, creation_time, modification_time)"
" SELECT uuid, owner, name, comment, resource_type, resource,"
" resource_uuid, resource_location, subject_type, subject,"
" subject_location, creation_time, modification_time"
" FROM permissions_trash"
" WHERE id = %llu;",
resource);
permission = sql_last_insert_id ();
name = permission_name (permission);
resource_type = permission_resource_type (permission);
perm_resource = permission_resource (permission);
subject_type = permission_subject_type (permission);
subject = permission_subject (permission);
subject_where = subject_where_clause (subject_type, subject);
free (subject_type);
tags_set_locations ("permission", resource, permission, LOCATION_TABLE);
if (strcasecmp (name, "super") == 0)
cache_all_permissions_for_users (NULL);
else if (perm_resource != 0
&& resource_type && strcmp (resource_type, ""))
cache_permissions_for_resource (resource_type, perm_resource, NULL);
/* Update Reports cache */
if (resource_type && perm_resource
&& strcmp (resource_type, "override") == 0)
{
reports = reports_for_override (perm_resource);
}
else if (strcasecmp (name, "super") == 0)
{
reports = reports_hashtable ();
clear_original = 1;
}
if (reports && g_hash_table_size (reports))
{
GHashTableIter reports_iter;
report_t *reports_ptr;
int auto_cache_rebuild;
reports_ptr = NULL;
g_hash_table_iter_init (&reports_iter, reports);
auto_cache_rebuild = setting_auto_cache_rebuild_int ();
while (g_hash_table_iter_next (&reports_iter,
((gpointer*)&reports_ptr), NULL))
{
if (auto_cache_rebuild)
report_cache_counts (*reports_ptr, clear_original, 1,
subject_where);
else
report_clear_count_cache (*reports_ptr, clear_original, 1,
subject_where);
}
}
g_free (subject_where);
if (reports)
g_hash_table_destroy (reports);
free (name);
free (resource_type);
sql ("DELETE FROM permissions_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Role. */
if (find_trash ("role", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
role_t role;
GArray *affected_users;
iterator_t users_iter;
if (sql_int ("SELECT count(*) FROM roles"
" WHERE name ="
" (SELECT name FROM roles_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
sql ("INSERT INTO roles"
" (uuid, owner, name, comment, creation_time,"
" modification_time)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time"
" FROM roles_trash WHERE id = %llu;",
resource);
role = sql_last_insert_id ();
sql ("INSERT INTO role_users"
" (\"role\", \"user\")"
" SELECT %llu, \"user\""
" FROM role_users_trash WHERE role = %llu;",
role,
resource);
permissions_set_locations ("role", resource, role, LOCATION_TABLE);
tags_set_locations ("role", resource, role, LOCATION_TABLE);
permissions_set_subjects ("role", resource, role, LOCATION_TABLE);
sql ("DELETE FROM role_users_trash WHERE role = %llu;", resource);
sql ("DELETE FROM roles_trash WHERE id = %llu;", resource);
affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
init_iterator (&users_iter,
"SELECT \"user\" FROM role_users"
" WHERE \"role\" = %llu",
role);
while (next (&users_iter))
{
user_t user = iterator_int64 (&users_iter, 0);
g_array_append_val (affected_users, user);
}
cleanup_iterator (&users_iter);
cache_all_permissions_for_users (affected_users);
g_array_free (affected_users, TRUE);
sql_commit ();
return 0;
}
/* Scanner. */
if (find_trash ("scanner", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
scanner_t scanner;
if (sql_int ("SELECT count(*) FROM scanners"
" WHERE name ="
" (SELECT name FROM scanners_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource, current_credentials.uuid))
{
sql_rollback ();
return 3;
}
if (sql_int ("SELECT credential_location = " G_STRINGIFY (LOCATION_TRASH)
" FROM scanners_trash WHERE id = %llu;",
resource))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO scanners"
" (uuid, owner, name, comment, host, port, type, ca_pub,"
" credential, creation_time, modification_time)"
" SELECT uuid, owner, name, comment, host, port, type, ca_pub,"
" credential, creation_time, modification_time"
" FROM scanners_trash WHERE id = %llu;", resource);
/* Update the scanner in any trashcan configs and tasks. */
scanner = sql_last_insert_id ();
sql ("UPDATE configs_trash"
" SET scanner = %llu,"
" scanner_location = " G_STRINGIFY (LOCATION_TABLE)
" WHERE scanner = %llu"
" AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
scanner, resource);
sql ("UPDATE tasks"
" SET scanner = %llu,"
" scanner_location = " G_STRINGIFY (LOCATION_TABLE)
" WHERE scanner = %llu"
" AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
scanner, resource);
permissions_set_locations ("scanner", resource,
sql_last_insert_id (),
LOCATION_TABLE);
tags_set_locations ("scanner", resource,
sql_last_insert_id (), LOCATION_TABLE);
sql ("DELETE FROM scanners_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Schedule. */
if (find_trash ("schedule", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
if (sql_int ("SELECT count(*) FROM schedules"
" WHERE name ="
" (SELECT name FROM schedules_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
sql ("INSERT INTO schedules"
" (uuid, owner, name, comment, first_time, period, period_months,"
" byday, duration, timezone, creation_time,"
" modification_time, icalendar)"
" SELECT uuid, owner, name, comment, first_time, period,"
" period_months, byday, duration, timezone,"
" creation_time, modification_time, icalendar"
" FROM schedules_trash WHERE id = %llu;",
resource);
/* Update the schedule in any trashcan tasks. */
sql ("UPDATE tasks"
" SET schedule = %llu,"
" schedule_location = " G_STRINGIFY (LOCATION_TABLE)
" WHERE schedule = %llu"
" AND schedule_location = " G_STRINGIFY (LOCATION_TRASH),
sql_last_insert_id (),
resource);
permissions_set_locations ("schedule", resource,
sql_last_insert_id (),
LOCATION_TABLE);
tags_set_locations ("schedule", resource,
sql_last_insert_id (),
LOCATION_TABLE);
sql ("DELETE FROM schedules_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Tag */
if (find_trash ("tag", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
tag_t restored_tag;
sql ("INSERT INTO tags"
" (uuid, owner, name, comment, creation_time,"
" modification_time, resource_type, active, value)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time, resource_type, active, value"
" FROM tags_trash WHERE id = %llu;",
resource);
restored_tag = sql_last_insert_id ();
sql ("INSERT INTO tag_resources"
" (tag, resource_type, resource, resource_uuid, resource_location)"
" SELECT"
" %llu, resource_type, resource, resource_uuid, resource_location"
" FROM tag_resources_trash WHERE tag = %llu;",
restored_tag, resource);
sql ("DELETE FROM tag_resources_trash WHERE tag = %llu",
resource);
permissions_set_locations ("tag", resource, restored_tag,
LOCATION_TABLE);
sql ("DELETE FROM tags_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Target. */
if (find_trash ("target", id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
target_t restored_target;
if (sql_int ("SELECT count(*) FROM targets"
" WHERE name ="
" (SELECT name FROM targets_trash WHERE id = %llu)"
" AND " ACL_USER_OWNS () ";",
resource,
current_credentials.uuid))
{
sql_rollback ();
return 3;
}
/* Check if it uses a credential or port list in the trashcan. */
if (sql_int ("SELECT port_list_location = " G_STRINGIFY (LOCATION_TRASH)
" OR EXISTS ("
" SELECT *"
" FROM targets_trash_login_data WHERE target = %llu"
" AND credential_location = " G_STRINGIFY (LOCATION_TRASH)
" )"
" FROM targets_trash WHERE id = %llu;",
resource, resource))
{
sql_rollback ();
return 1;
}
sql ("INSERT INTO targets"
" (uuid, owner, name, hosts, exclude_hosts, comment,"
" port_list, reverse_lookup_only, reverse_lookup_unify,"
" alive_test, allow_simultaneous_ips,"
" creation_time, modification_time)"
" SELECT uuid, owner, name, hosts, exclude_hosts, comment,"
" port_list, reverse_lookup_only, reverse_lookup_unify,"
" alive_test, allow_simultaneous_ips,"
" creation_time, modification_time"
" FROM targets_trash WHERE id = %llu;",
resource);
restored_target = sql_last_insert_id ();
/* Copy login data */
sql ("INSERT INTO targets_login_data"
" (target, type, credential, port)"
" SELECT %llu, type, credential, port"
" FROM targets_trash_login_data WHERE target = %llu;",
restored_target, resource);
sql ("DELETE FROM targets_trash_login_data"
" WHERE target = %llu;",
resource);
/* Update the target in any trashcan tasks. */
sql ("UPDATE tasks"
" SET target = %llu,"
" target_location = " G_STRINGIFY (LOCATION_TABLE)
" WHERE target = %llu"
" AND target_location = " G_STRINGIFY (LOCATION_TRASH),
restored_target,
resource);
permissions_set_locations ("target", resource,
sql_last_insert_id (),
LOCATION_TABLE);
tags_set_locations ("target", resource,
sql_last_insert_id (),
LOCATION_TABLE);
sql ("DELETE FROM targets_trash WHERE id = %llu;", resource);
sql_commit ();
return 0;
}
/* Task. */
if (find_trash_task (id, &resource))
{
sql_rollback ();
return -1;
}
if (resource)
{
/* Check if it's in use by a resource in the trashcan. */
if (sql_int ("SELECT (target_location = " G_STRINGIFY (LOCATION_TRASH) ")"
" OR (config_location = " G_STRINGIFY (LOCATION_TRASH) ")"
" OR (schedule_location = " G_STRINGIFY (LOCATION_TRASH) ")"
" OR (scanner_location = " G_STRINGIFY (LOCATION_TRASH) ")"
" OR (SELECT count(*) > 0 FROM task_alerts"
" WHERE task = tasks.id"
" AND alert_location = " G_STRINGIFY (LOCATION_TRASH) ")"
" FROM tasks WHERE id = %llu;",
resource))
{
sql_rollback ();
return 1;
}
permissions_set_locations ("task", resource, resource, LOCATION_TABLE);
tags_set_locations ("task", resource, resource, LOCATION_TABLE);
sql ("UPDATE tag_resources SET resource_location = "
G_STRINGIFY (LOCATION_TABLE)
" WHERE resource_type = 'report'"
" AND resource IN (SELECT id FROM reports"
" WHERE reports.task = %llu);",
resource);
sql ("UPDATE tag_resources SET resource_location = "
G_STRINGIFY (LOCATION_TABLE)
" WHERE resource_type = 'result'"
" AND resource IN (SELECT results.id FROM results"
" WHERE results.task = %llu);",
resource);
sql ("UPDATE tag_resources_trash SET resource_location = "
G_STRINGIFY (LOCATION_TABLE)
" WHERE resource_type = 'report'"
" AND resource IN (SELECT id FROM reports"
" WHERE reports.task = %llu);",
resource);
sql ("UPDATE tag_resources_trash SET resource_location = "
G_STRINGIFY (LOCATION_TABLE)
" WHERE resource_type = 'result'"
" AND resource IN (SELECT results.id FROM results"
" WHERE results.task = %llu);",
resource);
sql ("INSERT INTO results"
" (uuid, task, host, port, nvt, result_nvt, type, description,"
" report, nvt_version, severity, qod, qod_type, owner, date,"
" hostname, path)"
" SELECT uuid, task, host, port, nvt, result_nvt, type,"
" description, report, nvt_version, severity, qod,"
" qod_type, owner, date, hostname, path"
" FROM results_trash"
" WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
resource);
tickets_restore_task (resource);
sql ("DELETE FROM results_trash"
" WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
resource);
/* For safety, in case anything recounted this report while it was in
* the trash (which would have resulted in the wrong counts, because
* the counting does not know about results_trash). */
sql ("DELETE FROM report_counts"
" WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
resource);
sql ("UPDATE tasks SET hidden = 0 WHERE id = %llu;", resource);
cache_permissions_for_resource ("task", resource, NULL);
sql_commit ();
return 0;
}
sql_rollback ();
return 2;
}
/**
* @brief Owner SQL for manage_empty_trash.
*/
#define WHERE_OWNER \
" WHERE owner = (SELECT id FROM users WHERE uuid = '%s')", \
current_credentials.uuid
/**
* @brief Empty the trashcan.
*
* @return 0 success, 99 permission denied, -1 error.
*/
int
manage_empty_trashcan ()
{
sql_begin_immediate ();
if (acl_user_may ("empty_trashcan") == 0)
{
sql_rollback ();
return 99;
}
sql ("DELETE FROM nvt_selectors"
" WHERE name != '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
" AND name IN (SELECT nvt_selector FROM configs_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM config_preferences_trash"
" WHERE config IN (SELECT id FROM configs_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM configs_trash" WHERE_OWNER);
empty_trashcan_tickets ();
sql ("DELETE FROM permissions"
" WHERE subject_type = 'group'"
" AND subject IN (SELECT id from groups_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'))"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
current_credentials.uuid);
sql ("DELETE FROM group_users_trash"
" WHERE \"group\" IN (SELECT id from groups_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM groups_trash" WHERE_OWNER);
sql ("DELETE FROM alert_condition_data_trash"
" WHERE alert IN (SELECT id from alerts_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM alert_event_data_trash"
" WHERE alert IN (SELECT id from alerts_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM alert_method_data_trash"
" WHERE alert IN (SELECT id from alerts_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM alerts_trash" WHERE_OWNER);
sql ("DELETE FROM credentials_trash_data"
" WHERE credential IN (SELECT id from credentials_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM credentials_trash" WHERE_OWNER);
sql ("DELETE FROM filters_trash" WHERE_OWNER);
sql ("DELETE FROM notes_trash" WHERE_OWNER);
sql ("DELETE FROM overrides_trash" WHERE_OWNER);
sql ("DELETE FROM permissions_trash" WHERE_OWNER);
empty_trashcan_port_lists ();
sql ("DELETE FROM permissions"
" WHERE subject_type = 'role'"
" AND subject IN (SELECT id from roles_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'))"
" AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
current_credentials.uuid);
sql ("DELETE FROM role_users_trash"
" WHERE role IN (SELECT id from roles_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM roles_trash" WHERE_OWNER);
sql ("DELETE FROM scanners_trash" WHERE_OWNER);
sql ("DELETE FROM schedules_trash" WHERE_OWNER);
// Remove resource data of all trash tags of the current user.
sql ("DELETE FROM tag_resources_trash"
" WHERE tag IN (SELECT id FROM tags_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM tags_trash" WHERE_OWNER);
sql ("DELETE FROM targets_trash_login_data"
" WHERE target IN (SELECT id from targets_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'));",
current_credentials.uuid);
sql ("DELETE FROM targets_trash" WHERE_OWNER);
if (delete_trash_tasks ())
{
sql_rollback ();
return -1;
}
sql ("UPDATE permissions"
" SET resource = -1"
" WHERE resource > 0"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH)
" AND owner = (SELECT id FROM users WHERE uuid = '%s');",
current_credentials.uuid);
// Remove all trash resources from all tags of the current user.
sql ("DELETE FROM tag_resources"
" WHERE tag IN (SELECT id FROM tags_trash"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'))"
" AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
current_credentials.uuid);
/* Remove report formats last, because dir deletion can't be rolled back. */
if (empty_trashcan_report_formats ())
{
sql_rollback ();
return -1;
}
sql_commit ();
return 0;
}
/* Assets. */
/**
* \page asset_rules Ruleset for updating assets from scan detections
*
* During a scan various assets are identfied. The findings are by default
* used to update the asset database. Since assets may already be present in
* the database or even be present with contradictive properties, a ruleset
* defines how the asset database is updated upon findings.
*
* Hosts
* -----
*
* When a host is detected, and there is at least one asset host that has the
* same name and owner as the detected host, and whose identifiers all have
* the same values as the identifiers of the detected host, then the most
* recent such asset host is used. Otherwise a new asset host is created.
* Either way the identifiers are added to the asset host. It does not matter
* if the asset host has fewer identifiers than detected, as long as the
* existing identifiers match.
*
* At the beginning of a scan, when a host is first detected, the decision
* about which asset host to use is made by \ref host_notice. At the end
* of the scan, if the host has identifiers, then this decision is revised
* by \ref hosts_set_identifiers to take the identifiers into account.
*
* Host identifiers can be ip, hostname, MAC, OS or ssh-key.
*
* This documentation includes some pseudo-code and tabular definition.
* Eventually one of them will repalce the other.
*
* Name : The assigned name (usually the IP)
* IP : The detected IP
* Hostname: The detected Hostname
* OS: : The detected OS
*
* If IP And Not Hostname:
* If Not Assets.Host(id=Name) And Not Assets.Host(attrib=IP, IP):
* Assets.Host.New(id=Name, ip=IP)
* If Assets.Host(id=Name) == 1:
* Assets.Host.Add(id=Name, ip=IP)
*
* This pseudo-code is equivalent to the first two rows of:
*
* Detection | Asset State | Asset Update
* ----------------------------- | --------------------------------------------------------------------------- | -----------------------------
* IP address X. | No host with Name=X or any ip=X. | Create host with Name=X and ip=X.
* IP address X. | Host A with Name=X. | Add ip=X to host A.
* IP address X. | (Host A with Name=X and ip=X) and (Host B with Name=X and ip=X). | Add ip=X to host (Newest(A,B)).
* IP address X with Hostname Y. | Host A with Name=X and ip=X. | Add ip=X and hostname=Y to host A.
* IP address X with Hostname Y. | Host A with Name=X and ip=X and hostname=Y. | Add ip=X and hostname=Y to host A.
* IP address X with Hostname Y. | Host A with Name=X and ip=X and hostname<>Y. | Create host with Name=X, ip=X and hostname=Y.
* IP address X with Hostname Y. | Host A with Name=X and ip=X and hostname=Y and host B with Name=X and ip=X. | Add ip=X and hostname=Y to host (Newst(A,B)).
*
* Follow up action: If a MAC, OS or ssh-key was detected, then the respective
* identifiers are added to the asset host selected during asset update.
*
* Operating Systems
* -----------------
*
* If OS:
* If Not Assets.OS(id=OS):
* Assets.OS.New(id=OS)
*
* This pseudo-code is equivalent to:
*
* Detection | Asset State | Asset Update
* --------- | ------------------ | ------------------------
* OS X. | No OS with Name=X. | Create OS with Name=X.
* OS X. | OS with Name=X. | No action.
*/
/**
* @brief Return the UUID of the asset associated with a result host.
*
* @param[in] host Host value from result.
* @param[in] result Result.
*
* @return Asset UUID.
*/
char *
result_host_asset_id (const char *host, result_t result)
{
gchar *quoted_host;
char *asset_id;
quoted_host = sql_quote (host);
asset_id = sql_string ("SELECT uuid FROM hosts"
" WHERE id = (SELECT host FROM host_identifiers"
" WHERE source_type = 'Report Host'"
" AND name = 'ip'"
" AND source_id"
" = (SELECT uuid"
" FROM reports"
" WHERE id = (SELECT report"
" FROM results"
" WHERE id = %llu))"
" AND value = '%s'"
" LIMIT 1);",
result,
quoted_host);
g_free (quoted_host);
return asset_id;
}
/**
* @brief Return the UUID of a host.
*
* @param[in] host Host.
*
* @return Host UUID.
*/
char*
host_uuid (resource_t host)
{
return sql_string ("SELECT uuid FROM hosts WHERE id = %llu;",
host);
}
/**
* @brief Add a report host.
*
* @param[in] report UUID of resource.
* @param[in] host Host.
* @param[in] start Start time.
* @param[in] end End time.
*
* @return Report host.
*/
report_host_t
manage_report_host_add (report_t report, const char *host, time_t start,
time_t end)
{
char *quoted_host = sql_quote (host);
sql ("INSERT INTO report_hosts"
" (report, host, start_time, end_time, current_port, max_port)"
" SELECT %llu, '%s', %lld, %lld, 0, 0"
" WHERE NOT EXISTS (SELECT 1 FROM report_hosts WHERE report = %llu"
" AND host = '%s');",
report, quoted_host, (long long) start, (long long) end, report,
quoted_host);
g_free (quoted_host);
return sql_last_insert_id ();
}
/**
* @brief Tests if a report host is marked as dead.
*
* @param[in] report_host Report host.
*
* @return 1 if the host is marked as dead, 0 otherwise.
*/
static int
report_host_dead (report_host_t report_host)
{
return sql_int ("SELECT count(*) != 0 FROM report_host_details"
" WHERE report_host = %llu"
" AND name = 'Host dead'"
" AND value != '0';",
report_host);
}
/**
* @brief Counts.
*
* @param[in] report_host Report host.
*
* @return 1 if the host is marked as dead, 0 otherwise.
*/
static int
report_host_result_count (report_host_t report_host)
{
return sql_int ("SELECT count(*) FROM report_hosts, results"
" WHERE report_hosts.id = %llu"
" AND results.report = report_hosts.report"
" AND report_hosts.host = results.host;",
report_host);
}
/**
* @brief Set end time of a report host.
*
* @param[in] report_host Report host.
* @param[in] end_time End time.
*/
void
report_host_set_end_time (report_host_t report_host, time_t end_time)
{
sql ("UPDATE report_hosts SET end_time = %lld WHERE id = %llu;",
end_time, report_host);
}
/**
* @brief Host identifiers for the current scan.
*/
array_t *identifiers = NULL;
/**
* @brief Unique hosts listed in host_identifiers.
*/
static array_t *identifier_hosts = NULL;
/**
* @brief Host identifier type.
*/
typedef struct
{
gchar *ip; ///< IP of host.
gchar *name; ///< Name of identifier, like "hostname".
gchar *value; ///< Value of identifier.
gchar *source_type; ///< Type of identifier source, like "Report Host".
gchar *source_id; ///< ID of source.
gchar *source_data; ///< Extra data for source.
} identifier_t;
/**
* @brief Free an identifier.
*
* @param[in] identifier Identifier.
*/
static void
identifier_free (identifier_t *identifier)
{
if (identifier)
{
g_free (identifier->ip);
g_free (identifier->name);
g_free (identifier->value);
g_free (identifier->source_type);
g_free (identifier->source_id);
g_free (identifier->source_data);
}
}
/**
* @brief Setup hosts and their identifiers after a scan, from host details.
*
* At the end of a scan this revises the decision about which asset host to use
* for each host that has identifiers. The rules for this decision are described
* in \ref asset_rules. (The initial decision is made by \ref host_notice.)
*
* @param[in] report Report that the identifiers come from.
*/
void
hosts_set_identifiers (report_t report)
{
if (identifier_hosts)
{
int host_index, index;
gchar *ip;
array_terminate (identifiers);
array_terminate (identifier_hosts);
host_index = 0;
while ((ip = (gchar*) g_ptr_array_index (identifier_hosts, host_index)))
{
host_t host, host_new;
gchar *quoted_host_name;
identifier_t *identifier;
GString *select;
if (report_host_noticeable (report, ip) == 0)
continue;
quoted_host_name = sql_quote (ip);
select = g_string_new ("");
/* Select the most recent host whose identifiers all match the given
* identifiers, even if the host has fewer identifiers than given. */
g_string_append_printf (select,
"SELECT id FROM hosts"
" WHERE name = '%s'"
" AND owner = (SELECT id FROM users"
" WHERE uuid = '%s')",
quoted_host_name,
current_credentials.uuid);
index = 0;
while ((identifier = (identifier_t*) g_ptr_array_index (identifiers, index)))
{
gchar *quoted_identifier_name, *quoted_identifier_value;
if (strcmp (identifier->ip, ip))
{
index++;
continue;
}
quoted_identifier_name = sql_quote (identifier->name);
quoted_identifier_value = sql_quote (identifier->value);
g_string_append_printf (select,
" AND (EXISTS (SELECT * FROM host_identifiers"
" WHERE host = hosts.id"
" AND owner = (SELECT id FROM users"
" WHERE uuid = '%s')"
" AND name = '%s'"
" AND value = '%s')"
" OR NOT EXISTS (SELECT * FROM host_identifiers"
" WHERE host = hosts.id"
" AND owner = (SELECT id FROM users"
" WHERE uuid = '%s')"
" AND name = '%s'))",
current_credentials.uuid,
quoted_identifier_name,
quoted_identifier_value,
current_credentials.uuid,
quoted_identifier_name);
g_free (quoted_identifier_name);
g_free (quoted_identifier_value);
index++;
}
g_string_append_printf (select,
" ORDER BY creation_time DESC LIMIT 1;");
switch (sql_int64 (&host, select->str))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
host = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
host = 0;
break;
}
g_string_free (select, TRUE);
if (host == 0)
{
/* Add the host. */
sql ("INSERT into hosts"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" (make_uuid (), (SELECT id FROM users WHERE uuid = '%s'), '%s', '',"
" m_now (), m_now ());",
current_credentials.uuid,
quoted_host_name);
host_new = host = sql_last_insert_id ();
/* Make sure the Report Host identifiers added when the host was
* first noticed now refer to the new host. */
sql ("UPDATE host_identifiers SET host = %llu"
" WHERE source_id = (SELECT uuid FROM reports"
" WHERE id = %llu)"
" AND name = 'ip'"
" AND value = '%s';",
host_new,
report,
quoted_host_name);
}
else
{
/* Use the existing host. */
host_new = 0;
}
/* Add the host identifiers. */
index = 0;
while ((identifier = (identifier_t*) g_ptr_array_index (identifiers,
index)))
{
gchar *quoted_identifier_name, *quoted_identifier_value;
gchar *quoted_source_id, *quoted_source_type, *quoted_source_data;
if (strcmp (identifier->ip, ip))
{
index++;
continue;
}
quoted_identifier_name = sql_quote (identifier->name);
quoted_identifier_value = sql_quote (identifier->value);
quoted_source_id = sql_quote (identifier->source_id);
quoted_source_data = sql_quote (identifier->source_data);
quoted_source_type = sql_quote (identifier->source_type);
if (strcmp (identifier->name, "OS") == 0)
{
resource_t os;
switch (sql_int64 (&os,
"SELECT id FROM oss"
" WHERE name = '%s'"
" AND owner = (SELECT id FROM users"
" WHERE uuid = '%s');",
quoted_identifier_value,
current_credentials.uuid))
{
case 0:
break;
default: /* Programming error. */
assert (0);
case -1:
case 1: /* Too few rows in result of query. */
sql ("INSERT into oss"
" (uuid, owner, name, comment, creation_time,"
" modification_time)"
" VALUES"
" (make_uuid (),"
" (SELECT id FROM users WHERE uuid = '%s'),"
" '%s', '', m_now (), m_now ());",
current_credentials.uuid,
quoted_identifier_value);
os = sql_last_insert_id ();
break;
}
sql ("INSERT into host_oss"
" (uuid, host, owner, name, comment, os, source_type,"
" source_id, source_data, creation_time, modification_time)"
" VALUES"
" (make_uuid (), %llu,"
" (SELECT id FROM users WHERE uuid = '%s'),"
" '%s', '', %llu, '%s', '%s', '%s', m_now (), m_now ());",
host,
current_credentials.uuid,
quoted_identifier_name,
os,
quoted_source_type,
quoted_source_id,
quoted_source_data);
if (host_new == 0)
{
sql ("UPDATE hosts"
" SET modification_time = (SELECT modification_time"
" FROM host_oss"
" WHERE id = %llu)"
" WHERE id = %llu;",
sql_last_insert_id (),
host);
sql ("UPDATE oss"
" SET modification_time = (SELECT modification_time"
" FROM host_oss"
" WHERE id = %llu)"
" WHERE id = %llu;",
sql_last_insert_id (),
os);
}
}
else
{
sql ("INSERT into host_identifiers"
" (uuid, host, owner, name, comment, value, source_type,"
" source_id, source_data, creation_time, modification_time)"
" VALUES"
" (make_uuid (), %llu,"
" (SELECT id FROM users WHERE uuid = '%s'),"
" '%s', '', '%s', '%s', '%s', '%s', m_now (), m_now ());",
host,
current_credentials.uuid,
quoted_identifier_name,
quoted_identifier_value,
quoted_source_type,
quoted_source_id,
quoted_source_data);
if (host_new == 0)
sql ("UPDATE hosts"
" SET modification_time = (SELECT modification_time"
" FROM host_identifiers"
" WHERE id = %llu)"
" WHERE id = %llu;",
sql_last_insert_id (),
host);
}
g_free (quoted_source_type);
g_free (quoted_source_id);
g_free (quoted_source_data);
g_free (quoted_identifier_name);
g_free (quoted_identifier_value);
index++;
}
g_free (quoted_host_name);
host_index++;
}
index = 0;
while (identifiers && (index < identifiers->len))
identifier_free (g_ptr_array_index (identifiers, index++));
array_free (identifiers);
identifiers = NULL;
array_free (identifier_hosts);
identifier_hosts = NULL;
}
}
/**
* @brief Set the maximum severity of each host in a scan.
*
* @param[in] report The report associated with the scan.
* @param[in] overrides_arg Whether override should be applied.
* @param[in] min_qod_arg Min QOD to use.
*/
void
hosts_set_max_severity (report_t report, int *overrides_arg, int *min_qod_arg)
{
gchar *new_severity_sql;
int dynamic_severity, overrides, min_qod;
if (overrides_arg)
overrides = *overrides_arg;
else
{
task_t task;
/* Get "Assets Apply Overrides" task preference. */
overrides = 1;
if (report_task (report, &task) == 0)
{
char *value;
value = task_preference_value (task, "assets_apply_overrides");
if (value && (strcmp (value, "yes") == 0))
overrides = 1;
else
overrides = 0;
free (value);
}
}
if (min_qod_arg)
min_qod = *min_qod_arg;
else
{
task_t task;
/* Get "Assets Min QOD". */
min_qod = MIN_QOD_DEFAULT;
if (report_task (report, &task) == 0)
{
char *value;
value = task_preference_value (task, "assets_min_qod");
if (value)
min_qod = atoi (value);
free (value);
}
}
dynamic_severity = setting_dynamic_severity_int ();
new_severity_sql = new_severity_clause (overrides, dynamic_severity);
sql ("INSERT INTO host_max_severities"
" (host, severity, source_type, source_id, creation_time)"
" SELECT asset_host,"
" coalesce ((SELECT max (%s) FROM results"
" WHERE report = %llu"
" AND qod >= %i"
" AND host = (SELECT name FROM hosts"
" WHERE id = asset_host)),"
" 0.0),"
" 'Report',"
" (SELECT uuid FROM reports WHERE id = %llu),"
" m_now ()"
" FROM (SELECT host AS asset_host"
" FROM host_identifiers"
" WHERE source_id = (SELECT uuid FROM reports WHERE id = %llu))"
" AS subquery;",
new_severity_sql,
report,
min_qod,
report,
report);
g_free (new_severity_sql);
}
/**
* @brief Store certain host details in the assets after a scan.
*
* @param[in] report The report associated with the scan.
*/
void
hosts_set_details (report_t report)
{
sql ("INSERT INTO host_details"
" (detail_source_type, detail_source_name, detail_source_description,"
" name, value, source_type, source_id, host)"
" SELECT source_type,"
" source_name,"
" source_description,"
" name,"
" value,"
" 'Report',"
" (SELECT uuid FROM reports WHERE id = %llu),"
" (SELECT host"
" FROM host_identifiers"
" WHERE source_id = (SELECT uuid FROM reports"
" WHERE id = %llu)"
" AND (SELECT name FROM hosts WHERE id = host)"
" = (SELECT host FROM report_hosts"
" WHERE id = report_host_details.report_host)"
" LIMIT 1)"
" FROM report_host_details"
" WHERE (SELECT report FROM report_hosts"
" WHERE id = report_host)"
" = %llu"
/* Only if the task is included in the assets. */
" AND (SELECT value = 'yes' FROM task_preferences"
" WHERE task = (SELECT task FROM reports WHERE id = %llu)"
" AND name = 'in_assets')"
/* Ensure that every report host detail has a corresponding host
* in the assets. */
" AND EXISTS (SELECT *"
" FROM host_identifiers"
" WHERE source_id = (SELECT uuid FROM reports"
" WHERE id = %llu)"
" AND (SELECT name FROM hosts WHERE id = host)"
" = (SELECT host FROM report_hosts"
" WHERE id = report_host_details.report_host))"
" AND (name IN ('best_os_cpe', 'best_os_txt', 'traceroute'));",
report,
report,
report,
report,
report);
}
/**
* @brief Get XML of a detailed host route.
*
* @param[in] host The host.
*
* @return XML.
*/
gchar*
host_routes_xml (host_t host)
{
iterator_t routes;
GString* buffer;
gchar *owned_clause, *with_clause;
owned_clause = acl_where_owned_for_get ("host", NULL, NULL, &with_clause);
buffer = g_string_new ("");
init_iterator (&routes,
"SELECT outer_details.value,"
" outer_details.source_type,"
" outer_details.source_id,"
" outer_identifiers.modification_time"
" FROM host_details AS outer_details"
" JOIN host_identifiers AS outer_identifiers"
" ON outer_identifiers.host = outer_details.host"
" WHERE outer_details.host = %llu"
" AND outer_details.name = 'traceroute'"
" AND outer_details.source_id = outer_identifiers.source_id"
" AND outer_identifiers.name='ip'"
" AND outer_identifiers.modification_time"
" = (SELECT max (modification_time)"
" FROM host_identifiers"
" WHERE host_identifiers.host = %llu"
" AND host_identifiers.source_id IN"
" (SELECT source_id FROM host_details"
" WHERE host = %llu"
" AND value = outer_details.value)"
" AND host_identifiers.name='ip')"
" ORDER BY outer_identifiers.modification_time DESC;",
host, host, host);
while (next (&routes))
{
const char *traceroute;
const char *source_id;
time_t modified;
gchar **hop_ips, **hop_ip;
int distance;
g_string_append (buffer, "");
traceroute = iterator_string (&routes, 0);
source_id = iterator_string (&routes, 2);
modified = iterator_int64 (&routes, 3);
hop_ips = g_strsplit (traceroute, ",", 0);
hop_ip = hop_ips;
distance = 0;
while (*hop_ip != NULL) {
iterator_t best_host_iterator;
const char *best_host_id;
int same_source;
init_iterator (&best_host_iterator,
"%s"
" SELECT hosts.uuid,"
" (source_id='%s')"
" AS same_source"
" FROM hosts, host_identifiers"
" WHERE hosts.id = host_identifiers.host"
" AND host_identifiers.name = 'ip'"
" AND host_identifiers.value='%s'"
" AND %s"
" ORDER BY same_source DESC,"
" abs(host_identifiers.modification_time"
" - %llu) ASC"
" LIMIT 1;",
with_clause ? with_clause : "",
source_id,
*hop_ip,
owned_clause,
modified);
if (next (&best_host_iterator))
{
best_host_id = iterator_string (&best_host_iterator, 0);
same_source = iterator_int (&best_host_iterator, 1);
}
else
{
best_host_id = NULL;
same_source = 0;
}
g_string_append_printf (buffer,
""
"%s"
"",
best_host_id ? best_host_id : "",
distance,
same_source,
*hop_ip);
cleanup_iterator (&best_host_iterator);
distance++;
hop_ip++;
}
g_string_append (buffer, "");
g_strfreev(hop_ips);
}
g_free (with_clause);
g_free (owned_clause);
cleanup_iterator (&routes);
g_string_append (buffer, "");
return g_string_free (buffer, FALSE);
}
/**
* @brief Add host details to a report host.
*
* @param[in] report UUID of resource.
* @param[in] ip Host.
* @param[in] entity XML entity containing details.
*
* @return 0 success, -1 failed to parse XML.
*/
int
manage_report_host_details (report_t report, const char *ip, entity_t entity)
{
int in_assets;
entities_t details;
entity_t detail;
char *uuid;
in_assets = sql_int ("SELECT not(value = 'no') FROM task_preferences"
" WHERE task = (SELECT task FROM reports"
" WHERE id = %llu)"
" AND name = 'in_assets';",
report);
details = entity->entities;
if (identifiers == NULL)
identifiers = make_array ();
if (identifier_hosts == NULL)
identifier_hosts = make_array ();
uuid = report_uuid (report);
while ((detail = first_entity (details)))
{
if (strcmp (entity_name (detail), "detail") == 0)
{
entity_t source, source_type, source_name, source_desc, name, value;
/* Parse host detail and add to report */
source = entity_child (detail, "source");
if (source == NULL)
goto error;
source_type = entity_child (source, "type");
if (source_type == NULL)
goto error;
source_name = entity_child (source, "name");
if (source_name == NULL)
goto error;
source_desc = entity_child (source, "description");
if (source_desc == NULL)
goto error;
name = entity_child (detail, "name");
if (name == NULL)
goto error;
value = entity_child (detail, "value");
if (value == NULL)
goto error;
insert_report_host_detail
(report, ip, entity_text (source_type), entity_text (source_name),
entity_text (source_desc), entity_text (name), entity_text (value));
/* Only add to assets if "Add to Assets" is set on the task. */
if (in_assets)
{
if (strcmp (entity_text (name), "hostname") == 0)
{
identifier_t *identifier;
identifier = g_malloc (sizeof (identifier_t));
identifier->ip = g_strdup (ip);
identifier->name = g_strdup ("hostname");
identifier->value = g_strdup (entity_text (value));
identifier->source_id = g_strdup (uuid);
identifier->source_type = g_strdup ("Report Host Detail");
identifier->source_data
= g_strdup (entity_text (source_name));
array_add (identifiers, identifier);
array_add_new_string (identifier_hosts, g_strdup (ip));
}
if (strcmp (entity_text (name), "MAC") == 0)
{
identifier_t *identifier;
identifier = g_malloc (sizeof (identifier_t));
identifier->ip = g_strdup (ip);
identifier->name = g_strdup ("MAC");
identifier->value = g_strdup (entity_text (value));
identifier->source_id = g_strdup (uuid);
identifier->source_type = g_strdup ("Report Host Detail");
identifier->source_data
= g_strdup (entity_text (source_name));
array_add (identifiers, identifier);
array_add_new_string (identifier_hosts, g_strdup (ip));
}
if (strcmp (entity_text (name), "OS") == 0
&& g_str_has_prefix (entity_text (value), "cpe:"))
{
identifier_t *identifier;
identifier = g_malloc (sizeof (identifier_t));
identifier->ip = g_strdup (ip);
identifier->name = g_strdup ("OS");
identifier->value = g_strdup (entity_text (value));
identifier->source_id = g_strdup (uuid);
identifier->source_type = g_strdup ("Report Host Detail");
identifier->source_data
= g_strdup (entity_text (source_name));
array_add (identifiers, identifier);
array_add_new_string (identifier_hosts, g_strdup (ip));
}
if (strcmp (entity_text (name), "ssh-key") == 0)
{
identifier_t *identifier;
identifier = g_malloc (sizeof (identifier_t));
identifier->ip = g_strdup (ip);
identifier->name = g_strdup ("ssh-key");
identifier->value = g_strdup (entity_text (value));
identifier->source_id = g_strdup (uuid);
identifier->source_type = g_strdup ("Report Host Detail");
identifier->source_data
= g_strdup (entity_text (source_name));
array_add (identifiers, identifier);
array_add_new_string (identifier_hosts, g_strdup (ip));
}
}
}
details = next_entities (details);
}
free (uuid);
return 0;
error:
free (uuid);
return -1;
}
/**
* @brief Add a host detail to a report host.
*
* @param[in] report UUID of resource.
* @param[in] host Host.
* @param[in] xml Report host detail XML.
*
* @return 0 success, -1 failed to parse XML, -2 host was NULL.
*/
int
manage_report_host_detail (report_t report, const char *host, const char *xml)
{
int ret;
entity_t entity;
if (host == NULL)
return -2;
entity = NULL;
if (parse_entity (xml, &entity))
return -1;
ret = manage_report_host_details (report, host, entity);
free_entity (entity);
return ret;
}
/**
* @brief Initialise a host identifier iterator.
*
* @param[in] iterator Iterator.
* @param[in] host Host.
* @param[in] ascending Whether to sort ascending or descending.
* @param[in] sort_field Field to sort on, or NULL for type then start.
*/
void
init_host_identifier_iterator (iterator_t* iterator, host_t host,
int ascending, const char* sort_field)
{
assert (current_credentials.uuid);
if (host)
init_iterator (iterator,
"SELECT id, uuid, name, comment, iso_time (creation_time),"
" iso_time (modification_time), creation_time,"
" modification_time, owner, owner, value,"
" source_type, source_id, source_data,"
" (CASE WHEN source_type LIKE 'Report%%'"
" THEN NOT EXISTS (SELECT * FROM reports"
" WHERE uuid = source_id)"
" ELSE CAST (0 AS boolean)"
" END),"
" '', ''"
" FROM host_identifiers"
" WHERE host = %llu"
" UNION"
" SELECT id, uuid, name, comment, iso_time (creation_time),"
" iso_time (modification_time), creation_time,"
" modification_time, owner, owner,"
" (SELECT name FROM oss WHERE id = os),"
" source_type, source_id, source_data,"
" (CASE WHEN source_type LIKE 'Report%%'"
" THEN NOT EXISTS (SELECT * FROM reports"
" WHERE uuid = source_id)"
" ELSE CAST (0 AS boolean)"
" END),"
" (SELECT uuid FROM oss WHERE id = os),"
" cpe_title ((SELECT name FROM oss WHERE id = os))"
" FROM host_oss"
" WHERE host = %llu"
" ORDER BY %s %s;",
host,
host,
sort_field ? sort_field : "creation_time",
ascending ? "ASC" : "DESC");
else
init_iterator (iterator,
"SELECT id, uuid, name, comment, iso_time (creation_time),"
" iso_time (modification_time), creation_time,"
" modification_time, owner, owner, value,"
" source_type, source_id, source_data, 0, '', ''"
" FROM host_identifiers"
" ORDER BY %s %s;",
sort_field ? sort_field : "creation_time",
ascending ? "ASC" : "DESC");
}
/**
* @brief Get the value from a host identifier iterator.
*
* @param[in] iterator Iterator.
*
* @return The value of the host identifier, or NULL if iteration is complete.
* Freed by cleanup_iterator.
*/
DEF_ACCESS (host_identifier_iterator_value, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the source type from a host identifier iterator.
*
* @param[in] iterator Iterator.
*
* @return The source type of the host identifier, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_identifier_iterator_source_type,
GET_ITERATOR_COLUMN_COUNT + 1);
/**
* @brief Get the source from a host identifier iterator.
*
* @param[in] iterator Iterator.
*
* @return The source of the host identifier, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_identifier_iterator_source_id, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get the source data from a host identifier iterator.
*
* @param[in] iterator Iterator.
*
* @return The source data of the host identifier, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_identifier_iterator_source_data,
GET_ITERATOR_COLUMN_COUNT + 3);
/**
* @brief Get the source orphan state from a host identifier iterator.
*
* @param[in] iterator Iterator.
*
* @return The source orphan state of the host identifier, or 0 if iteration is
* complete. Freed by cleanup_iterator.
*/
int
host_identifier_iterator_source_orphan (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
}
/**
* @brief Get the OS UUID from a host identifier iterator.
*
* @param[in] iterator Iterator.
*
* @return The OS UUID of the host identifier, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_identifier_iterator_os_id,
GET_ITERATOR_COLUMN_COUNT + 5);
/**
* @brief Get the OS title from a host identifier iterator.
*
* @param[in] iterator Iterator.
*
* @return The OS title of the host identifier, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_identifier_iterator_os_title,
GET_ITERATOR_COLUMN_COUNT + 6);
/**
* @brief Filter columns for host iterator.
*/
#define HOST_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "severity", "os", "oss", "hostname", "ip", \
"severity_level", "updated", NULL }
/**
* @brief Host iterator columns.
*/
#define HOST_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (hosts), \
{ \
"1", \
"writable", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"0", \
"in_use", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT round (CAST (severity AS numeric), 1)" \
" FROM host_max_severities" \
" WHERE host = hosts.id" \
" ORDER by creation_time DESC" \
" LIMIT 1)", \
"severity", \
KEYWORD_TYPE_DOUBLE \
}, \
{ \
"(SELECT CASE" \
" WHEN best_os_text LIKE '%[possible conflict]%'" \
" THEN best_os_text" \
" WHEN best_os_cpe IS NULL" \
" THEN '[unknown]'" \
" ELSE best_os_cpe" \
" END" \
" FROM (SELECT (SELECT value" \
" FROM (SELECT max (id) AS id" \
" FROM host_details" \
" WHERE host = hosts.id" \
" AND name = 'best_os_cpe')" \
" AS sub," \
" host_details" \
" WHERE sub.id = host_details.id)" \
" AS best_os_cpe," \
" (SELECT value" \
" FROM (SELECT max (id) AS id" \
" FROM host_details" \
" WHERE host = hosts.id" \
" AND name = 'best_os_text')" \
" AS sub," \
" host_details" \
" WHERE sub.id = host_details.id)" \
" AS best_os_text)" \
" AS vars)", \
"os", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT group_concat (name, ', ') FROM oss" \
" WHERE id IN (SELECT distinct os FROM host_oss" \
" WHERE host = hosts.id))", \
"oss", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT value" \
" FROM host_identifiers" \
" WHERE host = hosts.id" \
" AND name = 'hostname'" \
" ORDER by creation_time DESC" \
" LIMIT 1)", \
"hostname", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT value" \
" FROM host_identifiers" \
" WHERE host = hosts.id" \
" AND name = 'ip'" \
" ORDER by creation_time DESC" \
" LIMIT 1)", \
"ip", \
KEYWORD_TYPE_STRING \
}, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Host iterator WHERE columns.
*/
#define HOST_ITERATOR_WHERE_COLUMNS \
{ \
{ \
"(SELECT severity_to_level (CAST (severity AS numeric), 0)" \
" FROM host_max_severities" \
" WHERE host = hosts.id" \
" ORDER by creation_time DESC" \
" LIMIT 1)", \
"severity_level", \
KEYWORD_TYPE_STRING \
}, \
{ \
"modification_time", "updated", KEYWORD_TYPE_INTEGER \
}, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Initialise a host iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find host, 2 failed to find filter,
* -1 error.
*/
int
init_asset_host_iterator (iterator_t *iterator, const get_data_t *get)
{
static const char *filter_columns[] = HOST_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = HOST_ITERATOR_COLUMNS;
static column_t where_columns[] = HOST_ITERATOR_WHERE_COLUMNS;
return init_get_iterator2 (iterator,
"host",
get,
/* Columns. */
columns,
/* Columns for trashcan. */
NULL,
/* WHERE Columns. */
where_columns,
/* WHERE Columns for trashcan. */
NULL,
filter_columns,
0,
NULL,
NULL,
NULL,
TRUE,
FALSE,
NULL);
}
/**
* @brief Get the writable status from an asset iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if writable, else 0.
*/
int
asset_iterator_writable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT);
}
/**
* @brief Get the "in use" status from an asset iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if in use, else 0.
*/
int
asset_iterator_in_use (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
}
/**
* @brief Get the max severity from an asset host iterator.
*
* @param[in] iterator Iterator.
*
* @return The maximum severity of the host, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (asset_host_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Count number of hosts.
*
* @param[in] get GET params.
*
* @return Total number of hosts in filtered set.
*/
int
asset_host_count (const get_data_t *get)
{
static const char *filter_columns[] = HOST_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = HOST_ITERATOR_COLUMNS;
static column_t where_columns[] = HOST_ITERATOR_WHERE_COLUMNS;
return count2 ("host", get, columns, NULL, where_columns, NULL,
filter_columns, 0, NULL, NULL, NULL, TRUE);
}
/**
* @brief Filter columns for os iterator.
*/
#define OS_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "title", "hosts", "latest_severity", \
"highest_severity", "average_severity", "average_severity_score", \
"severity", NULL }
/**
* @brief OS iterator columns.
*/
#define OS_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (oss), \
{ \
"0", \
"writable", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT count (*) > 0 FROM host_oss WHERE os = oss.id)", \
"in_use", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT coalesce (cpe_title (oss.name), ''))", \
"title", \
KEYWORD_TYPE_STRING \
}, \
{ \
"(SELECT count(*)" \
" FROM (SELECT inner_cpes[1] AS cpe, host" \
" FROM (SELECT array_agg (host_details.value" \
" ORDER BY host_details.id DESC)" \
" AS inner_cpes," \
" host" \
" FROM host_details, hosts" \
" WHERE host_details.name = 'best_os_cpe'" \
" AND hosts.id = host_details.host" \
" AND (" ACL_USER_MAY_OPTS ("hosts") ")" \
" GROUP BY host)" \
" AS host_details_subselect)" \
" AS array_removal_subselect" \
" WHERE cpe = oss.name)", \
"hosts", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT round (CAST (severity AS numeric), 1) FROM host_max_severities" \
" WHERE host = (SELECT host FROM host_oss" \
" WHERE os = oss.id" \
" ORDER BY creation_time DESC LIMIT 1)" \
" ORDER BY creation_time DESC LIMIT 1)", \
"latest_severity", \
KEYWORD_TYPE_DOUBLE \
}, \
{ \
"(SELECT round (max (CAST (severity AS numeric)), 1)" \
" FROM host_max_severities" \
" WHERE host IN (SELECT DISTINCT host FROM host_oss" \
" WHERE os = oss.id))", \
"highest_severity", \
KEYWORD_TYPE_DOUBLE \
}, \
{ \
"(SELECT round (CAST (avg (severity) AS numeric), 2)" \
" FROM (SELECT (SELECT severity FROM host_max_severities" \
" WHERE host = hosts.host" \
" ORDER BY creation_time DESC LIMIT 1)" \
" AS severity" \
" FROM (SELECT distinct host FROM host_oss WHERE os = oss.id)" \
" AS hosts)" \
" AS severities)", \
"average_severity", \
KEYWORD_TYPE_DOUBLE \
}, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief OS iterator optional filtering columns.
*/
#define OS_ITERATOR_WHERE_COLUMNS \
{ \
{ \
"(SELECT round (CAST (avg (severity) AS numeric)" \
" * (SELECT count (distinct host)" \
" FROM host_oss WHERE os = oss.id), 2)" \
" FROM (SELECT (SELECT severity FROM host_max_severities" \
" WHERE host = hosts.host" \
" ORDER BY creation_time DESC LIMIT 1)" \
" AS severity" \
" FROM (SELECT distinct host FROM host_oss WHERE os = oss.id)" \
" AS hosts)" \
" AS severities)", \
"average_severity_score", \
KEYWORD_TYPE_DOUBLE \
}, \
{ \
"(SELECT round (CAST (avg (severity) AS numeric), 2)" \
" FROM (SELECT (SELECT severity FROM host_max_severities" \
" WHERE host = hosts.host" \
" ORDER BY creation_time DESC LIMIT 1)" \
" AS severity" \
" FROM (SELECT distinct host FROM host_oss WHERE os = oss.id)" \
" AS hosts)" \
" AS severities)", \
"severity", \
KEYWORD_TYPE_DOUBLE \
}, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Generate the extra_tables string for an OS iterator.
*
* @return Newly allocated string.
*/
static gchar *
asset_os_iterator_opts_table ()
{
assert (current_credentials.uuid);
return g_strdup_printf (", (SELECT"
" (SELECT id FROM users"
" WHERE users.uuid = '%s')"
" AS user_id,"
" 'host' AS type)"
" AS opts",
current_credentials.uuid);
}
/**
* @brief Initialise an OS iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find os, 2 failed to find filter,
* -1 error.
*/
int
init_asset_os_iterator (iterator_t *iterator, const get_data_t *get)
{
int ret;
static const char *filter_columns[] = OS_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = OS_ITERATOR_COLUMNS;
static column_t where_columns[] = OS_ITERATOR_WHERE_COLUMNS;
gchar *extra_tables;
extra_tables = asset_os_iterator_opts_table ();
ret = init_get_iterator2_with (iterator,
"os",
get,
/* Columns. */
columns,
/* Columns for trashcan. */
NULL,
/* WHERE Columns. */
where_columns,
/* WHERE Columns for trashcan. */
NULL,
filter_columns,
0,
extra_tables,
NULL,
NULL,
TRUE,
FALSE,
NULL,
NULL,
0,
0);
g_free (extra_tables);
return ret;
}
/**
* @brief Get the title from an OS iterator.
*
* @param[in] iterator Iterator.
*
* @return The title of the OS, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (asset_os_iterator_title, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get the number of installs from an asset OS iterator.
*
* @param[in] iterator Iterator.
*
* @return Number of hosts that have the OS.
*/
int
asset_os_iterator_installs (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
}
/**
* @brief Get the latest severity from an OS iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity of the OS, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (asset_os_iterator_latest_severity, GET_ITERATOR_COLUMN_COUNT + 4);
/**
* @brief Get the highest severity from an OS iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity of the OS, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (asset_os_iterator_highest_severity, GET_ITERATOR_COLUMN_COUNT + 5);
/**
* @brief Get the average severity from an OS iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity of the OS, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (asset_os_iterator_average_severity, GET_ITERATOR_COLUMN_COUNT + 6);
/**
* @brief Count number of oss.
*
* @param[in] get GET params.
*
* @return Total number of oss in filtered set.
*/
int
asset_os_count (const get_data_t *get)
{
static const char *extra_columns[] = OS_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = OS_ITERATOR_COLUMNS;
static column_t where_columns[] = OS_ITERATOR_WHERE_COLUMNS;
int ret;
ret = count2 ("os", get, columns, NULL, where_columns, NULL,
extra_columns, 0, 0, 0, NULL, TRUE);
return ret;
}
/**
* @brief Initialise an OS host iterator.
*
* @param[in] iterator Iterator.
* @param[in] os OS.
*/
void
init_os_host_iterator (iterator_t* iterator, resource_t os)
{
assert (os);
init_iterator (iterator,
"SELECT id, uuid, name, comment, iso_time (creation_time),"
" iso_time (modification_time), creation_time,"
" modification_time, owner, owner,"
" (SELECT round (CAST (severity AS numeric), 1)"
" FROM host_max_severities"
" WHERE host = hosts.id"
" ORDER by creation_time DESC"
" LIMIT 1)"
" FROM hosts"
" WHERE id IN (SELECT DISTINCT host FROM host_oss"
" WHERE os = %llu)"
" ORDER BY modification_time DESC;",
os);
}
/**
* @brief Get the severity from an OS host detail iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity of the OS host, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (os_host_iterator_severity, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Initialise an asset host detail iterator.
*
* @param[in] iterator Iterator.
* @param[in] host Host.
*/
void
init_host_detail_iterator (iterator_t* iterator, resource_t host)
{
assert (host);
init_iterator (iterator,
"SELECT sub.id, name, value, source_type, source_id"
" FROM (SELECT max (id) AS id FROM host_details"
" WHERE host = %llu"
" GROUP BY name)"
" AS sub,"
" host_details"
" WHERE sub.id = host_details.id"
" ORDER BY name ASC;",
host);
}
/**
* @brief Get the name from an asset host detail iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the host detail, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_detail_iterator_name, 1);
/**
* @brief Get the name from an asset host detail iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the host detail, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_detail_iterator_value, 2);
/**
* @brief Get the source type from an asset host detail iterator.
*
* @param[in] iterator Iterator.
*
* @return The source type of the host detail, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_detail_iterator_source_type, 3);
/**
* @brief Get the source ID from an asset host detail iterator.
*
* @param[in] iterator Iterator.
*
* @return The source ID of the host detail, or NULL if iteration is
* complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (host_detail_iterator_source_id, 4);
/**
* @brief Find a host for a specific permission, given a UUID.
*
* @param[in] uuid UUID of host.
* @param[out] host Host return, 0 if successfully failed to find host.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find host), TRUE on error.
*/
static gboolean
find_host_with_permission (const char* uuid, host_t* host,
const char *permission)
{
return find_resource_with_permission ("host", uuid, host, permission, 0);
}
/**
* @brief Check whether a string is an identifier name.
*
* @param[in] name Possible identifier name.
*
* @return 1 if an identifier name, else 0.
*/
static int
identifier_name (const char *name)
{
return (strcmp ("hostname", name) == 0)
|| (strcmp ("MAC", name) == 0)
|| (strcmp ("OS", name) == 0)
|| (strcmp ("ssh-key", name) == 0);
}
/**
* @brief Create a host asset.
*
* @param[in] host_name Host Name.
* @param[in] comment Comment.
* @param[out] host_return Created asset.
*
* @return 0 success, 1 failed to find report, 2 host not an IP address,
* 99 permission denied, -1 error.
*/
int
create_asset_host (const char *host_name, const char *comment,
resource_t* host_return)
{
int host_type;
resource_t host;
gchar *quoted_host_name, *quoted_comment;
if (host_name == NULL)
return -1;
sql_begin_immediate ();
if (acl_user_may ("create_asset") == 0)
{
sql_rollback ();
return 99;
}
host_type = gvm_get_host_type (host_name);
if (host_type != HOST_TYPE_IPV4 && host_type != HOST_TYPE_IPV6)
{
sql_rollback ();
return 2;
}
quoted_host_name = sql_quote (host_name);
quoted_comment = sql_quote (comment ? comment : "");
sql ("INSERT into hosts"
" (uuid, owner, name, comment, creation_time, modification_time)"
" VALUES"
" (make_uuid (), (SELECT id FROM users WHERE uuid = '%s'), '%s', '%s',"
" m_now (), m_now ());",
current_credentials.uuid,
quoted_host_name,
quoted_comment);
g_free (quoted_comment);
host = sql_last_insert_id ();
sql ("INSERT into host_identifiers"
" (uuid, host, owner, name, comment, value, source_type, source_id,"
" source_data, creation_time, modification_time)"
" VALUES"
" (make_uuid (), %llu, (SELECT id FROM users WHERE uuid = '%s'), 'ip',"
" '', '%s', 'User', '%s', '', m_now (), m_now ());",
host,
current_credentials.uuid,
quoted_host_name,
current_credentials.uuid);
g_free (quoted_host_name);
if (host_return)
*host_return = host;
sql_commit ();
return 0;
}
/**
* @brief Create all available assets from a report.
*
* @param[in] report_id UUID of report.
* @param[in] term Filter term, for min_qod and apply_overrides.
*
* @return 0 success, 1 failed to find report, 99 permission denied, -1 error.
*/
int
create_asset_report (const char *report_id, const char *term)
{
resource_t report;
iterator_t hosts;
gchar *quoted_report_id;
int apply_overrides, min_qod;
if (report_id == NULL)
return -1;
sql_begin_immediate ();
if (acl_user_may ("create_asset") == 0)
{
sql_rollback ();
return 99;
}
report = 0;
if (find_report_with_permission (report_id, &report, "get_reports"))
{
sql_rollback ();
return -1;
}
if (report == 0)
{
sql_rollback ();
return 1;
}
/* These are freed by hosts_set_identifiers. */
if (identifiers == NULL)
identifiers = make_array ();
if (identifier_hosts == NULL)
identifier_hosts = make_array ();
quoted_report_id = sql_quote (report_id);
sql ("DELETE FROM host_identifiers WHERE source_id = '%s';",
quoted_report_id);
sql ("DELETE FROM host_oss WHERE source_id = '%s';",
quoted_report_id);
sql ("DELETE FROM host_max_severities WHERE source_id = '%s';",
quoted_report_id);
sql ("DELETE FROM host_details WHERE source_id = '%s';",
quoted_report_id);
g_free (quoted_report_id);
init_report_host_iterator (&hosts, report, NULL, 0);
while (next (&hosts))
{
const char *host;
report_host_t report_host;
iterator_t details;
host = host_iterator_host (&hosts);
report_host = host_iterator_report_host (&hosts);
if (report_host_dead (report_host)
|| report_host_result_count (report_host) == 0)
continue;
host_notice (host, "ip", host, "Report Host", report_id, 0, 0);
init_report_host_details_iterator (&details, report_host);
while (next (&details))
{
const char *name;
name = report_host_details_iterator_name (&details);
if (identifier_name (name))
{
const char *value;
identifier_t *identifier;
value = report_host_details_iterator_value (&details);
if ((strcmp (name, "OS") == 0)
&& (g_str_has_prefix (value, "cpe:") == 0))
continue;
identifier = g_malloc (sizeof (identifier_t));
identifier->ip = g_strdup (host);
identifier->name = g_strdup (name);
identifier->value = g_strdup (value);
identifier->source_id = g_strdup (report_id);
identifier->source_type = g_strdup ("Report Host Detail");
identifier->source_data
= g_strdup (report_host_details_iterator_source_name (&details));
array_add (identifiers, identifier);
array_add_new_string (identifier_hosts, g_strdup (host));
}
}
cleanup_iterator (&details);
}
cleanup_iterator (&hosts);
hosts_set_identifiers (report);
apply_overrides = filter_term_apply_overrides (term);
min_qod = filter_term_min_qod (term);
hosts_set_max_severity (report, &apply_overrides, &min_qod);
hosts_set_details (report);
sql_commit ();
return 0;
}
/**
* @brief Modify an asset.
*
* @param[in] asset_id UUID of asset.
* @param[in] comment Comment on asset.
*
* @return 0 success, 1 failed to find asset, 3 asset_id required,
* 99 permission denied, -1 internal error.
*/
int
modify_asset (const char *asset_id, const char *comment)
{
gchar *quoted_asset_id, *quoted_comment;
resource_t asset;
if (asset_id == NULL)
return 3;
sql_begin_immediate ();
if (acl_user_may ("modify_asset") == 0)
{
sql_rollback ();
return 99;
}
/* Host. */
quoted_asset_id = sql_quote (asset_id);
switch (sql_int64 (&asset,
"SELECT id FROM hosts WHERE uuid = '%s';",
quoted_asset_id))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
asset = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_asset_id);
sql_rollback ();
return -1;
break;
}
g_free (quoted_asset_id);
if (asset == 0)
{
sql_rollback ();
return 1;
}
quoted_comment = sql_quote (comment ?: "");
sql ("UPDATE hosts SET"
" comment = '%s',"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_comment, asset);
g_free (quoted_comment);
sql_commit ();
return 0;
}
/**
* @brief Delete all asset that came from a report.
*
* Assume caller started a transaction.
*
* @param[in] report_id UUID of report.
*
* @return 0 success, 2 failed to find report, 4 UUID
* required, 99 permission denied, -1 error.
*/
static int
delete_report_assets (const char *report_id)
{
resource_t report;
gchar *quoted_report_id;
report = 0;
if (find_report_with_permission (report_id, &report, "delete_report"))
{
sql_rollback ();
return -1;
}
if (report == 0)
{
sql_rollback ();
return 1;
}
quoted_report_id = sql_quote (report_id);
/* Delete the hosts and OSs identified by this report if they were only
* identified by this report. */
sql ("CREATE TEMPORARY TABLE delete_report_assets_hosts (host INTEGER);");
/* Collect hosts that were only identified by the given source. */
sql ("INSERT into delete_report_assets_hosts"
" (host)"
" SELECT id FROM hosts"
" WHERE (EXISTS (SELECT * FROM host_identifiers"
" WHERE host = hosts.id"
" AND source_id = '%s')"
" OR EXISTS (SELECT * FROM host_oss"
" WHERE host = hosts.id"
" AND source_id = '%s'))"
" AND NOT EXISTS (SELECT * FROM host_identifiers"
" WHERE host = hosts.id"
" AND source_id != '%s')"
" AND NOT EXISTS (SELECT * FROM host_oss"
" WHERE host = hosts.id"
" AND source_id != '%s');",
quoted_report_id,
quoted_report_id,
quoted_report_id,
quoted_report_id);
sql ("DELETE FROM host_identifiers WHERE source_id = '%s';",
quoted_report_id);
sql ("DELETE FROM host_oss WHERE source_id = '%s';",
quoted_report_id);
sql ("DELETE FROM host_max_severities WHERE source_id = '%s';",
quoted_report_id);
sql ("DELETE FROM host_details WHERE source_id = '%s';",
quoted_report_id);
g_free (quoted_report_id);
/* The host may have details from sources that did not identify the host. */
sql ("DELETE FROM host_details"
" WHERE host in (SELECT host FROM delete_report_assets_hosts);");
/* The host may have severities from sources that did not identify the
* host. */
sql ("DELETE FROM host_max_severities"
" WHERE host in (SELECT host FROM delete_report_assets_hosts);");
sql ("DELETE FROM hosts"
" WHERE id in (SELECT host FROM delete_report_assets_hosts);");
sql ("DROP TABLE delete_report_assets_hosts;");
sql_commit ();
return 0;
}
/**
* @brief Delete an asset.
*
* @param[in] asset_id UUID of asset.
* @param[in] report_id UUID of report from which to delete assets.
* Overridden by asset_id.
* @param[in] dummy Dummy arg to match other delete functions.
*
* @return 0 success, 1 asset is in use, 2 failed to find asset, 4 UUID
* required, 99 permission denied, -1 error.
*/
int
delete_asset (const char *asset_id, const char *report_id, int dummy)
{
resource_t asset, parent;
gchar *quoted_asset_id, *parent_id;
asset = parent = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_asset") == 0)
{
sql_rollback ();
return 99;
}
if (asset_id == NULL)
{
if (report_id == NULL)
{
sql_rollback ();
return 3;
}
return delete_report_assets (report_id);
}
/* Host identifier. */
quoted_asset_id = sql_quote (asset_id);
switch (sql_int64 (&asset,
"SELECT id FROM host_identifiers WHERE uuid = '%s';",
quoted_asset_id))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
asset = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_asset_id);
sql_rollback ();
return -1;
break;
}
g_free (quoted_asset_id);
if (asset)
{
parent_id = sql_string ("SELECT uuid FROM hosts"
" WHERE id = (SELECT host FROM host_identifiers"
" WHERE id = %llu);",
asset);
parent = 0;
if (find_host_with_permission (parent_id, &parent, "delete_asset"))
{
sql_rollback ();
return -1;
}
if (parent == 0)
{
sql_rollback ();
return 99;
}
sql ("DELETE FROM host_identifiers WHERE id = %llu;", asset);
sql_commit ();
return 0;
}
/* Host OS. */
quoted_asset_id = sql_quote (asset_id);
switch (sql_int64 (&asset,
"SELECT id FROM host_oss WHERE uuid = '%s';",
quoted_asset_id))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
asset = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_asset_id);
sql_rollback ();
return -1;
break;
}
g_free (quoted_asset_id);
if (asset)
{
parent_id = sql_string ("SELECT uuid FROM hosts"
" WHERE id = (SELECT host FROM host_oss"
" WHERE id = %llu);",
asset);
parent = 0;
if (find_host_with_permission (parent_id, &parent, "delete_asset"))
{
sql_rollback ();
return -1;
}
if (parent == 0)
{
sql_rollback ();
return 99;
}
sql ("DELETE FROM host_oss WHERE id = %llu;", asset);
sql_commit ();
return 0;
}
/* OS. */
quoted_asset_id = sql_quote (asset_id);
switch (sql_int64 (&asset,
"SELECT id FROM oss WHERE uuid = '%s';",
quoted_asset_id))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
asset = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_asset_id);
sql_rollback ();
return -1;
break;
}
g_free (quoted_asset_id);
if (asset)
{
if (sql_int ("SELECT count (*) FROM host_oss"
" WHERE os = %llu;",
asset))
{
sql_rollback ();
return 1;
}
sql ("DELETE FROM oss WHERE id = %llu;", asset);
permissions_set_orphans ("os", asset, LOCATION_TABLE);
tags_remove_resource ("os", asset, LOCATION_TABLE);
sql_commit ();
return 0;
}
/* Host. */
quoted_asset_id = sql_quote (asset_id);
switch (sql_int64 (&asset,
"SELECT id FROM hosts WHERE uuid = '%s';",
quoted_asset_id))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
asset = 0;
break;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_asset_id);
sql_rollback ();
return -1;
break;
}
g_free (quoted_asset_id);
if (asset)
{
sql ("DELETE FROM host_identifiers WHERE host = %llu;", asset);
sql ("DELETE FROM host_oss WHERE host = %llu;", asset);
sql ("DELETE FROM host_max_severities WHERE host = %llu;", asset);
sql ("DELETE FROM host_details WHERE host = %llu;", asset);
sql ("DELETE FROM hosts WHERE id = %llu;", asset);
permissions_set_orphans ("host", asset, LOCATION_TABLE);
tags_remove_resource ("host", asset, LOCATION_TABLE);
sql_commit ();
return 0;
}
sql_rollback ();
return 2;
}
/**
* @brief Generates and adds assets from report host details
*
* @param[in] report The report to get host details from.
* @param[in] host_ip IP address of the host to get details from.
*
* @return 0 success, -1 error.
*/
int
add_assets_from_host_in_report (report_t report, const char *host_ip)
{
int ret;
gchar *quoted_host;
char *report_id;
report_host_t report_host = 0;
/* Get report UUID */
report_id = report_uuid (report);
if (report_id == NULL)
{
g_warning ("%s: report %llu not found.",
__func__, report);
return -1;
}
/* Find report_host */
quoted_host = sql_quote (host_ip);
sql_int64 (&report_host,
"SELECT id FROM report_hosts"
" WHERE host = '%s' AND report = %llu",
quoted_host,
report);
g_free (quoted_host);
if (report_host == 0)
{
g_warning ("%s: report_host for host '%s' and report '%s' not found.",
__func__, host_ip, report_id);
free (report_id);
return -1;
}
/* Create assets */
if (report_host_noticeable (report, host_ip))
{
host_notice (host_ip, "ip", host_ip, "Report Host", report_id, 1, 1);
}
ret = add_tls_certificates_from_report_host (report_host,
report_id,
host_ip);
if (ret)
{
free (report_id);
return ret;
}
return 0;
}
/* Settings. */
/**
* @brief Filter columns for setting iterator.
*/
#define SETTING_ITERATOR_FILTER_COLUMNS \
{ "name", "comment", "value", NULL }
/**
* @brief Setting iterator columns.
*/
#define SETTING_ITERATOR_COLUMNS \
{ \
{ "id" , NULL, KEYWORD_TYPE_INTEGER }, \
{ "uuid", NULL, KEYWORD_TYPE_STRING }, \
{ "name", NULL, KEYWORD_TYPE_STRING }, \
{ "comment", NULL, KEYWORD_TYPE_STRING }, \
{ "value", NULL, KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of settings.
*
* @param[in] filter Filter term.
*
* @return Total number of settings in filtered set.
*/
int
setting_count (const char *filter)
{
static const char *filter_columns[] = SETTING_ITERATOR_FILTER_COLUMNS;
static column_t select_columns[] = SETTING_ITERATOR_COLUMNS;
gchar *clause;
int ret;
assert (current_credentials.uuid);
clause = filter_clause ("setting", filter, filter_columns, select_columns,
NULL, 0, NULL, NULL, NULL, NULL, NULL);
ret = sql_int ("SELECT count (*)"
" FROM settings"
" WHERE"
" (owner = (SELECT id FROM users WHERE uuid = '%s')"
" OR (owner IS NULL"
" AND uuid"
" NOT IN (SELECT uuid FROM settings"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'))))"
"%s%s;",
current_credentials.uuid,
current_credentials.uuid,
clause ? " AND " : "",
clause ? clause : "");
g_free (clause);
return ret;
}
/**
* @brief Return the uuid of a resource filter from settings.
*
* @param[in] resource Resource (eg. Filters, Targets, CPE).
*
* @return resource filter uuid in settings if it exists, "" otherwise.
*/
char *
setting_filter (const char *resource)
{
return sql_string ("SELECT value FROM settings WHERE name = '%s Filter'"
" AND " ACL_GLOBAL_OR_USER_OWNS () ""
" ORDER BY coalesce (owner, 0) DESC;",
resource,
current_credentials.uuid);
}
/**
* @brief Return the Default Severity user setting as a double.
*
* @return The user's Default Severity.
*/
static double
setting_default_severity_dbl ()
{
return current_credentials.default_severity;
}
/**
* @brief Return the Dynamic Severity user setting as an int.
*
* @return 1 if user's Dynamic Severity is "Yes", 0 if it is "No",
* or does not exist.
*/
static int
setting_dynamic_severity_int ()
{
return current_credentials.dynamic_severity;
}
/**
* @brief Return the user's timezone.
*
* @return User Severity Class in settings if it exists, else NULL.
*/
static char *
setting_timezone ()
{
return sql_string ("SELECT timezone FROM users WHERE uuid = '%s'",
current_credentials.uuid);
}
/**
* @brief Return the Auto Cache Rebuild user setting as an int.
*
* @return 1 if cache is rebuilt automatically, 0 if not.
*/
static int
setting_auto_cache_rebuild_int ()
{
return sql_int ("SELECT coalesce"
" ((SELECT value FROM settings"
" WHERE uuid = 'a09285b0-2d47-49b6-a4ef-946ee71f1d5c'"
" AND " ACL_USER_OWNS () ""
" ORDER BY coalesce (owner, 0) DESC LIMIT 1),"
" '1');",
current_credentials.uuid);
}
/**
* @brief Initialise a setting iterator, including observed settings.
*
* @param[in] iterator Iterator.
* @param[in] uuid UUID of setting to limit iteration to. 0 for all.
* @param[in] filter Filter term.
* @param[in] first First setting.
* @param[in] max Maximum number of settings returned.
* @param[in] ascending Whether to sort ascending or descending.
* @param[in] sort_field Field to sort on, or NULL for "id".
*/
void
init_setting_iterator (iterator_t *iterator, const char *uuid,
const char *filter, int first, int max, int ascending,
const char *sort_field)
{
static const char *filter_columns[] = SETTING_ITERATOR_FILTER_COLUMNS;
static column_t select_columns[] = SETTING_ITERATOR_COLUMNS;
gchar *clause, *columns, *quoted_uuid;
assert (current_credentials.uuid);
if (first < 0)
first = 0;
if (max < 1)
max = -1;
clause = filter_clause ("setting", filter, filter_columns, select_columns,
NULL, 0, NULL, NULL, NULL, NULL, NULL);
quoted_uuid = uuid ? sql_quote (uuid) : NULL;
columns = columns_build_select (select_columns);
if (quoted_uuid)
init_iterator (iterator,
"SELECT %s"
" FROM settings"
" WHERE uuid = '%s'"
" AND (owner = (SELECT id FROM users WHERE uuid = '%s')"
" OR (owner IS NULL"
" AND uuid"
" NOT IN (SELECT uuid FROM settings"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'))))",
columns,
quoted_uuid,
current_credentials.uuid,
current_credentials.uuid);
else
init_iterator (iterator,
"SELECT %s"
" FROM settings"
" WHERE"
" (owner = (SELECT id FROM users WHERE uuid = '%s')"
" OR (owner IS NULL"
" AND uuid"
" NOT IN (SELECT uuid FROM settings"
" WHERE owner = (SELECT id FROM users"
" WHERE uuid = '%s'))))"
"%s%s"
" ORDER BY %s %s"
" LIMIT %s OFFSET %i;",
columns,
current_credentials.uuid,
current_credentials.uuid,
clause ? " AND " : "",
clause ? clause : "",
sort_field ? sort_field : "id",
ascending ? "ASC" : "DESC",
sql_select_limit (max),
first);
g_free (quoted_uuid);
g_free (columns);
g_free (clause);
}
/**
* @brief Get the UUID from a setting iterator.
*
* @param[in] iterator Iterator.
*
* @return The UUID of the setting, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (setting_iterator_uuid, 1);
/**
* @brief Get the name from a setting iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the setting, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (setting_iterator_name, 2);
/**
* @brief Get the comment from a setting iterator.
*
* @param[in] iterator Iterator.
*
* @return The comment of the setting, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (setting_iterator_comment, 3);
/**
* @brief Get the value from a setting iterator.
*
* @param[in] iterator Iterator.
*
* @return The value of the setting, or NULL if iteration is complete. Freed by
* cleanup_iterator.
*/
DEF_ACCESS (setting_iterator_value, 4);
/**
* @brief Get the value of a setting as a string.
*
* @param[in] uuid UUID of setting.
* @param[out] value Freshly allocated value.
*
* @return 0 success, -1 error.
*/
int
setting_value (const char *uuid, char **value)
{
gchar *quoted_uuid;
if (value == NULL || uuid == NULL)
return -1;
quoted_uuid = sql_quote (uuid);
if (sql_int ("SELECT count (*)"
" FROM settings"
" WHERE uuid = '%s'"
" AND " ACL_GLOBAL_OR_USER_OWNS () ";",
quoted_uuid,
current_credentials.uuid)
== 0)
{
*value = NULL;
g_free (quoted_uuid);
return -1;
}
*value = sql_string
("SELECT value"
" FROM settings"
" WHERE uuid = '%s'"
" AND " ACL_GLOBAL_OR_USER_OWNS ()
/* Force the user's setting to come before the default. */
" ORDER BY coalesce (owner, 0) DESC;",
quoted_uuid,
current_credentials.uuid);
g_free (quoted_uuid);
return 0;
}
/**
* @brief Get the value of a setting.
*
* @param[in] uuid UUID of setting.
* @param[out] value Value.
*
* @return 0 success, -1 error.
*/
static int
setting_value_int (const char *uuid, int *value)
{
gchar *quoted_uuid;
if (value == NULL || uuid == NULL)
return -1;
quoted_uuid = sql_quote (uuid);
if (sql_int ("SELECT count (*)"
" FROM settings"
" WHERE uuid = '%s'"
" AND " ACL_GLOBAL_OR_USER_OWNS () ";",
quoted_uuid,
current_credentials.uuid)
== 0)
{
*value = -1;
g_free (quoted_uuid);
return -1;
}
*value = sql_int ("SELECT value"
" FROM settings"
" WHERE uuid = '%s'"
" AND " ACL_GLOBAL_OR_USER_OWNS ()
/* Force the user's setting to come before the default. */
" ORDER BY coalesce (owner, 0) DESC;",
quoted_uuid,
current_credentials.uuid);
g_free (quoted_uuid);
return 0;
}
/**
* @brief Set the value of a setting.
*
* @param[in] uuid UUID of setting.
* @param[in] name Setting name. For Timezone and Password.
* @param[in] value_64 New setting value, base64 encoded.
* @param[out] r_errdesc If not NULL the address of a variable to receive
* a malloced string with the error description. Will
* always be set to NULL on success.
*
* @return 0 success, 1 failed to find setting, 2 syntax error in value,
* 99 permission denied, -1 on error.
*/
int
modify_setting (const gchar *uuid, const gchar *name,
const gchar *value_64, gchar **r_errdesc)
{
char *setting_name;
assert (current_credentials.uuid);
if (acl_user_may ("modify_setting") == 0)
return 99;
if (r_errdesc)
*r_errdesc = NULL;
if (name && (strcmp (name, "Timezone") == 0))
{
gsize value_size;
gchar *quoted_timezone, *value;
if (value_64 && strlen (value_64))
{
value = (gchar*) g_base64_decode (value_64, &value_size);
if (g_utf8_validate (value, value_size, NULL) == FALSE)
{
if (r_errdesc)
*r_errdesc = g_strdup ("Value cannot be decoded to"
" valid UTF-8");
g_free (value);
return -1;
}
}
else
{
value = g_strdup ("");
value_size = 0;
}
quoted_timezone = sql_quote (value);
g_free (value);
sql ("UPDATE users SET timezone = '%s', modification_time = m_now ()"
" WHERE uuid = '%s';",
quoted_timezone,
current_credentials.uuid);
g_free (quoted_timezone);
return 0;
}
if (name && (strcmp (name, "Password") == 0))
{
gsize value_size;
gchar *value;
int ret;
assert (current_credentials.username);
if (value_64 && strlen (value_64))
{
value = (gchar*) g_base64_decode (value_64, &value_size);
if (g_utf8_validate (value, value_size, NULL) == FALSE)
{
if (r_errdesc)
*r_errdesc = g_strdup ("Value cannot be decoded to"
" valid UTF-8");
g_free (value);
return -1;
}
}
else
{
value = g_strdup ("");
value_size = 0;
}
ret = set_password (current_credentials.username,
current_credentials.uuid,
value,
r_errdesc);
g_free (value);
return ret;
}
if (uuid && (strcmp (uuid, SETTING_UUID_ROWS_PER_PAGE) == 0
|| strcmp (uuid, "6765549a-934e-11e3-b358-406186ea4fc5") == 0
|| strcmp (uuid, "77ec2444-e7f2-4a80-a59b-f4237782d93f") == 0
|| strcmp (uuid, "7eda49c5-096c-4bef-b1ab-d080d87300df") == 0
|| strcmp (uuid, "578a1c14-e2dc-45ef-a591-89d31391d007") == 0
|| strcmp (uuid, "02e294fa-061b-11e6-ae64-28d24461215b") == 0
|| strcmp (uuid, "5a9046cc-0628-11e6-ba53-28d24461215b") == 0
|| strcmp (uuid, "a09285b0-2d47-49b6-a4ef-946ee71f1d5c") == 0))
{
gsize value_size;
gchar *value, *quoted_uuid, *quoted_value;
assert (current_credentials.username);
quoted_uuid = sql_quote (uuid);
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '%s'"
" AND " ACL_IS_GLOBAL () ";",
quoted_uuid,
current_credentials.uuid)
== 0)
{
g_free (quoted_uuid);
return 1;
}
if (value_64 && strlen (value_64))
{
value = (gchar*) g_base64_decode (value_64, &value_size);
if (g_utf8_validate (value, value_size, NULL) == FALSE)
{
if (r_errdesc)
*r_errdesc = g_strdup ("Value cannot be decoded to"
" valid UTF-8");
g_free (value);
return -1;
}
}
else
{
value = g_strdup ("");
value_size = 0;
}
if (strcmp (uuid, SETTING_UUID_ROWS_PER_PAGE) == 0)
{
const gchar *val;
/* Rows Per Page. */
val = value;
while (*val && isdigit (*val)) val++;
if (*val && strcmp (value, "-1"))
{
g_free (quoted_uuid);
return 2;
}
}
if (strcmp (uuid, "6765549a-934e-11e3-b358-406186ea4fc5") == 0)
{
GRegex *languages_regex;
gboolean match;
/*
* regex: colon-separated lists of language or language and country
* codes (ISO 639-1, 639-2 and 3166-1 alpha-2)
* as used in the LANGUAGE env variable by gettext
*/
languages_regex
= g_regex_new ("^(Browser Language|"
"([a-z]{2,3})(_[A-Z]{2})?(@[[:alnum:]_-]+)?"
"(:([a-z]{2,3})(_[A-Z]{2})?(@[[:alnum:]_-]+)?)*)$",
0, 0, NULL);
match = g_regex_match (languages_regex, value, 0, NULL);
g_regex_unref (languages_regex);
/* User Interface Language. */
if (match)
{
// Valid languages string or "Browser Language":
// keep string as it is
}
/* Legacy full language names */
else if (strcmp (value, "Chinese") == 0)
{
g_free (value);
value = g_strdup ("zh_CN");
}
else if (strcmp (value, "English") == 0)
{
g_free (value);
value = g_strdup ("en");
}
else if (strcmp (value, "German") == 0)
{
g_free (value);
value = g_strdup ("de");
}
/* Invalid value */
else
{
g_free (quoted_uuid);
g_free (value);
return 2;
}
}
if (strcmp (uuid, "77ec2444-e7f2-4a80-a59b-f4237782d93f") == 0)
{
/* Dynamic Severity */
current_credentials.dynamic_severity = atoi (value);
reports_clear_count_cache (current_credentials.uuid);
}
if (strcmp (uuid, "7eda49c5-096c-4bef-b1ab-d080d87300df") == 0)
{
double severity_dbl;
/* Default Severity */
if (sscanf (value, "%lf", &severity_dbl) != 1
|| severity_dbl < 0.0 || severity_dbl > 10.0)
{
g_free (value);
return 2;
}
else
current_credentials.default_severity = severity_dbl;
}
if (strcmp (uuid, "a09285b0-2d47-49b6-a4ef-946ee71f1d5c") == 0)
{
int value_int;
/* Auto Cache Rebuild */
if (sscanf (value, "%d", &value_int) != 1
|| (strcmp (value, "0") && strcmp (value, "1")))
{
g_free (value);
return 2;
}
}
quoted_value = sql_quote (value);
g_free (value);
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '%s'"
" AND owner = (SELECT id FROM users WHERE uuid = '%s');",
quoted_uuid,
current_credentials.uuid))
sql ("UPDATE settings SET value = '%s'"
" WHERE uuid = '%s'"
" AND owner = (SELECT id FROM users WHERE uuid = '%s');",
quoted_value,
quoted_uuid,
current_credentials.uuid);
else
sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
" VALUES"
" ('%s',"
" (SELECT id FROM users WHERE uuid = '%s'),"
" (SELECT name FROM settings"
" WHERE uuid = '%s' AND " ACL_IS_GLOBAL ()
" LIMIT 1),"
" (SELECT comment FROM settings"
" WHERE uuid = '%s' AND " ACL_IS_GLOBAL ()
" LIMIT 1),"
" '%s');",
quoted_uuid,
current_credentials.uuid,
quoted_uuid,
quoted_uuid,
quoted_value);
g_free (quoted_uuid);
g_free (quoted_value);
return 0;
}
/* Export file name format */
if (uuid
&& (strcmp (uuid, "a6ac88c5-729c-41ba-ac0a-deea4a3441f2") == 0
|| strcmp (uuid, "0872a6ed-4f85-48c5-ac3f-a5ef5e006745") == 0
|| strcmp (uuid, "e1a2ae0b-736e-4484-b029-330c9e15b900") == 0))
{
gsize value_size;
gchar *value, *quoted_value;
assert (current_credentials.uuid);
if (strcmp (uuid, "a6ac88c5-729c-41ba-ac0a-deea4a3441f2") == 0)
setting_name = "Details Export File Name";
else if (strcmp (uuid, "0872a6ed-4f85-48c5-ac3f-a5ef5e006745") == 0)
setting_name = "List Export File Name";
else if (strcmp (uuid, "e1a2ae0b-736e-4484-b029-330c9e15b900") == 0)
setting_name = "Report Export File Name";
else
return -1;
if (value_64 && strlen (value_64))
{
value = (gchar*) g_base64_decode (value_64, &value_size);
if (g_utf8_validate (value, value_size, NULL) == FALSE)
{
if (r_errdesc)
*r_errdesc = g_strdup ("Value cannot be decoded to"
" valid UTF-8");
g_free (value);
return -1;
}
}
else
{
value = g_strdup ("");
value_size = 0;
}
quoted_value = sql_quote (value);
if (strcmp (value, "") == 0)
{
g_free (value);
g_free (quoted_value);
return 2;
}
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '%s'"
" AND owner = (SELECT id FROM users WHERE uuid = '%s');",
uuid,
current_credentials.uuid))
sql ("UPDATE settings SET value = '%s'"
" WHERE uuid = '%s'"
" AND owner = (SELECT id FROM users WHERE uuid = '%s');",
quoted_value,
uuid,
current_credentials.uuid);
else
sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
" VALUES"
" ('%s',"
" (SELECT id FROM users WHERE uuid = '%s'),"
" '%s',"
" (SELECT comment FROM settings"
" WHERE uuid = '%s' AND " ACL_IS_GLOBAL () "),"
" '%s');",
uuid,
current_credentials.uuid,
setting_name,
uuid,
quoted_value);
g_free (value);
g_free (quoted_value);
return 0;
}
/* Resources filters, default resource selections and chart preferences. */
setting_name = NULL;
if (uuid)
{
/* Filters */
if (strcmp (uuid, "b833a6f2-dcdc-4535-bfb0-a5154b5b5092") == 0)
setting_name = g_strdup ("Alerts Filter");
else if (strcmp (uuid, "0f040d06-abf9-43a2-8f94-9de178b0e978") == 0)
setting_name = g_strdup ("Assets Filter");
else if (strcmp (uuid, "1a9fbd91-0182-44cd-bc88-a13a9b3b1bef") == 0)
setting_name = g_strdup ("Configs Filter");
else if (strcmp (uuid, "186a5ac8-fe5a-4fb1-aa22-44031fb339f3") == 0)
setting_name = g_strdup ("Credentials Filter");
else if (strcmp (uuid, "f9691163-976c-47e7-ad9a-38f2d5c81649") == 0)
setting_name = g_strdup ("Filters Filter");
else if (strcmp (uuid, "f722e5a4-88d8-475f-95b9-e4dcafbc075b") == 0)
setting_name = g_strdup ("Groups Filter");
else if (strcmp (uuid, "37562dfe-1f7e-4cae-a7c0-fa95e6f194c5") == 0)
setting_name = g_strdup ("Hosts Filter");
else if (strcmp (uuid, "96abcd5a-9b6d-456c-80b8-c3221bfa499d") == 0)
setting_name = g_strdup ("Notes Filter");
else if (strcmp (uuid, "f608c3ec-ce73-4ff6-8e04-7532749783af") == 0)
setting_name = g_strdup ("Operating Systems Filter");
else if (strcmp (uuid, "eaaaebf1-01ef-4c49-b7bb-955461c78e0a") == 0)
setting_name = g_strdup ("Overrides Filter");
else if (strcmp (uuid, "ffb16b28-538c-11e3-b8f9-406186ea4fc5") == 0)
setting_name = g_strdup ("Permissions Filter");
else if (strcmp (uuid, "7d52d575-baeb-4d98-bb68-e1730dbc6236") == 0)
setting_name = g_strdup ("Port Lists Filter");
else if (strcmp (uuid, "48ae588e-9085-41bc-abcb-3d6389cf7237") == 0)
setting_name = g_strdup ("Reports Filter");
else if (strcmp (uuid, "249c7a55-065c-47fb-b453-78e11a665565") == 0)
setting_name = g_strdup ("Report Formats Filter");
else if (strcmp (uuid, "739ab810-163d-11e3-9af6-406186ea4fc5") == 0)
setting_name = g_strdup ("Results Filter");
else if (strcmp (uuid, "f38e673a-bcd1-11e2-a19a-406186ea4fc5") == 0)
setting_name = g_strdup ("Roles Filter");
else if (strcmp (uuid, "ba00fe91-bdce-483c-b8df-2372e9774ad6") == 0)
setting_name = g_strdup ("Scanners Filter");
else if (strcmp (uuid, "a83e321b-d994-4ae8-beec-bfb5fe3e7336") == 0)
setting_name = g_strdup ("Schedules Filter");
else if (strcmp (uuid, "108eea3b-fc61-483c-9da9-046762f137a8") == 0)
setting_name = g_strdup ("Tags Filter");
else if (strcmp (uuid, "236e2e41-9771-4e7a-8124-c432045985e0") == 0)
setting_name = g_strdup ("Targets Filter");
else if (strcmp (uuid, "1c981851-8244-466c-92c4-865ffe05e721") == 0)
setting_name = g_strdup ("Tasks Filter");
else if (strcmp (uuid, "801544de-f06d-4377-bb77-bbb23369bad4") == 0)
setting_name = g_strdup ("Tickets Filter");
else if (strcmp (uuid, "34a176c1-0278-4c29-b84d-3d72117b2169") == 0)
setting_name = g_strdup ("TLS Certificates Filter");
else if (strcmp (uuid, "a33635be-7263-4549-bd80-c04d2dba89b4") == 0)
setting_name = g_strdup ("Users Filter");
else if (strcmp (uuid, "17c9d269-95e7-4bfa-b1b2-bc106a2175c7") == 0)
setting_name = g_strdup ("Vulnerabilities Filter");
else if (strcmp (uuid, "3414a107-ae46-4dea-872d-5c4479a48e8f") == 0)
setting_name = g_strdup ("CPE Filter");
else if (strcmp (uuid, "def63b5a-41ef-43f4-b9ef-03ef1665db5d") == 0)
setting_name = g_strdup ("CVE Filter");
else if (strcmp (uuid, "bef08b33-075c-4f8c-84f5-51f6137e40a3") == 0)
setting_name = g_strdup ("NVT Filter");
else if (strcmp (uuid, "adb6ffc8-e50e-4aab-9c31-13c741eb8a16") == 0)
setting_name = g_strdup ("OVAL Filter");
else if (strcmp (uuid, "e4cf514a-17e2-4ab9-9c90-336f15e24750") == 0)
setting_name = g_strdup ("CERT-Bund Filter");
else if (strcmp (uuid, "312350ed-bc06-44f3-8b3f-ab9eb828b80b") == 0)
setting_name = g_strdup ("DFN-CERT Filter");
else if (strcmp (uuid, "32b3d606-461b-4770-b3e1-b9ea3cf0f84c") == 0)
setting_name = g_strdup ("Notes Filter");
else if (strcmp (uuid, "956d13bd-3baa-4404-a138-5e7eb8f9630e") == 0)
setting_name = g_strdup ("Overrides Filter");
/* Content composer defaults */
else if (strcmp (uuid, "b6b449ee-5d90-4ff0-af20-7e838c389d39") == 0)
setting_name = g_strdup ("Report Composer Defaults");
/* Default resource selections */
else if (strcmp (uuid, "f9f5a546-8018-48d0-bef5-5ad4926ea899") == 0)
setting_name = g_strdup ("Default Alert");
else if (strcmp (uuid, "fe7ea321-e3e3-4cc6-9952-da836aae83ce") == 0)
setting_name = g_strdup ("Default OpenVAS Scan Config");
else if (strcmp (uuid, "fb19ac4b-614c-424c-b046-0bc32bf1be73") == 0)
setting_name = g_strdup ("Default OSP Scan Config");
else if (strcmp (uuid, "6fc56b72-c1cf-451c-a4c4-3a9dc784c3bd") == 0)
setting_name = g_strdup ("Default SSH Credential");
else if (strcmp (uuid, "a25c0cfe-f977-417b-b1da-47da370c03e8") == 0)
setting_name = g_strdup ("Default SMB Credential");
else if (strcmp (uuid, "83545bcf-0c49-4b4c-abbf-63baf82cc2a7") == 0)
setting_name = g_strdup ("Default ESXi Credential");
else if (strcmp (uuid, "024550b8-868e-4b3c-98bf-99bb732f6a0d") == 0)
setting_name = g_strdup ("Default SNMP Credential");
else if (strcmp (uuid, "d74a9ee8-7d35-4879-9485-ab23f1bd45bc") == 0)
setting_name = g_strdup ("Default Port List");
else if (strcmp (uuid, "f7d0f6ed-6f9e-45dc-8bd9-05cced84e80d") == 0)
setting_name = g_strdup ("Default OpenVAS Scanner");
else if (strcmp (uuid, "b20697c9-be0a-4cd4-8b4d-5fe7841ebb03") == 0)
setting_name = g_strdup ("Default OSP Scanner");
else if (strcmp (uuid, "353304fc-645e-11e6-ba7a-28d24461215b") == 0)
setting_name = g_strdup ("Default Report Format");
else if (strcmp (uuid, "778eedad-5550-4de0-abb6-1320d13b5e18") == 0)
setting_name = g_strdup ("Default Schedule");
else if (strcmp (uuid, "23409203-940a-4b4a-b70c-447475f18323") == 0)
setting_name = g_strdup ("Default Target");
/*
* Main dashboard
*/
else if (strcmp (uuid, "d97eca9f-0386-4e5d-88f2-0ed7f60c0646") == 0)
setting_name = g_strdup ("Main Dashboard Configuration");
/*
* Scans dashboards
*/
else if (strcmp (uuid, "c7584d7c-649f-4f8b-9ded-9e1dc20f24c8") == 0)
setting_name = g_strdup ("Scans Dashboard Configuration");
/* Tasks dashboard settings */
else if (strcmp (uuid, "3d5db3c7-5208-4b47-8c28-48efc621b1e0") == 0)
setting_name = g_strdup ("Tasks Top Dashboard Configuration");
/* Reports dashboard settings */
else if (strcmp (uuid, "e599bb6b-b95a-4bb2-a6bb-fe8ac69bc071") == 0)
setting_name = g_strdup ("Reports Top Dashboard Configuration");
/* Results dashboard settings */
else if (strcmp (uuid, "0b8ae70d-d8fc-4418-8a72-e65ac8d2828e") == 0)
setting_name = g_strdup ("Results Top Dashboard Configuration");
/* Vulns dashboard settings */
else if (strcmp (uuid, "43690dcb-3174-4d84-aa88-58c1936c7f5c") == 0)
setting_name = g_strdup ("Vulnerabilities Top Dashboard Configuration");
/* Notes dashboard settings */
else if (strcmp (uuid, "ce7b121-c609-47b0-ab57-fd020a0336f4a") == 0)
setting_name = g_strdup ("Notes Top Dashboard Configuration");
/* Overrides dashboard settings */
else if (strcmp (uuid, "054862fe-0781-4527-b1aa-2113bcd16ce7") == 0)
setting_name = g_strdup ("Overrides Top Dashboard Configuration");
/*
* Assets dashboards
*/
else if (strcmp (uuid, "0320e0db-bf30-4d4f-9379-b0a022d07cf7") == 0)
setting_name = g_strdup ("Assets Dashboard Configuration");
/* Hosts dashboard settings */
else if (strcmp (uuid, "d3f5f2de-a85b-43f2-a817-b127457cc8ba") == 0)
setting_name = g_strdup ("Hosts Top Dashboard Configuration");
/* TLS Certificate dashboard settings */
else if (strcmp (uuid, "9b62bf16-bf90-11e9-ad97-28d24461215b") == 0)
setting_name = g_strdup ("TLS Certificates Top Dashboard Configuration");
/* Operating Systems dashboard settings */
else if (strcmp (uuid, "e93b51ed-5881-40e0-bc4f-7d3268a36177") == 0)
setting_name = g_strdup ("OSs Top Dashboard Configuration");
/*
* SecInfo dashboards
*/
else if (strcmp (uuid, "84ab32da-fe69-44d8-8a8f-70034cf28d4e") == 0)
setting_name = g_strdup ("SecInfo Dashboard Configuration");
/* NVTs dashboard settings */
else if (strcmp (uuid, "f68d9369-1945-477b-968f-121c6029971b") == 0)
setting_name = g_strdup ("NVTs Top Dashboard Configuration");
/* CVEs dashboard settings */
else if (strcmp (uuid, "815ddd2e-8654-46c7-a05b-d73224102240") == 0)
setting_name = g_strdup ("CVEs Top Dashboard Configuration");
/* CPEs dashboard settings */
else if (strcmp (uuid, "9cff9b4d-b164-43ce-8687-f2360afc7500") == 0)
setting_name = g_strdup ("CPEs Top Dashboard Configuration");
/* OVAL Definitions dashboard settings */
else if (strcmp (uuid, "9563efc0-9f4e-4d1f-8f8d-0205e32b90a4") == 0)
setting_name = g_strdup ("OVAL Definitions Top Dashboard Configuration");
/* CERT-Bund Advisories dashboard settings */
else if (strcmp (uuid, "a6946f44-480f-4f37-8a73-28a4cd5310c4") == 0)
setting_name = g_strdup ("CERT-Bund Advisories Top Dashboard"
" Configuration");
/* DFN-CERT Advisories */
else if (strcmp (uuid, "9812ea49-682d-4f99-b3cc-eca051d1ce59") == 0)
setting_name = g_strdup ("DFN-CERT Advisories Top Dashboard"
" Configuration");
/* All SecInfo */
else if (strcmp (uuid, "4c7b1ea7-b7e6-4d12-9791-eb9f72b6f864") == 0)
setting_name = g_strdup ("All SecInfo Top Dashboard Configuration");
/*
* Resilience / Remediation dashboards
*/
/* Tickets */
else if (strcmp (uuid, "70b0626f-a835-478e-8194-e09f97887a15") == 0)
setting_name = g_strdup ("Tickets Top Dashboard Configuration");
/* Business Process Model (BPM) */
else if (strcmp (uuid, "3232d608-e5bb-415e-99aa-019f16eede8d") == 0)
setting_name = g_strdup ("BPM Dashboard Configuration");
/*
* Client data for Business Process Modeling (BPM)
*/
else if (strcmp (uuid, "3ce2d136-bb52-448a-93f0-20069566f877") == 0)
setting_name = g_strdup ("BPM Data");
}
if (setting_name)
{
gchar *quoted_value, *value;
gsize value_size;
assert (current_credentials.username);
if (value_64 && strlen (value_64))
{
value = (gchar*) g_base64_decode (value_64, &value_size);
if (g_utf8_validate (value, value_size, NULL) == FALSE)
{
if (r_errdesc)
*r_errdesc = g_strdup ("Value cannot be decoded to"
" valid UTF-8");
g_free (value);
return -1;
}
}
else
{
value = g_strdup ("");
value_size = 0;
}
quoted_value = sql_quote (value);
if (sql_int ("SELECT count(*) FROM settings"
" WHERE uuid = '%s'"
" AND owner = (SELECT id FROM users WHERE uuid = '%s');",
uuid,
current_credentials.uuid))
sql ("UPDATE settings SET value = '%s'"
" WHERE uuid = '%s'"
" AND owner = (SELECT id FROM users WHERE uuid = '%s');",
quoted_value,
uuid,
current_credentials.uuid);
else
sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
" VALUES"
" ('%s',"
" (SELECT id FROM users WHERE uuid = '%s'),"
" '%s',"
" (SELECT comment FROM settings"
" WHERE uuid = '%s' AND " ACL_IS_GLOBAL () "),"
" '%s');",
uuid,
current_credentials.uuid,
setting_name,
uuid,
quoted_value);
g_free (value);
g_free (quoted_value);
return 0;
}
return 1;
}
/**
* @brief Return max, adjusted according to maximum allowed rows.
*
* @param[in] max Max.
*
* @return Adjusted max.
*/
int
manage_max_rows (int max)
{
int max_rows;
if (current_credentials.uuid == NULL)
return max;
if (ignore_max_rows_per_page
|| setting_value_int (SETTING_UUID_MAX_ROWS_PER_PAGE, &max_rows))
return max;
if (max_rows && (max < 0 || max > max_rows))
return max_rows;
return max;
}
/**
* @brief Get the name of a setting.
*
* @param[in] uuid UUID of setting.
*
* @return Setting name.
*/
static const gchar *
setting_name (const gchar *uuid)
{
if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
return "Default CA Cert";
if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
return "Max Rows Per Page";
if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
return "Debian LSC Package Maintainer";
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
return "Feed Import Owner";
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
return "Feed Import Roles";
return NULL;
}
/**
* @brief Check whether a setting is the Default CA Cert setting.
*
* @param[in] uuid UUID of setting.
*
* @return 1 if Default CA Cert, else 0.
*/
int
setting_is_default_ca_cert (const gchar *uuid)
{
return strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0;
}
/**
* @brief Get the description of a setting.
*
* @param[in] uuid UUID of setting.
*
* @return Setting description.
*/
static const gchar *
setting_description (const gchar *uuid)
{
if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
return "Default CA Certificate for Scanners";
if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
return "The default maximum number of rows displayed in any listing.";
if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
return "Maintainer email address used in generated Debian LSC packages.";
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
return "User who is given ownership of new resources from feed.";
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
return "Roles given access to new resources from feed.";
return NULL;
}
/**
* @brief Get the name of a setting.
*
* @param[in] uuid UUID of setting.
* @param[in] value Value of setting, to verify.
* @param[in] user User setting is to apply to, or NULL.
*
* @return 0 if valid, else 1.
*/
static int
setting_verify (const gchar *uuid, const gchar *value, const gchar *user)
{
if (value == NULL)
return 0;
if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
return 0;
if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
{
int max_rows;
max_rows = atoi (value);
if (user)
{
if (max_rows < -1)
return 1;
}
else if (max_rows < 0)
return 1;
}
if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
{
if (g_regex_match_simple
("^([[:alnum:]-_]*@[[:alnum:]-_][[:alnum:]-_.]*)?$",
value, 0, 0) == FALSE)
return 1;
}
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0
&& strlen (value))
{
user_t value_user;
gchar *quoted_uuid;
quoted_uuid = sql_quote (value);
switch (sql_int64 (&value_user,
"SELECT id FROM users WHERE uuid = '%s';",
quoted_uuid))
{
case 0:
break;
case 1: /* Too few rows in result of query. */
g_free (quoted_uuid);
return 1;
default: /* Programming error. */
assert (0);
case -1:
g_free (quoted_uuid);
return 1;
}
g_free (quoted_uuid);
}
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
{
gchar **split, **point;
point = split = g_strsplit (value, ",", 0);
while (*point)
{
if (g_regex_match_simple ("^[-0123456789abcdefABCDEF]{36}$",
g_strstrip (*point), 0, 0)
== FALSE)
{
g_strfreev (split);
return 1;
}
point++;
}
g_strfreev (split);
}
return 0;
}
/**
* @brief Normalise the value of a setting.
*
* @param[in] uuid UUID of setting.
* @param[in] value Value of setting, to verify.
*
* @return Normalised value.
*/
static gchar *
setting_normalise (const gchar *uuid, const gchar *value)
{
if (value == NULL)
return NULL;
if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
{
int max_rows;
max_rows = atoi (value);
if (max_rows < 0)
return NULL;
return g_strdup_printf ("%i", max_rows);
}
if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
{
return g_strstrip (g_strdup (value));
}
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
{
GString *normalised;
gchar **split, **point;
normalised = g_string_new ("");
point = split = g_strsplit (value, ",", 0);
while (*point)
{
g_string_append_printf (normalised,
"%s%s",
point == split ? "" : ",",
g_strstrip (*point));
point++;
}
g_strfreev (split);
g_string_ascii_down (normalised);
return g_string_free (normalised, FALSE);
}
return g_strdup (value);
}
/**
* @brief Change value of a setting.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] name Name of user.
* @param[in] uuid UUID of setting.
* @param[in] value New value.
*
* @return 0 success, 1 failed to find user, 2 value out of range, 3 error in
* setting uuid, 4 modifying setting for a single user forbidden,
* 5 syntax error in setting value, -1 error.
*/
int
manage_modify_setting (GSList *log_config, const db_conn_info_t *database,
const gchar *name, const gchar *uuid, const char *value)
{
int ret;
gchar *quoted_name, *quoted_description, *quoted_value, *normalised;
g_info (" Modifying setting.");
if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT)
&& strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE)
&& strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER)
&& strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER)
&& strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES))
{
fprintf (stderr, "Error in setting UUID.\n");
return 3;
}
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
sql_begin_immediate ();
if (setting_verify (uuid, value, name))
{
sql_rollback ();
fprintf (stderr, "Syntax error in setting value.\n");
manage_option_cleanup ();
return 5;
}
if (name)
{
user_t user;
if ((strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
|| (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
|| (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0))
{
sql_rollback ();
fprintf (stderr,
"Modifying this setting for a single user is forbidden.\n");
manage_option_cleanup ();
return 4;
}
if (find_user_by_name (name, &user))
{
sql_rollback ();
fprintf (stderr, "Internal error.\n");
manage_option_cleanup ();
return -1;
}
if (user == 0)
{
sql_rollback ();
fprintf (stderr, "Failed to find user.\n");
manage_option_cleanup ();
return 1;
}
sql ("DELETE FROM settings"
" WHERE uuid = '%s'"
" AND owner = %llu;",
uuid,
user);
normalised = setting_normalise (uuid, value);
if (normalised)
{
quoted_value = sql_quote (normalised);
g_free (normalised);
quoted_name = sql_quote (setting_name (uuid));
quoted_description = sql_quote (setting_description (uuid));
sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
" VALUES ('%s', %llu, '%s', '%s', '%s');",
uuid,
user,
quoted_name,
quoted_description,
quoted_value);
g_free (quoted_value);
g_free (quoted_name);
g_free (quoted_description);
}
}
else
{
sql ("DELETE FROM settings"
" WHERE uuid = '%s'"
" AND owner IS NULL;",
uuid);
normalised = setting_normalise (uuid, value);
if (normalised)
{
quoted_value = sql_quote (normalised);
g_free (normalised);
quoted_name = sql_quote (setting_name (uuid));
quoted_description = sql_quote (setting_description (uuid));
sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
" VALUES ('%s', NULL, '%s', '%s', '%s');",
uuid,
quoted_name,
quoted_description,
quoted_value);
g_free (quoted_value);
g_free (quoted_name);
g_free (quoted_description);
if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
{
migrate_predefined_configs ();
migrate_predefined_port_lists ();
if (migrate_predefined_report_formats ())
{
sql_rollback ();
manage_option_cleanup ();
return -1;
}
}
}
}
sql_commit ();
manage_option_cleanup ();
return 0;
}
/**
* @brief Get the default CA cert.
*
* @return Freshly allocated value of Default CA Cert setting.
*/
char *
manage_default_ca_cert ()
{
return sql_string ("SELECT value FROM settings"
" WHERE uuid = '" SETTING_UUID_DEFAULT_CA_CERT "';");
}
/* Users. */
/**
* @brief Create the given user.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] name Name of user.
* @param[in] password Password for user. Autogenerated if NULL.
* @param[in] role_name Role of user. Admin if NULL.
*
* @return 0 success, -1 error,
* -2 database is wrong version, -3 database needs to be initialised
* from server.
*/
int
manage_create_user (GSList *log_config, const db_conn_info_t *database,
const gchar *name, const gchar *password,
const gchar *role_name)
{
char *uuid;
array_t *roles;
int ret;
gchar *rejection_msg;
g_info (" Creating user.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
roles = make_array ();
if (role_name)
{
role_t role;
if (find_role_by_name (role_name, &role))
{
array_free (roles);
fprintf (stderr, "Internal Error.\n");
manage_option_cleanup ();
return -1;
}
if (role == 0)
{
array_free (roles);
fprintf (stderr, "Failed to find role.\n");
manage_option_cleanup ();
return -1;
}
array_add (roles, role_uuid (role));
}
else
array_add (roles, g_strdup (ROLE_UUID_ADMIN));
if (password)
uuid = NULL;
else
uuid = gvm_uuid_make ();
/* Setup a dummy user, so that create_user will work. */
current_credentials.uuid = "";
ret = create_user (name, password ? password : uuid, "", NULL, 0, NULL, 0,
NULL, NULL, NULL, roles, NULL, &rejection_msg, NULL, 0);
switch (ret)
{
case 0:
if (password)
printf ("User created.\n");
else
printf ("User created with password '%s'.\n", uuid);
break;
case -2:
fprintf (stderr, "User exists already.\n");
break;
default:
if (rejection_msg)
{
fprintf (stderr, "Failed to create user: %s\n", rejection_msg);
}
else
fprintf (stderr, "Failed to create user.\n");
break;
}
current_credentials.uuid = NULL;
g_free (rejection_msg);
array_free (roles);
free (uuid);
manage_option_cleanup ();
return ret;
}
/**
* @brief Delete the given user.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] name Name of user.
* @param[in] inheritor_name Name of user that inherits user's resources.
*
* @return 0 success, 2 failed to find user, 4 user has active tasks, -1 error.
* -2 database is wrong version, -3 database needs to be initialised
* from server.
*/
int
manage_delete_user (GSList *log_config, const db_conn_info_t *database,
const gchar *name, const gchar *inheritor_name)
{
int ret;
g_info (" Deleting user.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
/* Setup a dummy user, so that delete_user will work. */
current_credentials.uuid = "";
switch ((ret = delete_user (NULL, name, 1, 0, NULL, inheritor_name)))
{
case 0:
printf ("User deleted.\n");
break;
case 2:
fprintf (stderr, "Failed to find user.\n");
break;
case 4:
fprintf (stderr, "User has active tasks.\n");
break;
case 6:
fprintf (stderr, "Inheritor not found.\n");
break;
case 7:
fprintf (stderr, "Inheritor same as deleted user.\n");
break;
case 8:
fprintf (stderr, "Invalid inheritor.\n");
break;
case 9:
fprintf (stderr,
"Resources owned by the user are still in use by others.\n");
break;
case 10:
fprintf (stderr, "User is Feed Import Owner.\n");
break;
default:
fprintf (stderr, "Internal Error.\n");
break;
}
current_credentials.uuid = NULL;
manage_option_cleanup ();
return ret;
}
/**
* @brief List users.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] role_name Role name.
* @param[in] verbose Whether to print UUID.
*
* @return 0 success, -1 error.
*/
int
manage_get_users (GSList *log_config, const db_conn_info_t *database,
const gchar* role_name, int verbose)
{
iterator_t users;
int ret;
g_info (" Getting users.");
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
if (role_name)
{
role_t role;
if (find_role_by_name (role_name, &role))
{
fprintf (stderr, "Internal Error.\n");
manage_option_cleanup ();
return -1;
}
if (role == 0)
{
fprintf (stderr, "Failed to find role.\n");
manage_option_cleanup ();
return -1;
}
init_iterator (&users,
"SELECT name, uuid FROM users"
" WHERE id IN (SELECT \"user\" FROM role_users"
" WHERE role = %llu);",
role);
}
else
init_iterator (&users, "SELECT name, uuid FROM users;");
while (next (&users))
if (verbose)
printf ("%s %s\n", iterator_string (&users, 0), iterator_string (&users, 1));
else
printf ("%s\n", iterator_string (&users, 0));
cleanup_iterator (&users);
manage_option_cleanup ();
return 0;
}
/**
* @brief Set the password of a user.
*
* @param[in] name Name of user.
* @param[in] uuid User UUID.
* @param[in] password New password.
* @param[out] r_errdesc Address to receive a malloced string with the error
* description, or NULL.
*
* @return 0 success, -1 error.
*/
static int
set_password (const gchar *name, const gchar *uuid, const gchar *password,
gchar **r_errdesc)
{
gchar *errstr, *hash;
assert (name && uuid);
if ((errstr = gvm_validate_password (password, name)))
{
g_warning ("new password for '%s' rejected: %s", name, errstr);
if (r_errdesc)
*r_errdesc = errstr;
else
g_free (errstr);
return -1;
}
hash = manage_authentication_hash (password);
sql ("UPDATE users SET password = '%s', modification_time = m_now ()"
" WHERE uuid = '%s';",
hash,
uuid);
g_free (hash);
return 0;
}
/**
* @brief Set the password of a user.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] name Name of user.
* @param[in] password New password.
*
* @return 0 success, -1 error.
*/
int
manage_set_password (GSList *log_config, const db_conn_info_t *database,
const gchar *name, const gchar *password)
{
user_t user;
char *uuid;
int ret;
gchar *rejection_msg;
g_info (" Modifying user password.");
if (name == NULL)
{
fprintf (stderr, "--user required.\n");
return -1;
}
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
sql_begin_immediate ();
if (find_user_by_name (name, &user))
{
fprintf (stderr, "Internal error.\n");
goto fail;
}
if (user == 0)
{
fprintf (stderr, "Failed to find user.\n");
goto fail;
}
uuid = user_uuid (user);
if (uuid == NULL)
{
fprintf (stderr, "Failed to allocate UUID.\n");
goto fail;
}
rejection_msg = NULL;
if (set_password (name, uuid, password, &rejection_msg))
{
if (rejection_msg)
{
fprintf (stderr, "New password rejected: %s\n", rejection_msg);
g_free (rejection_msg);
}
else
fprintf (stderr, "New password rejected.\n");
free (uuid);
goto fail;
}
sql_commit ();
free (uuid);
manage_option_cleanup ();
return ret;
fail:
sql_rollback ();
manage_option_cleanup ();
return -1;
}
/**
* @brief Find a user for a specific permission, given a UUID.
*
* @param[in] uuid UUID of user.
* @param[out] user User return, 0 if successfully failed to find user.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find user), TRUE on error.
*/
static gboolean
find_user_with_permission (const char* uuid, user_t* user,
const char *permission)
{
return find_resource_with_permission ("user", uuid, user, permission, 0);
}
/**
* @brief Find a user given a name.
*
* @param[in] name A user name.
* @param[out] user User return, 0 if successfully failed to find user.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find user), TRUE on error.
*/
gboolean
find_user_by_name_with_permission (const char* name, user_t *user,
const char *permission)
{
return find_resource_by_name_with_permission ("user", name, user, permission);
}
/**
* @brief Find a user given a name.
*
* @param[in] name A user name.
* @param[out] user User return, 0 if successfully failed to find user.
*
* @return FALSE on success (including if failed to find user), TRUE on error.
*/
static gboolean
find_user_by_name (const char* name, user_t *user)
{
return find_resource_by_name ("user", name, user);
}
/**
* @brief Adds a new user to the GVM installation.
*
* @todo Adding users authenticating with certificates is not yet implemented.
*
* @param[in] name The name of the new user.
* @param[in] password The password of the new user.
* @param[in] comment Comment for the new user or NULL.
* @param[in] hosts The host the user is allowed/forbidden to scan.
* @param[in] hosts_allow Whether hosts is allow or forbid.
* @param[in] ifaces Interfaces the user is allowed/forbidden to scan.
* @param[in] ifaces_allow Whether ifaces is allow or forbid.
* @param[in] allowed_methods Allowed login methods.
* @param[in] groups Groups.
* @param[out] group_id_return ID of group on "failed to find" error.
* @param[in] roles Roles.
* @param[out] role_id_return ID of role on "failed to find" error.
* @param[out] r_errdesc If not NULL the address of a variable to receive
* a malloced string with the error description. Will
* always be set to NULL on success.
* @param[out] new_user Created user.
* @param[in] forbid_super_admin Whether to forbid creation of Super Admin.
*
* @return 0 if the user has been added successfully, 1 failed to find group,
* 2 failed to find role, 3 syntax error in hosts, 99 permission denied,
* -1 on error, -2 if user exists already.
*/
int
create_user (const gchar * name, const gchar * password, const gchar *comment,
const gchar * hosts, int hosts_allow,
const gchar *ifaces, int ifaces_allow,
const array_t * allowed_methods, array_t *groups,
gchar **group_id_return, array_t *roles, gchar **role_id_return,
gchar **r_errdesc, user_t *new_user, int forbid_super_admin)
{
char *errstr, *uuid;
gchar *quoted_hosts, *quoted_ifaces, *quoted_method, *quoted_name, *hash;
gchar *quoted_comment, *clean, *generated;
int index, max, ret;
user_t user;
GArray *cache_users;
assert (name);
assert (password);
if (r_errdesc)
*r_errdesc = NULL;
/* allowed_methods is a NULL terminated array. */
if (allowed_methods && (allowed_methods->len > 2))
return -3;
if (allowed_methods && (allowed_methods->len == 0))
allowed_methods = NULL;
// TODO validate methods single source, one of ldap, ...
if (validate_username (name) != 0)
{
g_warning ("Invalid characters in user name!");
if (r_errdesc)
*r_errdesc = g_strdup ("Invalid characters in user name");
return -1;
}
if (allowed_methods &&
(!strcmp (g_ptr_array_index (allowed_methods, 0), "ldap_connect")
|| !strcmp (g_ptr_array_index (allowed_methods, 0), "radius_connect")))
password = generated = gvm_uuid_make ();
else
generated = NULL;
if ((errstr = gvm_validate_password (password, name)))
{
g_warning ("new password for '%s' rejected: %s", name, errstr);
if (r_errdesc)
*r_errdesc = errstr;
else
g_free (errstr);
g_free (generated);
return -1;
}
sql_begin_immediate ();
if (acl_user_may ("create_user") == 0)
{
sql_rollback ();
g_free (generated);
return 99;
}
/* Check if user exists already. */
if (resource_with_name_exists_global (name, "user", 0))
{
sql_rollback ();
g_free (generated);
return -2;
}
quoted_name = sql_quote (name);
/* Check hosts. */
max = manage_max_hosts ();
manage_set_max_hosts (MANAGE_USER_MAX_HOSTS);
if (hosts && (manage_count_hosts (hosts, NULL) < 0))
{
manage_set_max_hosts (max);
sql_rollback ();
g_free (generated);
return 3;
}
manage_set_max_hosts (max);
/* Get the password hashes. */
hash = manage_authentication_hash (password);
/* Get the quoted comment */
if (comment)
quoted_comment = sql_quote (comment);
else
quoted_comment = g_strdup ("");
/* Add the user to the database. */
clean = clean_hosts (hosts ? hosts : "", &max);
quoted_hosts = sql_quote (clean);
quoted_ifaces = sql_quote (ifaces ? ifaces : "");
g_free (clean);
quoted_method = sql_quote (allowed_methods
? g_ptr_array_index (allowed_methods, 0)
: "file");
ret
= sql_error ("INSERT INTO users"
" (uuid, owner, name, password, comment, hosts, hosts_allow,"
" ifaces, ifaces_allow, method, creation_time,"
" modification_time)"
" VALUES"
" (make_uuid (),"
" (SELECT id FROM users WHERE uuid = '%s'),"
" '%s', '%s', '%s', '%s', %i,"
" '%s', %i, '%s', m_now (),"
" m_now ());",
current_credentials.uuid,
quoted_name,
hash,
quoted_comment,
quoted_hosts,
hosts_allow,
quoted_ifaces,
ifaces_allow,
quoted_method);
g_free (generated);
g_free (hash);
g_free (quoted_comment);
g_free (quoted_hosts);
g_free (quoted_ifaces);
g_free (quoted_method);
g_free (quoted_name);
if (ret == 3)
{
sql_rollback ();
return -2;
}
else if (ret)
{
sql_rollback ();
return -1;
}
user = sql_last_insert_id ();
/* Add the user to any given groups. */
index = 0;
while (groups && (index < groups->len))
{
gchar *group_id;
group_t group;
group_id = (gchar*) g_ptr_array_index (groups, index);
if (strcmp (group_id, "0") == 0)
{
index++;
continue;
}
if (find_group_with_permission (group_id, &group, "modify_group"))
{
sql_rollback ();
return -1;
}
if (group == 0)
{
sql_rollback ();
if (group_id_return) *group_id_return = group_id;
return 1;
}
sql ("INSERT INTO group_users (\"group\", \"user\") VALUES (%llu, %llu);",
group,
user);
index++;
}
/* Add the user to any given roles. */
index = 0;
while (roles && (index < roles->len))
{
gchar *role_id;
role_t role;
role_id = (gchar*) g_ptr_array_index (roles, index);
if (strcmp (role_id, "0") == 0)
{
index++;
continue;
}
if (forbid_super_admin && acl_role_can_super_everyone (role_id))
{
sql_rollback ();
return 99;
}
if (find_role_with_permission (role_id, &role, "get_roles"))
{
sql_rollback ();
return -1;
}
if (role == 0)
{
sql_rollback ();
if (role_id_return) *role_id_return = role_id;
return 2;
}
sql ("INSERT INTO role_users (role, \"user\") VALUES (%llu, %llu);",
role,
user);
index++;
}
if (new_user)
*new_user = user;
/* Ensure the user can see themself. */
uuid = user_uuid (user);
if (uuid == NULL)
{
g_warning ("%s: Failed to allocate UUID", __func__);
sql_rollback ();
return -1;
}
create_permission_internal (1,
"GET_USERS",
"Automatically created when adding user",
NULL,
uuid,
"user",
uuid,
NULL);
free (uuid);
/* Cache permissions. */
cache_users = g_array_new (TRUE, TRUE, sizeof (user_t));
g_array_append_val (cache_users, user);
cache_all_permissions_for_users (cache_users);
g_free (g_array_free (cache_users, TRUE));
sql_commit ();
return 0;
}
/**
* @brief Create a user from an existing user.
*
* @param[in] name Name of new user. NULL to copy from existing.
* @param[in] comment Comment on new user. NULL to copy from existing.
* @param[in] user_id UUID of existing user.
* @param[out] new_user New user.
*
* @return 0 success, 1 user exists already, 2 failed to find existing
* user, 99 permission denied, -1 error.
*/
int
copy_user (const char* name, const char* comment, const char *user_id,
user_t* new_user)
{
user_t user;
int ret;
gchar *quoted_uuid;
GArray *cache_users;
char *uuid;
if (acl_user_can_super_everyone (user_id))
return 99;
sql_begin_immediate ();
ret = copy_resource_lock ("user", name, comment, user_id,
"password, timezone, hosts, hosts_allow,"
" ifaces, ifaces_allow, method",
1, &user, NULL);
if (ret)
{
sql_rollback ();
return ret;
}
sql ("UPDATE users SET password = NULL WHERE id = %llu;", user);
quoted_uuid = sql_quote (user_id);
sql ("INSERT INTO group_users (\"user\", \"group\")"
" SELECT %llu, \"group\" FROM group_users"
" WHERE \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
user,
quoted_uuid);
sql ("INSERT INTO role_users (\"user\", role)"
" SELECT %llu, role FROM role_users"
" WHERE \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
user,
quoted_uuid);
g_free (quoted_uuid);
/* Ensure the user can see themself. */
uuid = user_uuid (user);
if (uuid == NULL)
{
g_warning ("%s: Failed to allocate UUID", __func__);
sql_rollback ();
return -1;
}
create_permission_internal (1,
"GET_USERS",
"Automatically created when adding user",
NULL,
uuid,
"user",
uuid,
NULL);
free (uuid);
/* Cache permissions. */
cache_users = g_array_new (TRUE, TRUE, sizeof (user_t));
g_array_append_val (cache_users, user);
cache_all_permissions_for_users (cache_users);
g_free (g_array_free (cache_users, TRUE));
sql_commit ();
if (new_user)
*new_user = user;
return ret;
}
/**
* @brief Delete a user.
*
* @param[in] user_id_arg UUID of user.
* @param[in] name_arg Name of user. Overridden by user_id.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
* @param[in] forbid_super_admin Whether to forbid removal of Super Admin.
* @param[in] inheritor_id UUID of user who will inherit owned objects.
* @param[in] inheritor_name Name of user who will inherit owned objects.
*
* @return 0 success, 2 failed to find user, 4 user has active tasks,
* 5 attempted suicide, 6 inheritor not found, 7 inheritor same as
* deleted user, 8 invalid inheritor, 9 resources still in use,
* 10 user is 'Feed Import Owner' 99 permission denied, -1 error.
*/
int
delete_user (const char *user_id_arg, const char *name_arg, int ultimate,
int forbid_super_admin,
const char* inheritor_id, const char *inheritor_name)
{
iterator_t tasks;
user_t user, inheritor;
get_data_t get;
char *current_uuid, *feed_owner_id;
gboolean has_rows;
iterator_t rows;
gchar *deleted_user_id;
assert (user_id_arg || name_arg);
if (current_credentials.username && current_credentials.uuid)
{
if (user_id_arg)
{
if (strcmp (user_id_arg, current_credentials.uuid) == 0)
return 5;
}
else if (name_arg
&& (strcmp (name_arg, current_credentials.username) == 0))
return 5;
}
sql_begin_immediate ();
if (acl_user_may ("delete_user") == 0)
{
sql_rollback ();
return 99;
}
user = 0;
if (user_id_arg)
{
if (forbid_super_admin
&& (strcmp (user_id_arg, ROLE_UUID_SUPER_ADMIN) == 0))
{
sql_rollback ();
return 99;
}
if (find_user_with_permission (user_id_arg, &user, "delete_user"))
{
sql_rollback ();
return -1;
}
}
else if (find_user_by_name_with_permission (name_arg, &user, "delete_user"))
{
sql_rollback ();
return -1;
}
if (user == 0)
return 2;
setting_value (SETTING_UUID_FEED_IMPORT_OWNER, &feed_owner_id);
if (feed_owner_id)
{
char *uuid;
uuid = user_uuid (user);
if (strcmp (uuid, feed_owner_id) == 0)
{
free (uuid);
free (feed_owner_id);
sql_rollback ();
return 10;
}
free (feed_owner_id);
free (uuid);
}
if (forbid_super_admin)
{
char *uuid;
uuid = user_uuid (user);
if (acl_user_is_super_admin (uuid))
{
free (uuid);
sql_rollback ();
return 99;
}
free (uuid);
}
/* Fail if there are any active tasks. */
memset (&get, '\0', sizeof (get));
current_uuid = current_credentials.uuid;
current_credentials.uuid = sql_string ("SELECT uuid FROM users"
" WHERE id = %llu;",
user);
init_user_task_iterator (&tasks, 0, 1);
while (next (&tasks))
switch (task_iterator_run_status (&tasks))
{
case TASK_STATUS_DELETE_REQUESTED:
case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
case TASK_STATUS_DELETE_ULTIMATE_WAITING:
case TASK_STATUS_DELETE_WAITING:
case TASK_STATUS_REQUESTED:
case TASK_STATUS_RUNNING:
case TASK_STATUS_QUEUED:
case TASK_STATUS_STOP_REQUESTED:
case TASK_STATUS_STOP_WAITING:
{
cleanup_iterator (&tasks);
free (current_credentials.uuid);
current_credentials.uuid = current_uuid;
sql_rollback ();
return 4;
}
default:
break;
}
cleanup_iterator (&tasks);
free (current_credentials.uuid);
current_credentials.uuid = current_uuid;
/* Check if there's an inheritor. */
if (inheritor_id && strcmp (inheritor_id, ""))
{
if (strcmp (inheritor_id, "self") == 0)
{
sql_int64 (&inheritor, "SELECT id FROM users WHERE uuid = '%s'",
current_credentials.uuid);
if (inheritor == 0)
{
sql_rollback ();
return -1;
}
}
else
{
if (find_user_with_permission (inheritor_id, &inheritor, "get_users"))
{
sql_rollback ();
return -1;
}
if (inheritor == 0)
{
sql_rollback ();
return 6;
}
}
}
else if (inheritor_name && strcmp (inheritor_name, ""))
{
if (find_user_by_name_with_permission (inheritor_name, &inheritor,
"get_users"))
{
sql_rollback ();
return -1;
}
if (inheritor == 0)
{
sql_rollback ();
return 6;
}
}
else
inheritor = 0;
if (inheritor)
{
gchar *deleted_user_name;
gchar *real_inheritor_id, *real_inheritor_name;
/* Transfer ownership of objects to the inheritor. */
if (inheritor == user)
{
sql_rollback ();
return 7;
}
real_inheritor_id = user_uuid (inheritor);
/* Only the current user, owned users or global users may inherit. */
if (strcmp (real_inheritor_id, current_credentials.uuid)
&& sql_int ("SELECT NOT (" ACL_IS_GLOBAL () ")"
" FROM users WHERE id = %llu",
inheritor)
&& ! acl_user_owns ("user", inheritor, 0)
&& sql_int ("SELECT owner != 0 FROM users WHERE id = %llu",
inheritor))
{
g_free (real_inheritor_id);
sql_rollback ();
return 8;
}
deleted_user_id = user_uuid (user);
deleted_user_name = user_name (deleted_user_id);
real_inheritor_name = user_name (real_inheritor_id);
g_log ("event user", G_LOG_LEVEL_MESSAGE,
"User %s (%s) is inheriting from %s (%s)",
real_inheritor_name, real_inheritor_id,
deleted_user_name, deleted_user_id);
g_free (deleted_user_name);
g_free (real_inheritor_id);
g_free (real_inheritor_name);
/* Transfer owned resources. */
sql ("UPDATE alerts SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE alerts_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE configs SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE configs_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE credentials SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE credentials_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE host_identifiers SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE host_oss SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE hosts SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE filters SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE filters_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE notes SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE notes_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE oss SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE permissions SET owner = %llu WHERE owner = %llu",
inheritor, user);
inherit_port_lists (user, inheritor);
sql ("UPDATE reports SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE report_counts SET \"user\" = %llu WHERE \"user\" = %llu",
inheritor, user);
sql ("UPDATE reports SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE results SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE results_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE overrides SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE overrides_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE permissions SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE permissions_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE scanners SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE scanners_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE schedules SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE schedules_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("DELETE FROM tag_resources"
" WHERE resource_type = 'user' AND resource = %llu;",
user);
sql ("UPDATE tags SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("DELETE FROM tag_resources_trash"
" WHERE resource_type = 'user' AND resource = %llu;",
user);
sql ("UPDATE tags_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE targets SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE targets_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE tasks SET owner = %llu WHERE owner = %llu;",
inheritor, user);
inherit_tickets (user, inheritor);
inherit_tls_certificates (user, inheritor);
sql ("UPDATE groups SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE roles SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE users SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE groups_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
sql ("UPDATE roles_trash SET owner = %llu WHERE owner = %llu;",
inheritor, user);
/* Report Formats. */
has_rows = inherit_report_formats (user, inheritor, &rows);
/* Delete user. */
sql ("DELETE FROM group_users WHERE \"user\" = %llu;", user);
sql ("DELETE FROM group_users_trash WHERE \"user\" = %llu;", user);
sql ("DELETE FROM role_users WHERE \"user\" = %llu;", user);
sql ("DELETE FROM role_users_trash WHERE \"user\" = %llu;", user);
delete_permissions_cache_for_user (user);
sql ("DELETE FROM settings WHERE owner = %llu;", user);
sql ("DELETE FROM users WHERE id = %llu;", user);
/* Very last: report formats dirs. */
if (deleted_user_id == NULL)
g_warning ("%s: deleted_user_id NULL, skipping dirs", __func__);
else if (has_rows)
do
{
inherit_report_format_dir (iterator_string (&rows, 0),
deleted_user_id,
inheritor);
} while (next (&rows));
g_free (deleted_user_id);
cleanup_iterator (&rows);
sql_commit ();
return 0;
}
/* Delete settings and miscellaneous resources not referenced directly. */
/* Settings. */
sql ("DELETE FROM settings WHERE owner = %llu;", user);
/* Delete data modifiers (not directly referenced) */
/* Notes. */
sql ("DELETE FROM notes WHERE owner = %llu;", user);
sql ("DELETE FROM notes_trash WHERE owner = %llu;", user);
/* Overrides. */
sql ("DELETE FROM overrides WHERE owner = %llu;", user);
sql ("DELETE FROM overrides_trash WHERE owner = %llu;", user);
/* Tags. */
sql ("DELETE FROM tag_resources"
" WHERE resource_type = 'user' AND resource = %llu;",
user);
sql ("DELETE FROM tag_resources"
" WHERE tag IN (SELECT id FROM tags WHERE owner = %llu);",
user);
sql ("DELETE FROM tags WHERE owner = %llu;", user);
sql ("DELETE FROM tag_resources_trash"
" WHERE resource_type = 'user' AND resource = %llu;",
user);
sql ("DELETE FROM tag_resources_trash"
" WHERE tag IN (SELECT id FROM tags_trash WHERE owner = %llu);",
user);
sql ("DELETE FROM tags_trash WHERE owner = %llu;", user);
delete_tickets_user (user);
delete_tls_certificates_user (user);
/* Delete assets (not directly referenced). */
/* Hosts. */
sql ("DELETE FROM host_details WHERE host IN"
" (SELECT id FROM hosts WHERE owner = %llu);", user);
sql ("DELETE FROM host_max_severities WHERE host IN"
" (SELECT id FROM hosts WHERE owner = %llu);", user);
sql ("DELETE FROM host_identifiers WHERE owner = %llu;", user);
sql ("DELETE FROM host_oss WHERE owner = %llu;", user);
sql ("DELETE FROM hosts WHERE owner = %llu;", user);
/* OSs. */
sql ("DELETE FROM oss WHERE owner = %llu;", user);
/* Delete report data and tasks (not directly referenced). */
/* Counts. */
sql ("DELETE FROM report_counts WHERE \"user\" = %llu", user);
sql ("DELETE FROM report_counts"
" WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
user);
/* Hosts. */
sql ("DELETE FROM report_host_details"
" WHERE report_host IN (SELECT id FROM report_hosts"
" WHERE report IN (SELECT id FROM reports"
" WHERE owner = %llu));",
user);
sql ("DELETE FROM report_hosts"
" WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
user);
/* Results. */
sql ("DELETE FROM results"
" WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
user);
sql ("DELETE FROM results_trash"
" WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
user);
/* Reports. */
sql ("DELETE FROM result_nvt_reports"
" WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
user);
sql ("DELETE FROM reports WHERE owner = %llu;", user);
/* Delete tasks (not directly referenced). */
if (user_resources_in_use (user,
"tasks", target_in_use,
NULL, NULL))
{
sql_rollback ();
return 9;
}
tickets_remove_tasks_user (user);
sql ("DELETE FROM task_alerts"
" WHERE task IN (SELECT id FROM tasks WHERE owner = %llu);",
user);
sql ("DELETE FROM task_files"
" WHERE task IN (SELECT id FROM tasks WHERE owner = %llu);",
user);
sql ("DELETE FROM task_preferences"
" WHERE task IN (SELECT id FROM tasks WHERE owner = %llu);",
user);
sql ("DELETE FROM tasks WHERE owner = %llu;", user);
/* Delete resources directly used by tasks. */
/* Alerts. */
if (user_resources_in_use (user,
"alerts", alert_in_use,
"alerts_trash", trash_alert_in_use))
{
sql_rollback ();
return 9;
}
sql ("DELETE FROM alert_condition_data"
" WHERE alert IN (SELECT id FROM alerts WHERE owner = %llu);",
user);
sql ("DELETE FROM alert_condition_data_trash"
" WHERE alert IN (SELECT id FROM alerts_trash WHERE owner = %llu);",
user);
sql ("DELETE FROM alert_event_data"
" WHERE alert IN (SELECT id FROM alerts WHERE owner = %llu);",
user);
sql ("DELETE FROM alert_event_data_trash"
" WHERE alert IN (SELECT id FROM alerts_trash WHERE owner = %llu);",
user);
sql ("DELETE FROM alert_method_data"
" WHERE alert IN (SELECT id FROM alerts WHERE owner = %llu);",
user);
sql ("DELETE FROM alert_method_data_trash"
" WHERE alert IN (SELECT id FROM alerts_trash WHERE owner = %llu);",
user);
sql ("DELETE FROM alerts WHERE owner = %llu;", user);
sql ("DELETE FROM alerts_trash WHERE owner = %llu;", user);
/* Configs. */
if (user_resources_in_use (user,
"configs", config_in_use,
"configs_trash", trash_config_in_use))
{
sql_rollback ();
return 9;
}
sql ("DELETE FROM nvt_selectors"
" WHERE name IN (SELECT nvt_selector FROM configs WHERE owner = %llu)"
" AND name != '" MANAGE_NVT_SELECTOR_UUID_ALL "';",
user);
sql ("DELETE FROM config_preferences"
" WHERE config IN (SELECT id FROM configs WHERE owner = %llu);",
user);
sql ("DELETE FROM config_preferences_trash"
" WHERE config IN (SELECT id FROM configs_trash WHERE owner = %llu);",
user);
sql ("DELETE FROM configs WHERE owner = %llu;", user);
sql ("DELETE FROM configs_trash WHERE owner = %llu;", user);
/* Scanners. */
if (user_resources_in_use (user,
"scanners", scanner_in_use,
"scanners_trash", trash_scanner_in_use))
{
sql_rollback ();
return 9;
}
sql ("DELETE FROM scanners WHERE owner = %llu;", user);
sql ("DELETE FROM scanners_trash WHERE owner = %llu;", user);
/* Schedules. */
if (user_resources_in_use (user,
"schedules", schedule_in_use,
"schedules_trash", trash_schedule_in_use))
{
sql_rollback ();
return 9;
}
sql ("DELETE FROM schedules WHERE owner = %llu;", user);
sql ("DELETE FROM schedules_trash WHERE owner = %llu;", user);
/* Targets. */
if (user_resources_in_use (user,
"targets", target_in_use,
"targets_trash", trash_target_in_use))
{
sql_rollback ();
return 9;
}
sql ("DELETE FROM targets_login_data WHERE target IN"
" (SELECT id FROM targets WHERE owner = %llu);", user);
sql ("DELETE FROM targets_trash_login_data WHERE target IN"
" (SELECT id FROM targets_trash WHERE owner = %llu);", user);
sql ("DELETE FROM targets WHERE owner = %llu;", user);
sql ("DELETE FROM targets_trash WHERE owner = %llu;", user);
/* Delete resources used indirectly by tasks */
/* Filters (used by alerts and settings). */
if (user_resources_in_use (user,
"filters", filter_in_use,
"filters_trash", trash_filter_in_use))
{
sql_rollback ();
return 9;
}
sql ("DELETE FROM filters WHERE owner = %llu;", user);
sql ("DELETE FROM filters_trash WHERE owner = %llu;", user);
/* Port lists (used by targets). */
if (user_resources_in_use (user,
"port_lists", port_list_in_use,
"port_lists_trash", trash_port_list_in_use))
{
sql_rollback ();
return 9;
}
delete_port_lists_user (user);
/* Check credentials before deleting report formats, because we can't
* rollback the deletion of the report format dirs. */
if (user_resources_in_use (user,
"credentials", credential_in_use,
"credentials_trash", trash_credential_in_use))
{
sql_rollback ();
return 9;
}
/* Check report formats (used by alerts). */
if (user_resources_in_use (user,
"report_formats",
report_format_in_use,
"report_formats_trash",
trash_report_format_in_use))
{
sql_rollback ();
return 9;
}
/* Delete credentials last because they can be used in various places */
sql ("DELETE FROM credentials_data WHERE credential IN"
" (SELECT id FROM credentials WHERE owner = %llu);",
user);
sql ("DELETE FROM credentials_trash_data WHERE credential IN"
" (SELECT id FROM credentials_trash WHERE owner = %llu);",
user);
sql ("DELETE FROM credentials WHERE owner = %llu;", user);
sql ("DELETE FROM credentials_trash WHERE owner = %llu;", user);
/* Make permissions global if they are owned by the user and are related
* to users/groups/roles that are owned by the user. */
sql ("UPDATE permissions SET owner = NULL"
" WHERE owner = %llu"
" AND ((subject_type = 'user' AND subject IN (SELECT id FROM users WHERE owner = %llu))"
" OR (subject_type = 'group' AND subject IN (SELECT id FROM groups WHERE owner = %llu))"
" OR (subject_type = 'role' AND subject IN (SELECT id FROM roles WHERE owner = %llu))"
" OR (resource_type = 'user' AND resource IN (SELECT id FROM users WHERE owner = %llu))"
" OR (resource_type = 'group' AND resource IN (SELECT id FROM groups WHERE owner = %llu))"
" OR (resource_type = 'role' AND resource IN (SELECT id FROM roles WHERE owner = %llu)));",
user,
user,
user,
user,
user,
user,
user);
/* Make users, roles and groups global if they are owned by the user. */
sql ("UPDATE users SET owner = NULL WHERE owner = %llu;", user);
sql ("UPDATE roles SET owner = NULL WHERE owner = %llu;", user);
sql ("UPDATE groups SET owner = NULL WHERE owner = %llu;", user);
sql ("UPDATE roles_trash SET owner = NULL WHERE owner = %llu;", user);
sql ("UPDATE groups_trash SET owner = NULL WHERE owner = %llu;", user);
/* Remove all other permissions owned by the user or given on the user. */
sql ("DELETE FROM permissions"
" WHERE owner = %llu"
" OR subject_type = 'user' AND subject = %llu"
" OR (resource_type = 'user' AND resource = %llu);", /* For Super. */
user,
user,
user);
sql ("DELETE FROM permissions_get_tasks WHERE \"user\" = %llu;", user);
/* Delete permissions granted by the user. */
sql ("DELETE FROM permissions WHERE owner = %llu;", user);
sql ("DELETE FROM permissions_trash WHERE owner = %llu;", user);
/* Remove user from groups and roles. */
sql ("DELETE FROM group_users WHERE \"user\" = %llu;", user);
sql ("DELETE FROM group_users_trash WHERE \"user\" = %llu;", user);
sql ("DELETE FROM role_users WHERE \"user\" = %llu;", user);
sql ("DELETE FROM role_users_trash WHERE \"user\" = %llu;", user);
/* Delete report formats. */
has_rows = delete_report_formats_user (user, &rows);
/* Delete user. */
deleted_user_id = user_uuid (user);
sql ("DELETE FROM users WHERE id = %llu;", user);
/* Delete report format dirs. */
if (deleted_user_id)
delete_report_format_dirs_user (deleted_user_id, has_rows ? &rows : NULL);
else
g_warning ("%s: deleted_user_id NULL, skipping removal of report formats dir",
__func__);
sql_commit ();
return 0;
}
/**
* @brief Modify a user.
*
* @param[in] user_id The UUID of the user. Overrides name.
* @param[in] name The name of the user. If NULL then set to name
* when return is 3 or 4.
* @param[in] new_name New name for the user. NULL to leave as is.
* @param[in] password The password of the user. NULL to leave as is.
* @param[in] comment The comment for the user. NULL to leave as is.
* @param[in] hosts The host the user is allowed/forbidden to scan.
* NULL to leave as is.
* @param[in] hosts_allow Whether hosts is allow or forbid.
* @param[in] ifaces Interfaces the user is allowed/forbidden to scan.
* @param[in] ifaces_allow Whether ifaces is allow or forbid.
* @param[in] allowed_methods Allowed login methods.
* @param[in] groups Groups.
* @param[out] group_id_return ID of group on "failed to find" error.
* @param[in] roles Roles.
* @param[out] role_id_return ID of role on "failed to find" error.
* @param[out] r_errdesc If not NULL the address of a variable to receive
* a malloced string with the error description. Will
* always be set to NULL on success.
*
* @return 0 if the user has been added successfully, 1 failed to find group,
* 2 failed to find user, 3 success and user gained admin, 4 success
* and user lost admin, 5 failed to find role, 6 syntax error in hosts,
* 7 syntax error in new name, 99 permission denied, -1 on error,
* -2 for an unknown role, -3 if wrong number of methods.
*/
int
modify_user (const gchar * user_id, gchar **name, const gchar *new_name,
const gchar * password, const gchar * comment,
const gchar * hosts, int hosts_allow,
const gchar *ifaces, int ifaces_allow,
const array_t * allowed_methods, array_t *groups,
gchar **group_id_return, array_t *roles, gchar **role_id_return,
gchar **r_errdesc)
{
char *errstr;
gchar *hash, *quoted_hosts, *quoted_ifaces, *quoted_method, *clean, *uuid;
gchar *quoted_new_name, *quoted_comment;
user_t user;
int max, was_admin, is_admin;
GArray *cache_users;
if (r_errdesc)
*r_errdesc = NULL;
/* allowed_methods is a NULL terminated array. */
if (allowed_methods && (allowed_methods->len > 2))
return -3;
if (allowed_methods && (allowed_methods->len == 0))
allowed_methods = NULL;
if (allowed_methods
&& ((g_ptr_array_index (allowed_methods, 0) == NULL)
|| (strlen (g_ptr_array_index (allowed_methods, 0)) == 0)))
allowed_methods = NULL;
// TODO Validate methods: single source, one of "", "ldap", ...
sql_begin_immediate ();
if (acl_user_may ("modify_user") == 0)
{
sql_rollback ();
return 99;
}
user = 0;
if (user_id)
{
if (find_user_with_permission (user_id, &user, "modify_user"))
{
sql_rollback ();
return -1;
}
}
else if (find_user_by_name_with_permission (*name, &user, "modify_user"))
{
sql_rollback ();
return -1;
}
if (user == 0)
{
sql_rollback ();
return 2;
}
uuid = sql_string ("SELECT uuid FROM users WHERE id = %llu",
user);
/* The only user that can edit a Super Admin is the Super Admin themself. */
if (acl_user_can_super_everyone (uuid) && strcmp (uuid, current_credentials.uuid))
{
g_free (uuid);
sql_rollback ();
return 99;
}
was_admin = acl_user_is_admin (uuid);
if (password)
{
char *user_name;
user_name = sql_string ("SELECT name FROM users WHERE id = %llu",
user);
errstr = gvm_validate_password (password, user_name);
if (errstr)
{
g_warning ("new password for '%s' rejected: %s", user_name, errstr);
if (r_errdesc)
*r_errdesc = errstr;
else
g_free (errstr);
sql_rollback ();
g_free (user_name);
return -1;
}
g_free (user_name);
}
/* Check hosts. */
max = manage_max_hosts ();
manage_set_max_hosts (MANAGE_USER_MAX_HOSTS);
if (hosts && (manage_count_hosts (hosts, NULL) < 0))
{
manage_set_max_hosts (max);
sql_rollback ();
return 6;
}
manage_set_max_hosts (max);
/* Check new name. */
if (new_name)
{
if (validate_username (new_name) != 0)
{
sql_rollback ();
return 7;
}
if (strcmp (uuid, current_credentials.uuid) == 0)
{
sql_rollback ();
return 99;
}
if (resource_with_name_exists_global (new_name, "user", user))
{
sql_rollback ();
return 8;
}
quoted_new_name = sql_quote (new_name);
}
else
quoted_new_name = NULL;
/* Get the password hashes. */
if (password)
hash = manage_authentication_hash (password);
else
hash = NULL;
if (comment)
{
quoted_comment = sql_quote (comment);
}
else
{
quoted_comment = NULL;
}
/* Update the user in the database. */
clean = clean_hosts (hosts ? hosts : "", &max);
if ((hosts_allow == 0) && (max == 0))
/* Convert "Deny none" to "Allow All". */
hosts_allow = 2;
quoted_ifaces = sql_quote (ifaces ? ifaces : "");
quoted_hosts = sql_quote (clean);
g_free (clean);
quoted_method = sql_quote (allowed_methods
? g_ptr_array_index (allowed_methods, 0)
: "");
sql ("UPDATE users"
" SET name = %s%s%s,"
" comment = %s%s%s,"
" hosts = '%s',"
" hosts_allow = '%i',"
" ifaces = '%s',"
" ifaces_allow = %i,"
" method = %s%s%s,"
" modification_time = m_now ()"
" WHERE id = %llu;",
quoted_new_name ? "'" : "",
quoted_new_name ? quoted_new_name : "name",
quoted_new_name ? "'" : "",
quoted_comment ? "'" : "",
quoted_comment ? quoted_comment : "comment",
quoted_comment ? "'" : "",
quoted_hosts,
hosts_allow,
quoted_ifaces,
ifaces_allow,
allowed_methods ? "'" : "",
allowed_methods ? quoted_method : "method",
allowed_methods ? "'" : "",
user);
g_free (quoted_new_name);
g_free (quoted_hosts);
g_free (quoted_ifaces);
g_free (quoted_method);
if (hash)
sql ("UPDATE users"
" SET password = '%s'"
" WHERE id = %llu;",
hash,
user);
g_free (hash);
/* Update the user groups. */
if (groups)
{
int index;
sql ("DELETE FROM group_users WHERE \"user\" = %llu;", user);
index = 0;
while (groups && (index < groups->len))
{
gchar *group_id;
group_t group;
group_id = (gchar*) g_ptr_array_index (groups, index);
if (strcmp (group_id, "0") == 0)
{
index++;
continue;
}
if (find_group_with_permission (group_id, &group, "modify_group"))
{
sql_rollback ();
return -1;
}
if (group == 0)
{
sql_rollback ();
if (group_id_return) *group_id_return = group_id;
return 1;
}
sql ("INSERT INTO group_users (\"group\", \"user\")"
" VALUES (%llu, %llu);",
group,
user);
index++;
}
}
/* Update the user roles. */
if (roles)
{
int index;
sql ("DELETE FROM role_users"
" WHERE \"user\" = %llu"
" AND role != (SELECT id from roles"
" WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "');",
user);
index = 0;
while (roles && (index < roles->len))
{
gchar *role_id;
role_t role;
role_id = (gchar*) g_ptr_array_index (roles, index);
if (strcmp (role_id, "0") == 0)
{
index++;
continue;
}
if (find_role_with_permission (role_id, &role, "get_roles"))
{
sql_rollback ();
return -1;
}
if (role == 0)
{
sql_rollback ();
if (role_id_return) *role_id_return = role_id;
return 1;
}
if (acl_role_can_super_everyone (role_id))
{
sql_rollback ();
return 99;
}
sql ("INSERT INTO role_users (role, \"user\") VALUES (%llu, %llu);",
role,
user);
index++;
}
}
cache_users = g_array_new (TRUE, TRUE, sizeof (user_t));
g_array_append_val (cache_users, user);
cache_all_permissions_for_users (cache_users);
g_free (g_array_free (cache_users, TRUE));
sql_commit ();
if (was_admin)
{
is_admin = acl_user_is_admin (uuid);
g_free (uuid);
if (is_admin)
return 0;
if (*name == NULL)
*name = sql_string ("SELECT name FROM users WHERE id = %llu",
user);
return 4;
}
is_admin = acl_user_is_admin (uuid);
g_free (uuid);
if (is_admin)
{
if (*name == NULL)
*name = sql_string ("SELECT name FROM users WHERE id = %llu",
user);
return 3;
}
return 0;
}
/**
* @brief Return the name of a user.
*
* @param[in] uuid UUID of user.
*
* @return Newly allocated name if available, else NULL.
*/
gchar*
user_name (const char *uuid)
{
gchar *name, *quoted_uuid;
quoted_uuid = sql_quote (uuid);
name = sql_string ("SELECT name FROM users WHERE uuid = '%s';",
quoted_uuid);
g_free (quoted_uuid);
return name;
}
/**
* @brief Return the UUID of a user.
*
* Warning: this is only safe for users that are known to be in the db.
*
* @param[in] user User.
*
* @return Newly allocated UUID if available, else NULL.
*/
char*
user_uuid (user_t user)
{
return sql_string ("SELECT uuid FROM users WHERE id = %llu;",
user);
}
/**
* @brief Check whether a user is in use.
*
* @param[in] user User.
*
* @return 1 yes, 0 no.
*/
int
user_in_use (user_t user)
{
return 0;
}
/**
* @brief Check whether a trashcan user is in use.
*
* @param[in] user User.
*
* @return 1 yes, 0 no.
*/
int
trash_user_in_use (user_t user)
{
return 0;
}
/**
* @brief Check whether a user is writable.
*
* @param[in] user User.
*
* @return 1 yes, 0 no.
*/
int
user_writable (user_t user)
{
return 1;
}
/**
* @brief Check whether a trashcan user is writable.
*
* @param[in] user User.
*
* @return 1 yes, 0 no.
*/
int
trash_user_writable (user_t user)
{
return 1;
}
/**
* @brief Return the ifaces of a user.
*
* @param[in] uuid UUID of user.
*
* @return Newly allocated ifaces value if available, else NULL.
*/
gchar*
user_ifaces (const char *uuid)
{
gchar *name, *quoted_uuid;
quoted_uuid = sql_quote (uuid);
name = sql_string ("SELECT ifaces FROM users WHERE uuid = '%s';",
quoted_uuid);
g_free (quoted_uuid);
return name;
}
/**
* @brief Return whether ifaces value of a user denotes allowed.
*
* @param[in] uuid UUID of user.
*
* @return 1 if allow, else 0.
*/
int
user_ifaces_allow (const char *uuid)
{
gchar *quoted_uuid;
int allow;
quoted_uuid = sql_quote (uuid);
allow = sql_int ("SELECT ifaces_allow FROM users WHERE uuid = '%s';",
quoted_uuid);
g_free (quoted_uuid);
return allow;
}
/**
* @brief Return the hosts of a user.
*
* @param[in] uuid UUID of user.
*
* @return Newly allocated hosts value if available, else NULL.
*/
gchar*
user_hosts (const char *uuid)
{
gchar *name, *quoted_uuid;
quoted_uuid = sql_quote (uuid);
name = sql_string ("SELECT hosts FROM users WHERE uuid = '%s';",
quoted_uuid);
g_free (quoted_uuid);
return name;
}
/**
* @brief Return whether hosts value of a user denotes allowed.
*
* @param[in] uuid UUID of user.
*
* @return 1 if allow, else 0.
*/
int
user_hosts_allow (const char *uuid)
{
gchar *quoted_uuid;
int allow;
quoted_uuid = sql_quote (uuid);
allow = sql_int ("SELECT hosts_allow FROM users WHERE uuid = '%s';",
quoted_uuid);
g_free (quoted_uuid);
return allow;
}
/**
* @brief User columns for user iterator.
*/
#define USER_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "method", "roles", "groups", "hosts", \
"ifaces", NULL }
/**
* @brief User iterator columns.
*/
#define USER_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (users), \
{ "method", NULL, KEYWORD_TYPE_STRING }, \
{ "hosts", NULL, KEYWORD_TYPE_STRING }, \
{ "hosts_allow", NULL, KEYWORD_TYPE_INTEGER }, \
{ \
"coalesce ((SELECT group_concat (name, ', ')" \
" FROM (SELECT DISTINCT name, order_role (name)" \
" FROM roles, role_users" \
" WHERE role_users.role = roles.id" \
" AND \"user\" = users.id" \
" ORDER BY order_role (roles.name) ASC)" \
" AS user_iterator_sub)," \
" '')", \
"roles", \
KEYWORD_TYPE_STRING \
}, \
{ \
"coalesce ((SELECT group_concat (name, ', ')" \
" FROM (SELECT DISTINCT name FROM groups, group_users" \
" WHERE group_users.\"group\" = groups.id" \
" AND \"user\" = users.id" \
" ORDER BY groups.name ASC)" \
" AS user_iterator_sub)," \
" '')", \
"groups", \
KEYWORD_TYPE_STRING \
}, \
{ "ifaces", NULL, KEYWORD_TYPE_STRING }, \
{ "ifaces_allow", NULL, KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief User iterator columns for trash case.
*/
#define USER_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (users_trash), \
{ "method", NULL, KEYWORD_TYPE_STRING }, \
{ "hosts", NULL, KEYWORD_TYPE_STRING }, \
{ "hosts_allow", NULL, KEYWORD_TYPE_INTEGER }, \
{ "ifaces", NULL, KEYWORD_TYPE_STRING }, \
{ "ifaces_allow", NULL, KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Count number of users.
*
* @param[in] get GET params.
*
* @return Total number of users in usered set.
*/
int
user_count (const get_data_t *get)
{
static const char *filter_columns[] = USER_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = USER_ITERATOR_COLUMNS;
return count ("user", get, columns, NULL, filter_columns,
0, 0, 0, TRUE);
}
/**
* @brief Initialise a user iterator, including observed users.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find user, 2 failed to find user (filt_id),
* -1 error.
*/
int
init_user_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = USER_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = USER_ITERATOR_COLUMNS;
static column_t trash_columns[] = USER_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"user",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Get the method of the user from a user iterator.
*
* @param[in] iterator Iterator.
*
* @return Method of the user or NULL if iteration is complete.
*/
DEF_ACCESS (user_iterator_method, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get the hosts from a user iterator.
*
* @param[in] iterator Iterator.
*
* @return Hosts or NULL if iteration is complete.
*/
DEF_ACCESS (user_iterator_hosts, GET_ITERATOR_COLUMN_COUNT + 1);
/**
* @brief Get the hosts allow value from a user iterator.
*
* @param[in] iterator Iterator.
*
* @return Hosts allow.
*/
int
user_iterator_hosts_allow (iterator_t* iterator)
{
if (iterator->done) return -1;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
}
/**
* @brief Get the ifaces from a user iterator.
*
* @param[in] iterator Iterator.
*
* @return Interfaces or NULL if iteration is complete.
*/
DEF_ACCESS (user_iterator_ifaces, GET_ITERATOR_COLUMN_COUNT + 5);
/**
* @brief Get the ifaces allow value from a user iterator.
*
* @param[in] iterator Iterator.
*
* @return Interfaces allow.
*/
int
user_iterator_ifaces_allow (iterator_t* iterator)
{
if (iterator->done) return -1;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
}
/**
* @brief Initialise an info iterator.
*
* @param[in] iterator Iterator.
* @param[in] user User.
*/
void
init_user_group_iterator (iterator_t *iterator, user_t user)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (user);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_groups"));
available = acl_where_owned ("group", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT DISTINCT id, uuid, name, %s FROM groups"
" WHERE id IN (SELECT \"group\" FROM group_users"
" WHERE \"user\" = %llu)"
" ORDER by name;",
with_clause ? with_clause : "",
available,
user);
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the UUID from a user group iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (user_group_iterator_uuid, 1);
/**
* @brief Get the NAME from a user group iterator.
*
* @param[in] iterator Iterator.
*
* @return NAME or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (user_group_iterator_name, 2);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
user_group_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 3);
}
/**
* @brief Initialise an info iterator.
*
* @param[in] iterator Iterator.
* @param[in] user User.
*/
void
init_user_role_iterator (iterator_t *iterator, user_t user)
{
gchar *available, *with_clause;
get_data_t get;
array_t *permissions;
assert (user);
get.trash = 0;
permissions = make_array ();
array_add (permissions, g_strdup ("get_roles"));
available = acl_where_owned ("role", &get, 1, "any", 0, permissions, 0,
&with_clause);
array_free (permissions);
init_iterator (iterator,
"%s"
" SELECT DISTINCT id, uuid, name, order_role (name), %s"
" FROM roles"
" WHERE id IN (SELECT role FROM role_users"
" WHERE \"user\" = %llu)"
" ORDER by order_role (name);",
with_clause ? with_clause : "",
available,
user);
g_free (with_clause);
g_free (available);
}
/**
* @brief Get the UUID from a user role iterator.
*
* @param[in] iterator Iterator.
*
* @return UUID or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (user_role_iterator_uuid, 1);
/**
* @brief Get the NAME from a user role iterator.
*
* @param[in] iterator Iterator.
*
* @return NAME or NULL if iteration is complete. Freed by cleanup_iterator.
*/
DEF_ACCESS (user_role_iterator_name, 2);
/**
* @brief Get the read permission status from a GET iterator.
*
* @param[in] iterator Iterator.
*
* @return 1 if may read, else 0.
*/
int
user_role_iterator_readable (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, 4);
}
/**
* @brief Check if a user still has resources that are in use.
*
* @param[in] user The user to check.
* @param[in] table The table to check for resources in use.
* @param[in] in_use Function to check if a resource is in use.
* @param[in] trash_table The trash table to check for resources in use.
* @param[in] trash_in_use Function to check if a trash resource is in use.
*
* @return 0 no resources in use, 1 found resources used by user.
*/
static int
user_resources_in_use (user_t user,
const char *table, int(*in_use)(resource_t),
const char *trash_table, int(*trash_in_use)(resource_t))
{
iterator_t iter;
int has_resource_in_use = 0;
init_iterator (&iter, "SELECT id FROM %s WHERE owner = %llu",
table, user);
while (next (&iter) && has_resource_in_use == 0)
{
resource_t resource = iterator_int64 (&iter, 0);
has_resource_in_use = in_use (resource);
}
cleanup_iterator (&iter);
if (has_resource_in_use)
return 1;
if (trash_table == NULL || trash_in_use == NULL)
return 0;
init_iterator (&iter, "SELECT id FROM %s WHERE owner = %llu",
trash_table, user);
while (next (&iter) && has_resource_in_use == 0)
{
resource_t resource = iterator_int64 (&iter, 0);
has_resource_in_use = trash_in_use (resource);
}
cleanup_iterator (&iter);
if (has_resource_in_use)
return 2;
return 0;
}
/**
* @brief Filter columns for vuln iterator.
*/
#define VULN_ITERATOR_FILTER_COLUMNS \
{ \
GET_ITERATOR_FILTER_COLUMNS, "results", "hosts", "severity", \
"qod", "oldest", "newest", "type", NULL \
}
/**
* @brief Results SQL for VULN_ITERATOR_COLUMNS.
*/
#define VULN_RESULTS_WHERE \
" WHERE nvt = vulns.uuid" \
" AND (opts.report IS NULL OR results.report = opts.report)" \
" AND (opts.task IS NULL OR results.task = opts.task)" \
" AND (opts.host IS NULL OR results.host = opts.host)" \
" AND (results.severity != " G_STRINGIFY (SEVERITY_ERROR) ")" \
" AND (SELECT has_permission FROM permissions_get_tasks" \
" WHERE \"user\" = gvmd_user ()" \
" AND task = results.task)"
/**
* @brief Vuln iterator columns.
*/
#define VULN_ITERATOR_COLUMNS \
{ \
/* The following must match GET_ITERATOR_COLUMNS */ \
{ "id", "id", KEYWORD_TYPE_INTEGER }, \
{ "uuid", "uuid", KEYWORD_TYPE_STRING }, \
{ "name", "name", KEYWORD_TYPE_STRING }, \
{ "''", "comment", KEYWORD_TYPE_STRING }, \
{ "iso_time (creation_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "iso_time (modification_time)", NULL, KEYWORD_TYPE_STRING }, \
{ "creation_time", "created", KEYWORD_TYPE_INTEGER }, \
{ "modification_time", "modified", KEYWORD_TYPE_INTEGER }, \
{ "cast (null AS text)", "_owner", KEYWORD_TYPE_INTEGER }, \
{ "''", "owner", KEYWORD_TYPE_STRING }, \
/* Type specific columns */ \
{ \
"vuln_results (uuid, opts.task, opts.report, opts.host)", \
"results", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT count(*) FROM" \
" (SELECT results.host FROM results" \
VULN_RESULTS_WHERE \
" GROUP BY results.host) AS hosts_subquery)", \
"hosts", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"severity", NULL, KEYWORD_TYPE_DOUBLE \
}, \
{ \
"qod", NULL, KEYWORD_TYPE_INTEGER \
}, \
{ \
"type", NULL, KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT min (date) FROM results" \
VULN_RESULTS_WHERE ")", \
"oldest", \
KEYWORD_TYPE_INTEGER \
}, \
{ \
"(SELECT max (date) FROM results" \
VULN_RESULTS_WHERE ")", \
"newest", \
KEYWORD_TYPE_INTEGER \
}, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Generate the extra_tables string for a vuln iterator.
*
* @param[in] task_id UUID of the task to limit vulns to.
* @param[in] report_id UUID of the report to limit vulns to.
* @param[in] host IP address of the task to limit vulns to.
* @param[in] min_qod Minimum QoD.
*
* @return Newly allocated string with the extra_tables clause.
*/
static gchar*
vuln_iterator_opts_table (const gchar *task_id, const gchar *report_id,
const gchar *host, int min_qod)
{
GString *ret;
ret = g_string_new (", (SELECT");
if (task_id && strcmp (task_id, ""))
{
task_t task = 0;
find_task_with_permission (task_id, &task, "get_tasks");
g_string_append_printf (ret, " %llu AS task,", task);
}
else
{
g_string_append (ret, " cast (null AS integer) AS task,");
}
if (report_id && strcmp (report_id, ""))
{
report_t report = 0;
find_report_with_permission (report_id, &report, "get_reports");
g_string_append_printf (ret, " %llu AS report,", report);
}
else
{
g_string_append (ret, " cast (null AS integer) AS report,");
}
if (host && strcmp (host, ""))
{
gchar *quoted_host = sql_quote (host);
g_string_append_printf (ret, " '%s' AS host,", quoted_host);
g_free (quoted_host);
}
else
{
g_string_append (ret, " cast (null AS text) AS host,");
}
g_string_append_printf (ret, " %d AS min_qod) AS opts", min_qod);
return g_string_free (ret, FALSE);
}
/**
* @brief Generate the extra_tables string for a vuln iterator using a filter.
*
* @param[in] filter The filter term to use.
*
* @return Newly allocated string with the extra_tables clause.
*/
static gchar*
vuln_iterator_opts_from_filter (const gchar *filter)
{
gchar *task_id, *report_id, *host;
int min_qod;
gchar *ret;
if (filter)
{
task_id = filter_term_value (filter, "task_id");
report_id = filter_term_value (filter, "report_id");
host = filter_term_value (filter, "host");
}
else
task_id = report_id = host = NULL;
min_qod = filter_term_min_qod (filter);
ret = vuln_iterator_opts_table (task_id, report_id, host, min_qod);
g_free (task_id);
g_free (report_id);
g_free (host);
return ret;
}
/**
* @brief Initialise a vulnerability iterator, including observed vulns.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find vuln, 2 failed to find filter (filt_id),
* -1 error.
*/
int
init_vuln_iterator (iterator_t* iterator, const get_data_t *get)
{
int ret;
gchar *filter;
gchar *extra_tables, *extra_where;
static const char *filter_columns[] = VULN_ITERATOR_FILTER_COLUMNS;
static column_t select_columns[] = VULN_ITERATOR_COLUMNS;
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
if (get->filter_replacement)
/* Replace the filter term with one given by the caller. This is
* used by GET_REPORTS to use the default filter with any task (when
* given the special value of -3 in filt_id). */
filter = g_strdup (get->filter_replacement);
else
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
extra_tables
= vuln_iterator_opts_from_filter (filter ? filter : get->filter);
extra_where
= vulns_extra_where ();
ret = init_get_iterator2 (iterator,
"vuln",
get,
select_columns,
NULL, /* trash_select_columns */
NULL, /* where_columns */
NULL, /* trash_where_columns */
filter_columns,
0 /* distinct */,
extra_tables, /* extra_tables */
extra_where, /* extra_where */
NULL, /* extra_where_single */
0, /* owned */
0, /* ignore_id */
NULL);/* extra_order */
g_free (extra_tables);
g_free (extra_where);
return ret;
}
/**
* @brief Get the number of results from a vuln iterator.
*
* @param[in] iterator Iterator.
*
* @return The number of results.
*/
int
vuln_iterator_results (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 0);
}
/**
* @brief Get the number of hosts from a vuln iterator.
*
* @param[in] iterator Iterator.
*
* @return The number of hosts.
*/
int
vuln_iterator_hosts (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
}
/**
* @brief Get the severity from a vuln iterator.
*
* @param[in] iterator Iterator.
*
* @return The severity.
*/
double
vuln_iterator_severity (iterator_t* iterator)
{
if (iterator->done) return SEVERITY_MISSING;
return iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
}
/**
* @brief Get the QoD from a vuln iterator.
*
* @param[in] iterator Iterator.
*
* @return The QoD.
*/
int
vuln_iterator_qod (iterator_t* iterator)
{
if (iterator->done) return -1;
return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
}
/**
* @brief Get the QoD from a vuln iterator.
*
* @param[in] iterator Iterator.
*
* @return The QoD.
*/
const char*
vuln_iterator_type (iterator_t* iterator)
{
if (iterator->done) return NULL;
return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
}
/**
* @brief Get the date of the oldest result from a vuln iterator.
*
* @param[in] iterator Iterator.
*
* @return The oldest result date.
*/
time_t
vuln_iterator_oldest (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
}
/**
* @brief Get the date of the oldest result from a vuln iterator.
*
* @param[in] iterator Iterator.
*
* @return The oldest result date.
*/
time_t
vuln_iterator_newest (iterator_t* iterator)
{
if (iterator->done) return 0;
return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
}
/**
* @brief Count number of vulns.
*
* @param[in] get GET params.
*
* @return Total number of vulns in filtered set.
*/
int
vuln_count (const get_data_t *get)
{
gchar *filter;
static const char *filter_columns[] = VULN_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = VULN_ITERATOR_COLUMNS;
gchar *extra_tables, *extra_where;
int ret;
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
if (get->filter_replacement)
/* Replace the filter term with one given by the caller. This is
* used by GET_REPORTS to use the default filter with any task (when
* given the special value of -3 in filt_id). */
filter = g_strdup (get->filter_replacement);
else
filter = filter_term (get->filt_id);
if (filter == NULL)
return 2;
}
else
filter = NULL;
extra_tables
= vuln_iterator_opts_from_filter (filter ? filter : get->filter);
extra_where
= vulns_extra_where ();
ret = count ("vuln", get, columns, NULL /*trash_columns*/, filter_columns, 0,
extra_tables, extra_where, FALSE);
g_free (filter);
g_free (extra_tables);
g_free (extra_where);
return ret;
}
/**
* @brief Extra WHERE clause for vulns.
*
* @return WHERE clause.
*/
static gchar*
vulns_extra_where ()
{
return g_strdup (" AND vuln_results_exist (uuid, opts.task, opts.report,"
" opts.host)"
" AND (qod >= opts.min_qod)");
}
/**
* @brief Get LDAP info.
*
* @param[out] enabled Whether LDAP is enabled.
* @param[out] host Freshly allocated host.
* @param[out] authdn Freshly allocated Auth DN.
* @param[out] plaintext Whether plaintext auth is allowed.
* @param[out] cacert CA cert if there's one, else NULL.
*/
void
manage_get_ldap_info (int *enabled, gchar **host, gchar **authdn,
int *plaintext, gchar **cacert)
{
if (enabled)
*enabled = ldap_auth_enabled ();
*host = sql_string ("SELECT value FROM meta"
" WHERE name = 'ldap_host';");
if (*host == NULL)
*host = g_strdup ("127.0.0.1");
*authdn = sql_string ("SELECT value FROM meta"
" WHERE name = 'ldap_authdn';");
if (*authdn == NULL)
*authdn = g_strdup ("userid=%s,dc=example,dc=org");
*plaintext = sql_int ("SELECT coalesce ((SELECT CAST (value AS integer)"
" FROM meta"
" WHERE name"
" = 'ldap_allow_plaintext'),"
" 0);");
*cacert = sql_string ("SELECT value FROM meta"
" WHERE name = 'ldap_cacert';");
}
/**
* @brief Set LDAP info.
*
* @param[in] enabled Whether LDAP is enabled. -1 to keep current value.
* @param[in] host LDAP host. NULL to keep current value.
* @param[in] authdn Auth DN. NULL to keep current value.
* @param[in] allow_plaintext Whether plaintext auth is allowed. -1 to
* keep current value.
* @param[in] cacert CA certificate. NULL to keep current value.
*/
void
manage_set_ldap_info (int enabled, gchar *host, gchar *authdn,
int allow_plaintext, gchar *cacert)
{
gchar *quoted;
sql_begin_immediate ();
if (enabled >= 0)
{
sql ("DELETE FROM meta WHERE name LIKE 'ldap_enable';");
sql ("INSERT INTO meta (name, value) VALUES ('ldap_enable', %i);", enabled);
}
if (host)
{
sql ("DELETE FROM meta WHERE name LIKE 'ldap_host';");
quoted = sql_quote (host);
sql ("INSERT INTO meta (name, value) VALUES ('ldap_host', '%s');",
quoted);
g_free (quoted);
}
if (authdn)
{
sql ("DELETE FROM meta WHERE name LIKE 'ldap_authdn';");
quoted = sql_quote (authdn);
sql ("INSERT INTO meta (name, value) VALUES ('ldap_authdn', '%s');",
quoted);
g_free (quoted);
}
if (allow_plaintext >= 0)
{
sql ("DELETE FROM meta WHERE name LIKE 'ldap_allow_plaintext';");
sql ("INSERT INTO meta (name, value) VALUES ('ldap_allow_plaintext', %i);",
allow_plaintext);
}
if (cacert)
{
sql ("DELETE FROM meta WHERE name LIKE 'ldap_cacert';");
quoted = sql_quote (cacert);
sql ("INSERT INTO meta (name, value) VALUES ('ldap_cacert', '%s');",
quoted);
g_free (quoted);
}
sql_commit ();
}
/**
* @brief Get RADIUS info.
*
* @param[out] enabled Whether RADIUS is enabled.
* @param[out] host Freshly allocated RADIUS host.
* @param[out] key Freshly allocated RADIUS secret key.
*/
void
manage_get_radius_info (int *enabled, char **host, char **key)
{
if (enabled)
*enabled = radius_auth_enabled ();
*host = sql_string ("SELECT value FROM meta WHERE name = 'radius_host';");
if (!*host)
*host = g_strdup ("127.0.0.1");
*key = sql_string ("SELECT value FROM meta WHERE name = 'radius_key';");
if (!*key)
*key = g_strdup ("testing123");
}
/**
* @brief Set RADIUS info.
*
* @param[out] enabled Whether RADIUS is enabled. -1 to keep current value.
* @param[out] host RADIUS host. NULL to keep current value.
* @param[out] key Secret key. NULL to keep current value.
*/
void
manage_set_radius_info (int enabled, gchar *host, gchar *key)
{
char *quoted;
sql_begin_immediate ();
if (enabled >= 0)
{
sql ("DELETE FROM meta WHERE name LIKE 'radius_enable';");
sql ("INSERT INTO meta (name, value) VALUES ('radius_enable', %i);",
enabled);
}
if (host)
{
sql ("DELETE FROM meta WHERE name LIKE 'radius_host';");
quoted = sql_quote (host);
sql ("INSERT INTO meta (name, value) VALUES ('radius_host', '%s');",
quoted);
g_free (quoted);
}
if (key)
{
sql ("DELETE FROM meta WHERE name LIKE 'radius_key';");
quoted = sql_quote (key);
sql ("INSERT INTO meta (name, value) VALUES ('radius_key', '%s');",
quoted);
g_free (quoted);
}
sql_commit ();
}
/* Tags */
/**
* @brief Add a resource to a tag.
*
* @param[in] tag Tag to attach to the resource.
* @param[in] type The resource Type.
* @param[in] uuid The resource UUID.
* @param[in] resource The resource row id.
* @param[in] location Whether the resource is in the trashcan.
*
* @return 0 success, -1 error
*/
static int
tag_add_resource (tag_t tag, const char *type, const char *uuid,
resource_t resource, int location)
{
int already_added, ret;
gchar *quoted_resource_uuid;
ret = 0;
quoted_resource_uuid = uuid ? sql_insert (uuid) : g_strdup ("''");
if (type_is_info_subtype (type))
already_added = sql_int ("SELECT count(*) FROM tag_resources"
" WHERE resource_type = '%s'"
" AND resource_uuid = %s"
" AND tag = %llu",
type, quoted_resource_uuid, tag);
else
already_added = sql_int ("SELECT count(*) FROM tag_resources"
" WHERE resource_type = '%s'"
" AND resource = %llu"
" AND resource_location = %d"
" AND tag = %llu",
type, resource, location, tag);
if (already_added == 0)
{
g_debug ("%s - adding %s %s", __func__, type, uuid);
sql ("INSERT INTO tag_resources"
" (tag, resource_type, resource, resource_uuid, resource_location)"
" VALUES (%llu, '%s', %llu, %s, %d)",
tag, type, resource, quoted_resource_uuid, location);
}
else
{
g_debug ("%s - skipping %s %s", __func__, type, uuid);
}
g_free (quoted_resource_uuid);
return ret;
}
/**
* @brief Find a resource by UUID and add it as a tag resource.
*
* @param[in] tag Tag to attach to the resource.
* @param[in] type The resource Type.
* @param[in] uuid The resource UUID.
* @param[in] permission The permission required to get the resource.
*
* @return 0 success, -1 error, 1 resource not found.
*/
static int
tag_add_resource_uuid (tag_t tag, const char *type, const char *uuid,
const char *permission)
{
int resource_location = LOCATION_TABLE;
resource_t resource;
if (find_resource_with_permission (type, uuid,
&resource, permission, 0))
{
g_warning ("%s: Failed to find %s %s",
__func__, type, uuid);
return -1;
}
else if (resource == 0
&& type_has_trash (type))
{
if (find_resource_with_permission (type, uuid,
&resource, permission,
1))
{
g_warning ("%s: Failed to find trash %s %s",
__func__, type, uuid);
return -1;
}
else if (resource != 0)
resource_location = LOCATION_TRASH;
}
if (resource == 0)
return 1;
return tag_add_resource (tag, type, uuid, resource, resource_location);
}
/**
* @brief Find resources from an array by UUID and insert as a tag resource.
*
* @param[in] tag Tag to attach to the resource.
* @param[in] type The resource type.
* @param[in] uuids The array of resource UUIDs.
* @param[out] error_extra Extra error output. Contains UUID if not found.
*
* @return 0 success, -1 error, 1 resource not found.
*/
static int
tag_add_resources_list (tag_t tag, const char *type, array_t *uuids,
gchar **error_extra)
{
gchar *resource_permission, *current_uuid;
int index;
if (type_is_info_subtype (type))
resource_permission = g_strdup ("get_info");
else if (type_is_asset_subtype (type))
resource_permission = g_strdup ("get_assets");
else
resource_permission = g_strdup_printf ("get_%ss", type);
index = 0;
while ((current_uuid = g_ptr_array_index (uuids, index++)))
{
int ret;
ret = tag_add_resource_uuid (tag, type, current_uuid,
resource_permission);
if (ret)
{
g_free (resource_permission);
if (error_extra)
*error_extra = g_strdup (current_uuid);
return ret;
}
}
return 0;
}
/**
* @brief Find resources using a filter and insert as tag resources.
*
* @param[in] tag Tag to attach to the resource.
* @param[in] type The resource type.
* @param[in] filter The filter to select resources with.
*
* @return 0 success, -1 error, 1 resource not found, 2 no resources returned.
*/
static int
tag_add_resources_filter (tag_t tag, const char *type, const char *filter)
{
iterator_t resources;
gchar *filtered_select;
get_data_t resources_get;
int ret;
memset (&resources_get, '\0', sizeof (resources_get));
resources_get.filter = g_strdup (filter);
resources_get.filt_id = FILT_ID_NONE;
resources_get.trash = LOCATION_TABLE;
resources_get.type = g_strdup (type);
ignore_max_rows_per_page = 1;
filtered_select = NULL;
if (strcasecmp (type, "TICKET") == 0)
{
/* TODO This is how it should be done for all types, in order
* to contain each per-resource implementation in its own file. */
if (init_ticket_iterator (&resources, &resources_get))
{
g_warning ("%s: Failed to build filter SELECT", __func__);
sql_rollback ();
g_free (resources_get.filter);
g_free (resources_get.type);
return -1;
}
}
else
{
gchar *columns;
columns = g_strdup_printf ("%ss.id, %ss.uuid", type, type);
switch (type_build_select (type,
columns,
&resources_get, 0, 1, NULL, NULL, NULL,
&filtered_select))
{
case 0:
g_free (columns);
if (sql_int ("SELECT count(*) FROM (%s) AS filter_selection",
filtered_select) == 0)
{
g_free (filtered_select);
return 2;
}
init_iterator (&resources,
"%s",
filtered_select);
break;
default:
g_free (columns);
ignore_max_rows_per_page = 0;
g_warning ("%s: Failed to build filter SELECT", __func__);
sql_rollback ();
g_free (resources_get.filter);
g_free (resources_get.type);
return -1;
}
}
ignore_max_rows_per_page = 0;
g_free (resources_get.filter);
g_free (resources_get.type);
ret = 2;
while (next (&resources))
{
resource_t resource;
const char *current_uuid;
int add_ret;
resource = iterator_int64 (&resources, 0);
current_uuid = iterator_string (&resources, 1);
add_ret = tag_add_resource (tag, type, current_uuid, resource,
LOCATION_TABLE);
if (add_ret)
{
ret = add_ret;
break;
}
ret = 0;
}
cleanup_iterator (&resources);
g_free (filtered_select);
return ret;
}
/**
* @brief Remove resources from a tag using a UUIDs array.
*
* @param[in] tag Tag to attach to the resource.
* @param[in] type The resource type.
* @param[in] uuids The array of resource UUIDs.
* @param[out] error_extra Extra error output. Contains UUID if not found.
*
* @return 0 success, -1 error, 1 resource not found.
*/
static int
tag_remove_resources_list (tag_t tag, const char *type, array_t *uuids,
gchar **error_extra)
{
gchar *current_uuid;
int index;
index = 0;
while ((current_uuid = g_ptr_array_index (uuids, index++)))
{
gchar *uuid_escaped = g_markup_escape_text (current_uuid, -1);
if (sql_int ("SELECT count(*) FROM tag_resources"
" WHERE tag = %llu AND resource_uuid = '%s'",
tag, uuid_escaped) == 0)
{
if (error_extra)
*error_extra = g_strdup (current_uuid);
g_free (uuid_escaped);
return 1;
}
sql ("DELETE FROM tag_resources"
" WHERE tag = %llu AND resource_uuid = '%s'",
tag, uuid_escaped);
g_free (uuid_escaped);
}
return 0;
}
/**
* @brief Remove resources from a tag using a filter.
*
* @param[in] tag Tag to attach to the resource.
* @param[in] type The resource type.
* @param[in] filter The filter to select resources with.
*
* @return 0 success, -1 error, 1 resource not found, 2 no resources returned.
*/
static int
tag_remove_resources_filter (tag_t tag, const char *type, const char *filter)
{
iterator_t resources;
gchar *iterator_select;
get_data_t resources_get;
int ret;
memset (&resources_get, '\0', sizeof (resources_get));
resources_get.filter = g_strdup (filter);
resources_get.filt_id = FILT_ID_NONE;
resources_get.trash = LOCATION_TABLE;
resources_get.type = g_strdup (type);
ignore_max_rows_per_page = 1;
iterator_select = NULL;
if (strcasecmp (type, "TICKET") == 0)
{
/* TODO This is how it should be done for all types, in order
* to contain each per-resource implementation in its own file. */
if (init_ticket_iterator (&resources, &resources_get))
{
ignore_max_rows_per_page = 0;
g_warning ("%s: Failed to init ticket iterator", __func__);
sql_rollback ();
g_free (resources_get.filter);
g_free (resources_get.type);
return -1;
}
}
else
{
gchar *columns;
columns = g_strdup_printf ("%ss.id", type);
switch (type_build_select (type,
columns,
&resources_get, 0, 1, NULL, NULL, NULL,
&iterator_select))
{
case 0:
g_free (columns);
init_iterator (&resources, "%s", iterator_select);
break;
default:
g_free (columns);
ignore_max_rows_per_page = 0;
g_warning ("%s: Failed to build filter SELECT", __func__);
sql_rollback ();
g_free (resources_get.filter);
g_free (resources_get.type);
return -1;
}
}
ignore_max_rows_per_page = 0;
g_free (resources_get.filter);
g_free (resources_get.type);
ret = 2;
while (next (&resources))
{
resource_t resource;
resource = iterator_int64 (&resources, 0);
ret = 0;
sql ("DELETE FROM tag_resources"
" WHERE tag = %llu"
" AND resource = %llu"
" AND resource_location = %d",
tag, resource, resources_get.trash);
}
cleanup_iterator (&resources);
g_free (iterator_select);
return ret;
}
/**
* @brief Find a tag for a specific permission, given a UUID.
*
* @param[in] uuid UUID of tag.
* @param[out] tag Tag return, 0 if successfully failed to find tag.
* @param[in] permission Permission.
*
* @return FALSE on success (including if failed to find tag), TRUE on error.
*/
static gboolean
find_tag_with_permission (const char* uuid, tag_t* tag,
const char *permission)
{
return find_resource_with_permission ("tag", uuid, tag, permission, 0);
}
/**
* @brief Create a tag from an existing tag.
*
* @param[in] name Name of new tag. NULL to copy from existing.
* @param[in] comment Comment on new tag. NULL to copy from existing.
* @param[in] tag_id UUID of existing tag.
* @param[out] new_tag_return New tag.
*
* @return 0 success, 2 failed to find existing tag,
* 99 permission denied, -1 error.
*/
int
copy_tag (const char* name, const char* comment, const char *tag_id,
tag_t* new_tag_return)
{
int ret = 0;
tag_t new_tag, old_tag;
ret = copy_resource ("tag", name, comment, tag_id,
"value, resource_type, active",
1, &new_tag, &old_tag);
if (ret)
return ret;
if (new_tag_return)
*new_tag_return = new_tag;
sql ("INSERT INTO tag_resources"
" (tag, resource_type, resource, resource_uuid, resource_location)"
" SELECT"
" %llu, resource_type, resource, resource_uuid, resource_location"
" FROM tag_resources"
" WHERE tag = %llu",
new_tag, old_tag);
return 0;
}
/**
* @brief Create a tag.
*
* @param[in] name Name of the tag.
* @param[in] comment Comment for the tag.
* @param[in] value Value of the tag.
* @param[in] resource_type Resource type to attach the tag to.
* @param[in] resource_uuids Unique IDs of the resource to attach the tag to.
* @param[in] resources_filter Filter to select resources to attach tag to.
* @param[in] active 0 for inactive, NULL or any other value for active.
* @param[out] tag Created tag.
* @param[out] error_extra Extra string for error (e.g. missing resource ID)
*
* @return 0 success, 1 resource ID not found (sets error_extra to UUID),
* 2 filter returned no results, 3 too many resources selected,
* 99 permission denied, -1 error.
*/
int
create_tag (const char * name, const char * comment, const char * value,
const char * resource_type, array_t * resource_uuids,
const char * resources_filter, const char * active, tag_t * tag,
gchar **error_extra)
{
gchar *quoted_name, *quoted_comment, *quoted_value;
gchar *lc_resource_type, *quoted_resource_type;
tag_t new_tag;
sql_begin_immediate ();
if (acl_user_may ("create_tag") == 0)
{
sql_rollback ();
return 99;
}
lc_resource_type = g_ascii_strdown (resource_type, -1);
if (strcmp (lc_resource_type, "")
&& valid_db_resource_type (lc_resource_type) == 0)
{
g_free (lc_resource_type);
sql_rollback ();
return -1;
}
quoted_name = sql_insert (name);
quoted_resource_type = sql_insert (lc_resource_type);
quoted_comment = sql_insert (comment ? comment : "");
quoted_value = sql_insert (value ? value : "");
sql ("INSERT INTO tags"
" (uuid, owner, creation_time, modification_time, name, comment,"
" value, resource_type, active)"
" VALUES"
" (make_uuid (), (SELECT id FROM users WHERE users.uuid = '%s'),"
" %i, %i, %s, %s, %s, %s, %i);",
current_credentials.uuid,
time (NULL),
time (NULL),
quoted_name,
quoted_comment,
quoted_value,
quoted_resource_type,
active
? (strcmp (active, "0") == 0
? 0
: 1)
: 1);
new_tag = sql_last_insert_id ();
g_free (quoted_name);
g_free (quoted_comment);
g_free (quoted_value);
g_free (quoted_resource_type);
/* Handle resource IDs */
if (resource_uuids)
{
int ret;
ret = tag_add_resources_list (new_tag, lc_resource_type, resource_uuids,
error_extra);
if (ret)
{
// Assume tag_add_resources_list return codes match
sql_rollback ();
g_free (lc_resource_type);
return ret;
}
}
/* Handle filter */
if (resources_filter && strcmp (resources_filter, ""))
{
int ret;
ret = tag_add_resources_filter (new_tag, lc_resource_type,
resources_filter);
if (ret)
{
// Assume tag_add_resources_list return codes match
sql_rollback ();
g_free (lc_resource_type);
return ret;
}
}
g_free (lc_resource_type);
if (tag)
*tag = new_tag;
sql_commit ();
return 0;
}
/**
* @brief Delete a tag.
*
* @param[in] tag_id UUID of tag.
* @param[in] ultimate Whether to remove entirely, or to trashcan.
*
* @return 0 success, 2 failed to find tag, 99 permission denied, -1 error.
*/
int
delete_tag (const char *tag_id, int ultimate)
{
tag_t tag = 0;
sql_begin_immediate ();
if (acl_user_may ("delete_tag") == 0)
{
sql_rollback ();
return 99;
}
if (find_tag_with_permission (tag_id, &tag, "delete_tag"))
{
sql_rollback ();
return -1;
}
if (tag == 0)
{
if (find_trash ("tag", tag_id, &tag))
{
sql_rollback ();
return -1;
}
if (tag == 0)
{
sql_rollback ();
return 2;
}
if (ultimate == 0)
{
/* It's already in the trashcan. */
sql_commit ();
return 0;
}
permissions_set_orphans ("tag", tag, LOCATION_TRASH);
sql ("DELETE FROM tag_resources_trash WHERE tag = %llu", tag);
sql ("DELETE FROM tags_trash WHERE id = %llu;", tag);
sql_commit ();
return 0;
}
if (ultimate == 0)
{
tag_t trash_tag;
sql ("INSERT INTO tags_trash"
" (uuid, owner, name, comment, creation_time,"
" modification_time, resource_type, active, value)"
" SELECT uuid, owner, name, comment, creation_time,"
" modification_time, resource_type, active, value"
" FROM tags WHERE id = %llu;",
tag);
trash_tag = sql_last_insert_id ();
sql ("INSERT INTO tag_resources_trash"
" (tag, resource_type, resource, resource_uuid, resource_location)"
" SELECT"
" %llu, resource_type, resource, resource_uuid, resource_location"
" FROM tag_resources WHERE tag = %llu;",
trash_tag, tag);
permissions_set_locations ("tag", tag, trash_tag, LOCATION_TRASH);
}
else
{
permissions_set_orphans ("tag", tag, LOCATION_TABLE);
tags_remove_resource ("tag", tag, LOCATION_TABLE);
}
sql ("DELETE FROM tag_resources WHERE tag = %llu", tag);
sql ("DELETE FROM tags WHERE id = %llu;", tag);
sql_commit ();
return 0;
}
/**
* @brief Modify a tag.
*
* @param[in] tag_id UUID of tag.
* @param[in] name New name of the tag or NULL.
* @param[in] comment New comment for the tag or NULL.
* @param[in] value New value of the tag or NULL.
* @param[in] resource_type New resource type to attach the tag to or NULL.
* @param[in] resource_uuids New Unique IDs of the resources to attach.
* @param[in] resources_filter Filter to select resources to attach tag to.
* @param[in] resources_action Resources action, e.g. "add" or "remove".
* @param[in] active 0 for inactive, any other for active or NULL.
* @param[out] error_extra Extra string for error (e.g. missing resource ID)
*
* @return 0 success, 1 failed to find tag, 2 tag_id required,
* 3 unexpected resource action,
* 4 resource ID not found (sets error_extra to UUID),
* 5 filter returned no results, 6 too many resources selected,
* 99 permission denied, -1 internal error.
*/
int
modify_tag (const char *tag_id, const char *name, const char *comment,
const char *value, const char *resource_type,
array_t *resource_uuids, const char *resources_filter,
const char *resources_action, const char *active,
gchar **error_extra)
{
gchar *quoted_name, *quoted_comment, *quoted_value;
gchar *lc_resource_type, *quoted_resource_type;
tag_t tag;
gchar *current_resource_type;
if (tag_id == NULL)
return 2;
sql_begin_immediate ();
assert (current_credentials.uuid);
if (acl_user_may ("modify_tag") == 0)
{
sql_rollback ();
return 99;
}
tag = 0;
if (find_tag_with_permission (tag_id, &tag, "modify_tag"))
{
sql_rollback ();
return -1;
}
if (tag == 0)
{
sql_rollback ();
return 1;
}
lc_resource_type = (resource_type
? g_ascii_strdown (resource_type, -1)
: g_strdup (""));
if (strcmp (lc_resource_type, "")
&& valid_db_resource_type (lc_resource_type) == 0)
{
sql_rollback ();
return -1;
}
quoted_resource_type = sql_insert (lc_resource_type);
quoted_name = sql_insert (name ? name : "");
quoted_comment = sql_insert (comment ? comment : "");
quoted_value = sql_insert (value ? value : "");
if (name)
{
sql ("UPDATE tags SET"
" name = %s"
" WHERE id = %llu;",
quoted_name,
tag);
}
if (resource_type)
{
sql ("UPDATE tags SET"
" resource_type = %s"
" WHERE id = %llu;",
quoted_resource_type,
tag);
}
if (comment)
{
sql ("UPDATE tags SET"
" comment = %s"
" WHERE id = %llu;",
quoted_comment,
tag);
}
if (value)
{
sql ("UPDATE tags SET"
" value = %s"
" WHERE id = %llu;",
quoted_value,
tag);
}
if (active)
{
sql ("UPDATE tags SET"
" active = %i"
" WHERE id = %llu;",
strcmp (active, "0") ? 1 : 0,
tag);
}
sql ("UPDATE tags SET"
" modification_time = %i"
" WHERE id = %llu;",
time (NULL),
tag);
g_free (quoted_name);
g_free (quoted_resource_type);
g_free (quoted_comment);
g_free (quoted_value);
current_resource_type = sql_string ("SELECT resource_type"
" FROM tags"
" WHERE id = %llu",
tag);
/* Clear old resources */
if (resources_action == NULL
|| strcmp (resources_action, "") == 0
|| strcmp (resources_action, "set") == 0)
{
if (resource_uuids
|| (resources_filter && strcmp (resources_filter, "")))
{
sql ("DELETE FROM tag_resources WHERE tag = %llu", tag);
}
}
else if (strcmp (resources_action, "add")
&& strcmp (resources_action, "remove"))
{
sql_rollback ();
g_free (current_resource_type);
g_free (lc_resource_type);
return 3;
}
/* Handle resource IDs */
if (resource_uuids)
{
int ret;
if (resources_action && strcmp (resources_action, "remove") == 0)
ret = tag_remove_resources_list (tag, current_resource_type,
resource_uuids, error_extra);
else
ret = tag_add_resources_list (tag, current_resource_type,
resource_uuids, error_extra);
if (ret)
{
sql_rollback ();
g_free (current_resource_type);
g_free (lc_resource_type);
// Assume return codes besides -1 are offset from create_tag
if (ret > 0)
return ret + 3;
else
return ret;
}
}
/* Handle filter */
if (resources_filter && strcmp (resources_filter, ""))
{
int ret;
if (resources_action && strcmp (resources_action, "remove") == 0)
ret = tag_remove_resources_filter (tag, current_resource_type,
resources_filter);
else
ret = tag_add_resources_filter (tag, current_resource_type,
resources_filter);
if (ret)
{
// Assume tag_add_resources_list return codes match
sql_rollback ();
g_free (current_resource_type);
g_free (lc_resource_type);
// Assume return codes besides -1 are offset from create_tag
if (ret > 0)
return ret + 3;
else
return ret;
}
}
g_free (current_resource_type);
g_free (lc_resource_type);
sql_commit ();
return 0;
}
/**
* @brief Filter columns for Tag iterator.
*/
#define TAG_ITERATOR_FILTER_COLUMNS \
{ GET_ITERATOR_FILTER_COLUMNS, "resource_type", "active", "value", \
"resources", NULL }
/**
* @brief Tag iterator columns.
*/
#define TAG_ITERATOR_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (tags), \
{ "resource_type", NULL, KEYWORD_TYPE_STRING }, \
{ "active", NULL, KEYWORD_TYPE_INTEGER }, \
{ "value", NULL, KEYWORD_TYPE_STRING }, \
{ "(SELECT count(*) FROM tag_resources" \
" WHERE tag = tags.id" \
" AND resource_location = " G_STRINGIFY (LOCATION_TABLE) ")", \
"resources", KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Tag iterator trash columns.
*/
#define TAG_ITERATOR_TRASH_COLUMNS \
{ \
GET_ITERATOR_COLUMNS (tags_trash), \
{ "resource_type", NULL, KEYWORD_TYPE_STRING }, \
{ "active", NULL, KEYWORD_TYPE_INTEGER }, \
{ "value", NULL, KEYWORD_TYPE_STRING }, \
{ "(SELECT count(*) FROM tag_resources_trash" \
" WHERE tag = tags_trash.id)", \
"resources", KEYWORD_TYPE_INTEGER }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Filter columns for Tag name iterator.
*/
#define TAG_NAME_ITERATOR_FILTER_COLUMNS \
{ "name", "resource_type", NULL }
/**
* @brief Tag name iterator columns.
*/
#define TAG_NAME_ITERATOR_COLUMNS \
{ \
{ "name", NULL, KEYWORD_TYPE_STRING }, \
{ "resource_type", NULL, KEYWORD_TYPE_STRING }, \
{ NULL, NULL, KEYWORD_TYPE_UNKNOWN } \
}
/**
* @brief Initialise a tag iterator.
*
* @param[in] iterator Iterator.
* @param[in] get GET data.
*
* @return 0 success, 1 failed to find tag, 2 failed to find filter,
* -1 error.
*/
int
init_tag_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = TAG_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = TAG_ITERATOR_COLUMNS;
static column_t trash_columns[] = TAG_ITERATOR_TRASH_COLUMNS;
return init_get_iterator (iterator,
"tag",
get,
columns,
trash_columns,
filter_columns,
0,
NULL,
NULL,
TRUE);
}
/**
* @brief Count number of tags.
*
* @param[in] get GET params.
*
* @return Total number of tags in filtered set.
*/
int
tag_count (const get_data_t *get)
{
static const char *filter_columns[] = TAG_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = TAG_ITERATOR_COLUMNS;
static column_t trash_columns[] = TAG_ITERATOR_TRASH_COLUMNS;
return count ("tag", get, columns, trash_columns, filter_columns,
0, 0, 0, TRUE);
}
/**
* @brief Get the resource_type from a Tag iterator.
*
* @param[in] iterator Iterator.
*
* @return The resource type attached to a tag.
*/
DEF_ACCESS (tag_iterator_resource_type, GET_ITERATOR_COLUMN_COUNT);
/**
* @brief Get if a tag is active from a Tag iterator.
*
* @param[in] iterator Iterator.
*
* @return Whether a tag is active (0 = inactive, 1 = active).
*/
int
tag_iterator_active (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
return ret;
}
/**
* @brief Get the value from a Tag iterator.
*
* @param[in] iterator Iterator.
*
* @return The value associated with a tag.
*/
DEF_ACCESS (tag_iterator_value, GET_ITERATOR_COLUMN_COUNT + 2);
/**
* @brief Get number of resources linked to tag.
*
* @param[in] iterator Iterator.
*
* @return Count of resources linked to tag.
*/
int
tag_iterator_resources (iterator_t* iterator)
{
int ret;
if (iterator->done) return -1;
ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
return ret;
}
/**
* @brief Initialise a iterator of tag names.
*
* @param[in] iterator Iterator.
* @param[in] get GET params.
*
* @return 0 success, -1 error.
*/
int
init_tag_name_iterator (iterator_t* iterator, const get_data_t *get)
{
static const char *filter_columns[] = TAG_NAME_ITERATOR_FILTER_COLUMNS;
static column_t columns[] = TAG_NAME_ITERATOR_COLUMNS;
return init_get_iterator (iterator,
"tag",
get,
columns,
columns,
filter_columns,
1,
NULL,
NULL,
TRUE);
}
/**
* @brief Get the name from a Tag name iterator.
*
* @param[in] iterator Iterator.
*
* @return The tag name.
*/
DEF_ACCESS (tag_name_iterator_name, 0);
/**
* @brief Initialise a iterator of tags attached to a resource.
*
* @param[in] iterator Iterator.
* @param[in] type Resource type.
* @param[in] resource Resource.
* @param[in] active_only Whether to select only active tags.
* @param[in] sort_field Field to sort by.
* @param[in] ascending Whether to sort in ascending order.
*
* @return 0 success, -1 error.
*/
int
init_resource_tag_iterator (iterator_t* iterator, const char* type,
resource_t resource, int active_only,
const char* sort_field, int ascending)
{
get_data_t get;
gchar *owned_clause, *with_clause;
assert (type);
assert (resource);
assert (current_credentials.uuid);
get.trash = 0;
owned_clause = acl_where_owned ("tag", &get, 1, "any", 0, NULL, 0,
&with_clause);
init_iterator (iterator,
"%s"
" SELECT id, uuid, name, value, comment"
" FROM tags"
" WHERE EXISTS"
" (SELECT * FROM tag_resources"
" WHERE resource_type = '%s'"
" AND resource = %llu"
" AND resource_location = %d"
" AND tag = tags.id)"
"%s"
" AND %s"
" ORDER BY %s %s;",
with_clause ? with_clause : "",
type,
resource,
LOCATION_TABLE,
active_only ? " AND active=1" : "",
owned_clause,
sort_field ? sort_field : "active DESC, name",
ascending ? "ASC" : "DESC");
g_free (with_clause);
g_free (owned_clause);
return 0;
}
/**
* @brief Get the Tag UUID from a resource Tag iterator.
*
* @param[in] iterator Iterator.
*
* @return The UUID of the tag.
*/
DEF_ACCESS (resource_tag_iterator_uuid, 1);
/**
* @brief Get the Tag name from a resource Tag iterator.
*
* @param[in] iterator Iterator.
*
* @return The name of the tag.
*/
DEF_ACCESS (resource_tag_iterator_name, 2);
/**
* @brief Get the Tag value from a resource Tag iterator.
*
* @param[in] iterator Iterator.
*
* @return The value of the tag.
*/
DEF_ACCESS (resource_tag_iterator_value, 3);
/**
* @brief Get the Tag comment from a resource Tag iterator.
*
* @param[in] iterator Iterator.
*
* @return The comment of the tag.
*/
DEF_ACCESS (resource_tag_iterator_comment, 4);
/**
* @brief Check if there are tags attached to a resource.
*
* @param[in] type Resource type.
* @param[in] resource Resource.
* @param[in] active_only Whether to count only active tags.
*
* @return 1 if resource has tags, else 0.
*/
int
resource_tag_exists (const char* type, resource_t resource, int active_only)
{
int ret;
assert (type);
assert (resource);
ret = sql_int ("SELECT EXISTS (SELECT *"
" FROM tags"
" WHERE resource_type = '%s'"
" AND EXISTS"
" (SELECT * FROM tag_resources"
" WHERE tag = tags.id"
" AND resource = %llu"
" AND resource_location = %d"
" AND tags.resource_type"
" = tag_resources.resource_type)"
" %s);",
type,
resource,
LOCATION_TABLE,
active_only ? "AND active=1": "");
return ret;
}
/**
* @brief Count number of tags attached to a resource.
*
* @param[in] type Resource type.
* @param[in] resource Resource.
* @param[in] active_only Whether to count only active tags.
*
* @return Total number of tags attached to the resource.
*/
int
resource_tag_count (const char* type, resource_t resource, int active_only)
{
int ret;
assert (type);
assert (resource);
ret = sql_int ("SELECT count (id)"
" FROM tags"
" WHERE resource_type = '%s'"
" AND EXISTS"
" (SELECT * FROM tag_resources"
" WHERE tag = tags.id"
" AND resource = %llu"
" AND resource_location = %d"
" AND tags.resource_type = tag_resources.resource_type)"
" %s;",
type,
resource,
LOCATION_TABLE,
active_only ? "AND active=1": "");
return ret;
}
/**
* @brief Return whether a tag is in use by a task.
*
* @param[in] tag Tag.
*
* @return 1 if in use, else 0.
*/
int
tag_in_use (tag_t tag)
{
return 0;
}
/**
* @brief Return whether a trashcan tag is referenced by a task.
*
* @param[in] tag Tag.
*
* @return 1 if in use, else 0.
*/
int
trash_tag_in_use (tag_t tag)
{
return 0;
}
/**
* @brief Return whether a tag is writable.
*
* @param[in] tag Tag.
*
* @return 1 if writable, else 0.
*/
int
tag_writable (tag_t tag)
{
return 1;
}
/**
* @brief Return whether a trashcan tag is writable.
*
* @param[in] tag Tag.
*
* @return 1 if writable, else 0.
*/
int
trash_tag_writable (tag_t tag)
{
return 0;
}
/**
* @brief Return whether a column is one containing timestamp.
*
* @param[in] column The column name.
*
* @return 1 if column contains timestamps, 0 otherwise.
*/
int
column_is_timestamp (const char* column)
{
return column
&& (strcmp (column, "created") == 0
|| strcmp (column, "date") == 0
|| strcmp (column, "modified") == 0
|| strcmp (column, "published") == 0
|| strcmp (column, "updated") == 0);
}
/**
* @brief Return the columns for a resource iterator.
*
* @param[in] type Resource type to get columns of.
*
* @return The columns.
*/
static column_t *
type_select_columns (const char *type)
{
static column_t alert_columns[] = ALERT_ITERATOR_COLUMNS;
static column_t cert_bund_adv_columns[] = CERT_BUND_ADV_INFO_ITERATOR_COLUMNS;
static column_t config_columns[] = CONFIG_ITERATOR_COLUMNS;
static column_t cpe_columns[] = CPE_INFO_ITERATOR_COLUMNS;
static column_t credential_columns[] = CREDENTIAL_ITERATOR_COLUMNS;
static column_t cve_columns[] = CVE_INFO_ITERATOR_COLUMNS;
static column_t dfn_cert_adv_columns[] = DFN_CERT_ADV_INFO_ITERATOR_COLUMNS;
static column_t filter_columns[] = FILTER_ITERATOR_COLUMNS;
static column_t group_columns[] = GROUP_ITERATOR_COLUMNS;
static column_t host_columns[] = HOST_ITERATOR_COLUMNS;
static column_t note_columns[] = NOTE_ITERATOR_COLUMNS;
static column_t nvt_columns[] = NVT_ITERATOR_COLUMNS;
static column_t os_columns[] = OS_ITERATOR_COLUMNS;
static column_t ovaldef_columns[] = OVALDEF_INFO_ITERATOR_COLUMNS;
static column_t override_columns[] = OVERRIDE_ITERATOR_COLUMNS;
static column_t permission_columns[] = PERMISSION_ITERATOR_COLUMNS;
static column_t report_columns[] = REPORT_ITERATOR_COLUMNS;
static column_t result_columns[] = RESULT_ITERATOR_COLUMNS;
static column_t result_columns_no_cert[] = RESULT_ITERATOR_COLUMNS_NO_CERT;
static column_t role_columns[] = ROLE_ITERATOR_COLUMNS;
static column_t scanner_columns[] = SCANNER_ITERATOR_COLUMNS;
static column_t schedule_columns[] = SCHEDULE_ITERATOR_COLUMNS;
static column_t tag_columns[] = TAG_ITERATOR_COLUMNS;
static column_t target_columns[] = TARGET_ITERATOR_COLUMNS;
static column_t task_columns[] = TASK_ITERATOR_COLUMNS;
static column_t user_columns[] = USER_ITERATOR_COLUMNS;
static column_t vuln_columns[] = VULN_ITERATOR_COLUMNS;
if (type == NULL)
return NULL;
if (strcasecmp (type, "ALERT") == 0)
return alert_columns;
if (strcasecmp (type, "CERT_BUND_ADV") == 0)
return cert_bund_adv_columns;
if (strcasecmp (type, "CONFIG") == 0)
return config_columns;
if (strcasecmp (type, "CPE") == 0)
return cpe_columns;
if (strcasecmp (type, "CREDENTIAL") == 0)
return credential_columns;
if (strcasecmp (type, "CVE") == 0)
return cve_columns;
if (strcasecmp (type, "DFN_CERT_ADV") == 0)
return dfn_cert_adv_columns;
if (strcasecmp (type, "FILTER") == 0)
return filter_columns;
if (strcasecmp (type, "GROUP") == 0)
return group_columns;
if (strcasecmp (type, "HOST") == 0)
return host_columns;
if (strcasecmp (type, "NOTE") == 0)
return note_columns;
if (strcasecmp (type, "NVT") == 0)
return nvt_columns;
if (strcasecmp (type, "OS") == 0)
return os_columns;
if (strcasecmp (type, "OVALDEF") == 0)
return ovaldef_columns;
if (strcasecmp (type, "OVERRIDE") == 0)
return override_columns;
if (strcasecmp (type, "PERMISSION") == 0)
return permission_columns;
if (strcasecmp (type, "PORT_LIST") == 0)
return port_list_select_columns ();
if (strcasecmp (type, "REPORT") == 0)
return report_columns;
if (strcasecmp (type, "REPORT_FORMAT") == 0)
return report_format_select_columns ();
if (strcasecmp (type, "RESULT") == 0)
{
if (manage_cert_loaded ())
return result_columns;
return result_columns_no_cert;
}
if (strcasecmp (type, "ROLE") == 0)
return role_columns;
if (strcasecmp (type, "SCANNER") == 0)
return scanner_columns;
if (strcasecmp (type, "SCHEDULE") == 0)
return schedule_columns;
if (strcasecmp (type, "TAG") == 0)
return tag_columns;
if (strcasecmp (type, "TARGET") == 0)
return target_columns;
if (strcasecmp (type, "TASK") == 0)
return task_columns;
/* Tickets don't use this. */
assert (strcasecmp (type, "ticket"));
if (strcasecmp (type, "TLS_CERTIFICATE") == 0)
return tls_certificate_select_columns ();
if (strcasecmp (type, "USER") == 0)
return user_columns;
if (strcasecmp (type, "VULN") == 0)
return vuln_columns;
return NULL;
}
/**
* @brief Return the columns for a resource iterator.
*
* @param[in] type Resource type to get columns of.
*
* @return The columns.
*/
static column_t *
type_where_columns (const char *type)
{
static column_t task_columns[] = TASK_ITERATOR_WHERE_COLUMNS;
static column_t report_columns[] = REPORT_ITERATOR_WHERE_COLUMNS;
static column_t host_columns[] = HOST_ITERATOR_WHERE_COLUMNS;
static column_t os_columns[] = OS_ITERATOR_WHERE_COLUMNS;
if (type == NULL)
return NULL;
if (strcasecmp (type, "TASK") == 0)
return task_columns;
if (strcasecmp (type, "REPORT") == 0)
return report_columns;
if (strcasecmp (type, "HOST") == 0)
return host_columns;
if (strcasecmp (type, "OS") == 0)
return os_columns;
return NULL;
}
/**
* @brief Return the filter columns for a resource iterator.
*
* @param[in] type Resource type to get columns of.
*
* @return The filter columns.
*/
static const char**
type_filter_columns (const char *type)
{
if (type == NULL)
return NULL;
if (strcasecmp (type, "ALERT") == 0)
{
static const char *ret[] = ALERT_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "CERT_BUND_ADV") == 0)
{
static const char *ret[] = CERT_BUND_ADV_INFO_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "CONFIG") == 0)
{
static const char *ret[] = CONFIG_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "CREDENTIAL") == 0)
{
static const char *ret[] = CREDENTIAL_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "CPE") == 0)
{
static const char *ret[] = CPE_INFO_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "CVE") == 0)
{
static const char *ret[] = CVE_INFO_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "DFN_CERT_ADV") == 0)
{
static const char *ret[] = DFN_CERT_ADV_INFO_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "FILTER") == 0)
{
static const char *ret[] = FILTER_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "GROUP") == 0)
{
static const char *ret[] = GROUP_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "HOST") == 0)
{
static const char *ret[] = HOST_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "NOTE") == 0)
{
static const char *ret[] = NOTE_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "NVT") == 0)
{
static const char *ret[] = NVT_INFO_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "OS") == 0)
{
static const char *ret[] = OS_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "OVALDEF") == 0)
{
static const char *ret[] = OVALDEF_INFO_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "OVERRIDE") == 0)
{
static const char *ret[] = OVERRIDE_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "PERMISSION") == 0)
{
static const char *ret[] = PERMISSION_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "PORT_LIST") == 0)
return port_list_filter_columns ();
if (strcasecmp (type, "REPORT") == 0)
{
static const char *ret[] = REPORT_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "REPORT_FORMAT") == 0)
return report_format_filter_columns ();
if (strcasecmp (type, "RESULT") == 0)
{
static const char *ret[] = RESULT_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "ROLE") == 0)
{
static const char *ret[] = ROLE_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "SCANNER") == 0)
{
static const char *ret[] = SCANNER_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "SCHEDULE") == 0)
{
static const char *ret[] = SCHEDULE_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "TAG") == 0)
{
static const char *ret[] = TAG_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "TARGET") == 0)
{
static const char *ret[] = TARGET_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "TASK") == 0)
{
static const char *ret[] = TASK_ITERATOR_FILTER_COLUMNS;
return ret;
}
/* Tickets don't use this. */
assert (strcasecmp (type, "ticket"));
if (strcasecmp (type, "TLS_CERTIFICATE") == 0)
return tls_certificate_filter_columns ();
if (strcasecmp (type, "USER") == 0)
{
static const char *ret[] = USER_ITERATOR_FILTER_COLUMNS;
return ret;
}
if (strcasecmp (type, "VULN") == 0)
{
static const char *ret[] = VULN_ITERATOR_FILTER_COLUMNS;
return ret;
}
return NULL;
}
/**
* @brief Return the opts subquery definition for a resource type.
*
* @param[in] type Resource type to get columns of.
* @param[in] filter Filter to apply.
*
* @return The SQL subquery definition.
*/
static gchar*
type_opts_table (const char *type, const char *filter)
{
if (type == NULL)
return NULL;
if (strcasecmp (type, "TASK") == 0)
return task_iterator_opts_table (filter_term_apply_overrides (filter),
filter_term_min_qod (filter), 0);
if (strcasecmp (type, "OS") == 0)
return asset_os_iterator_opts_table ();
if (strcasecmp (type, "REPORT") == 0)
return report_iterator_opts_table (filter_term_apply_overrides (filter),
filter_term_min_qod (filter));
if (strcasecmp (type, "RESULT") == 0)
return result_iterator_opts_table (filter_term_apply_overrides (filter),
setting_dynamic_severity_int ());
if (strcasecmp (type, "VULN") == 0)
{
gchar *task_id, *report_id, *host;
gchar *ret;
task_id = filter_term_value (filter, "task_id");
report_id = filter_term_value (filter, "report_id");
host = filter_term_value (filter, "host");
ret = vuln_iterator_opts_table (task_id, report_id, host,
filter_term_min_qod (filter));
g_free (task_id);
g_free (report_id);
g_free (host);
return ret;
}
return NULL;
}
/**
* @brief Return the table name or union for a resource type.
*
* @param[in] type Resource type to get columns of.
* @param[in] trash Whether to get the trash table.
*
* @return The SQL column definitions.
*/
static char*
type_table (const char *type, int trash)
{
if (type == NULL)
return NULL;
if (trash && type_trash_in_table (type) == 0)
return g_strdup_printf ("%ss_trash", type);
if (trash == 0 || type_trash_in_table (type))
return g_strdup_printf ("%ss", type);
return NULL;
}
/**
* @brief Return addition to the WHERE clause if required for a resource type.
*
* @param[in] type Resource type to get columns of.
* @param[in] trash Whether to get the trash table.
* @param[in] filter The filter term.
* @param[in] extra_params Optional extra parameters.
*
* @return The newly allocated WHERE clause additions.
*/
static gchar*
type_extra_where (const char *type, int trash, const char *filter,
GHashTable *extra_params)
{
gchar *extra_where;
if (strcasecmp (type, "CONFIG") == 0 && extra_params)
{
gchar *usage_type;
if (extra_params)
usage_type = g_hash_table_lookup (extra_params, "usage_type");
else
usage_type = NULL;
extra_where = configs_extra_where (usage_type);
if (extra_where == NULL)
extra_where = g_strdup ("");
}
else if (strcasecmp (type, "TASK") == 0)
{
gchar *usage_type;
if (extra_params)
usage_type = g_hash_table_lookup (extra_params, "usage_type");
else
usage_type = NULL;
extra_where = tasks_extra_where (trash, usage_type);
}
else if (strcasecmp (type, "TLS_CERTIFICATE") == 0)
{
extra_where = tls_certificate_extra_where (filter);
}
else if (strcasecmp (type, "REPORT") == 0)
{
if (trash)
extra_where = g_strdup (" AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 2");
else
extra_where = g_strdup (" AND (SELECT hidden FROM tasks"
" WHERE tasks.id = task)"
" = 0");
}
else if (strcasecmp (type, "RESULT") == 0)
{
int apply_overrides;
gchar *report_id;
report_t report;
/* Note: This keyword may be removed or renamed at any time once there
* is a better solution like an operator for conditions that must always
* apply or support for parentheses in filters. */
report_id = filter_term_value (filter,
"_and_report_id");
report = 0;
if (report_id)
{
if (find_report_with_permission (report_id,
&report,
NULL))
{
g_free (report_id);
g_warning ("Failed to get report");
return NULL;
}
if (report == 0)
report = -1;
}
g_free (report_id);
apply_overrides = filter_term_apply_overrides (filter);
extra_where = results_extra_where (trash, report, NULL,
apply_overrides,
setting_dynamic_severity_int (),
filter,
NULL);
}
else if (strcasecmp (type, "VULN") == 0)
{
extra_where = vulns_extra_where ();
}
else
extra_where = g_strdup ("");
return extra_where;
}
/**
* @brief Get the extra WITH clauses for a resource type.
*
* @param[in] type The resource type.
*
* @return The extra WITH clauses.
*/
static const char *
type_extra_with (const char *type)
{
return NULL;
}
/**
* @brief Builds a filtered SELECT statement for a certain type.
*
* @param[in] type Resource type
* @param[in] columns_str Columns to get (as used in SQL)
* @param[in] get The get data
* @param[in] distinct Whether to get distinct items
* @param[in] ordered Whether to apply the ordering
* @param[in] extra_tables Extra tables / subqueries
* @param[in] given_extra_where Extra expressions for WHERE clause
* @param[in] group_by Column(s) to group by, NULL for no grouping
* @param[out] select Output of newly allocated SELECT statement
*
* @return 0: success, 1: filter not found, -1 error.
*/
static int
type_build_select (const char *type, const char *columns_str,
const get_data_t *get,
gboolean distinct, gboolean ordered,
const char *extra_tables, const char *given_extra_where,
const char *group_by,
gchar **select)
{
gchar *filter, *with;
gchar *from_table, *opts_table;
gchar *clause, *extra_where, *filter_order;
int first, max;
gchar *owned_clause, *owner_filter;
array_t *permissions;
const char *extra_with;
column_t *select_columns, *where_columns;
const char **filter_columns;
gchar *pagination_clauses;
assert (select);
// Get filter
if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
{
if (get->filter_replacement)
/* Replace the filter term with one given by the caller. This is
* used by GET_REPORTS to use the default filter with any task (when
* given the special value of -3 in filt_id). */
filter = g_strdup (get->filter_replacement);
else
filter = filter_term (get->filt_id);
if (filter == NULL)
{
return 1;
}
}
else
filter = NULL;
// FROM ... part
from_table = type_table (type, get->trash);
opts_table = type_opts_table (type, filter ? filter : get->filter);
if (strcasecmp (type, "RESULT") == 0)
{
gchar *original;
int overrides, dynamic;
overrides = filter_term_apply_overrides (filter ? filter : get->filter);
dynamic = setting_dynamic_severity_int ();
original = opts_table;
opts_table = g_strdup_printf (" LEFT OUTER JOIN nvts"
" ON results.nvt = nvts.oid %s,"
" LATERAL %s AS lateral_new_severity",
original,
result_iterator_lateral (overrides,
dynamic));
g_free (original);
}
// WHERE ... part
select_columns = type_select_columns (type);
where_columns = type_where_columns (type);
filter_columns = type_filter_columns (type);
if (filter_columns == NULL)
return -1;
clause = filter_clause (type, filter ? filter : get->filter, filter_columns,
select_columns, where_columns, get->trash,
&filter_order, &first, &max, &permissions,
&owner_filter);
owned_clause = acl_where_owned (type, get, type_owned (type),
owner_filter, 0, permissions, 0,
&with);
if (given_extra_where)
extra_where = g_strdup (given_extra_where);
else
extra_where = type_extra_where (type, get->trash,
filter ? filter : get->filter,
get->extra_params);
if (get->ignore_pagination)
pagination_clauses = NULL;
else
pagination_clauses = g_strdup_printf (" LIMIT %s OFFSET %d",
sql_select_limit (max),
first);
extra_with = type_extra_with (type);
if (extra_with)
{
if (with)
{
gchar *old_with;
old_with = with;
with = g_strdup_printf ("%s, %s", old_with, extra_with);
g_free (old_with);
}
else
with = g_strdup_printf ("WITH %s", extra_with);
}
*select = g_strdup_printf
("%s" // WITH
"SELECT%s %s" // DISTINCT, columns
" FROM %s%s%s" // from_table, opts_table, extra_tables
" WHERE"
" %s%s" // owned_clause, extra_where
" %s%s%s" // (filter) clause
" %s%s" // group_by
" %s" // ORDER BY (filter_order)
" %s", // pagination_clauses
with ? with : "",
distinct ? " DISTINCT" : "",
columns_str,
from_table,
opts_table ? opts_table : "",
extra_tables ? extra_tables : "",
owned_clause,
extra_where,
clause ? " AND (" : "",
clause ? clause : "",
clause ? ")" : "",
group_by ? " GROUP BY " : "",
group_by ? group_by : "",
(ordered && filter_order) ? filter_order : "",
pagination_clauses ? pagination_clauses : "");
g_free (with);
g_free (from_table);
g_free (opts_table);
g_free (owned_clause);
g_free (extra_where);
g_free (pagination_clauses);
return 0;
}
/**
* @brief Remove a resource from tags.
*
* @param[in] type Type.
* @param[in] resource Resource.
* @param[in] location Location: table or trash.
*/
void
tags_remove_resource (const char *type, resource_t resource, int location)
{
sql ("DELETE FROM tag_resources"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = %i;",
type,
resource,
location);
}
/**
* @brief Adjust location of resource in tags.
*
* @param[in] type Type.
* @param[in] old Resource ID in old table.
* @param[in] new Resource ID in new table.
* @param[in] to Destination, trash or table.
*/
void
tags_set_locations (const char *type, resource_t old, resource_t new,
int to)
{
sql ("UPDATE tag_resources SET resource_location = %i, resource = %llu"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = %i;",
to,
new,
type,
old,
to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
sql ("UPDATE tag_resources_trash SET resource_location = %i, resource = %llu"
" WHERE resource_type = '%s' AND resource = %llu"
" AND resource_location = %i;",
to,
new,
type,
old,
to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
}
/**
* @brief Get a GArray of all users as user_t.
*
* @return Newly allocated GArray containing all users.
*/
static GArray*
all_users_array ()
{
iterator_t users_iter;
GArray *ret;
ret = g_array_new (TRUE, TRUE, sizeof (resource_t));
init_iterator (&users_iter, "SELECT id FROM users;");
while (next (&users_iter))
{
user_t user = iterator_int64 (&users_iter, 0);
g_array_append_val (ret, user);
}
cleanup_iterator (&users_iter);
return ret;
}
/**
* @brief Update permissions cache for a resource.
*
* @param[in] type Resource type.
* @param[in] resource The resource to update the cache for.
* @param[in] cache_users GArray of users to create cache for or NULL for all.
*/
static void
cache_permissions_for_resource (const char *type, resource_t resource,
GArray *cache_users)
{
int free_users;
if (type == NULL || resource == 0 || resource == -1)
return;
if (cache_users == NULL)
{
g_debug ("%s: Getting all users", __func__);
free_users = 1;
cache_users = all_users_array ();
}
else
free_users = 0;
if (strcmp (type, "task") == 0)
{
char* old_current_user_id;
gchar *resource_id;
int user_index;
old_current_user_id = current_credentials.uuid;
resource_id = resource_uuid (type, resource);
g_debug ("%s: Caching permissions on %s \"%s\" for %d user(s)",
__func__, type, resource_id, cache_users->len);
for (user_index = 0; user_index < cache_users->len; user_index++)
{
user_t user;
gchar *user_id;
user = g_array_index (cache_users, user_t, user_index);
user_id = user_uuid (user);
current_credentials.uuid = user_id;
manage_session_init (user_id);
if (sql_int ("SELECT count(*) FROM permissions_get_%ss"
" WHERE \"user\" = %llu"
" AND %s = %llu;",
type,
user,
type,
resource))
{
sql ("UPDATE permissions_get_%ss"
" SET has_permission"
" = user_has_access_uuid (cast ('%s' as text),"
" cast ('%s' as text),"
" cast ('get_%ss' as text),"
" 0)"
" WHERE \"user\" = %llu"
" AND %s = %llu;",
type,
type,
resource_id,
type,
user,
type,
resource);
}
else
{
sql ("INSERT INTO permissions_get_%ss"
" (\"user\", %s, has_permission)"
" SELECT %llu, %llu,"
" user_has_access_uuid (cast ('%s' as text),"
" cast ('%s' as text),"
" cast ('get_%ss' as text),"
" 0);",
type,
type,
user,
resource,
type,
resource_id,
type);
}
g_free (user_id);
current_credentials.uuid = NULL;
}
current_credentials.uuid = old_current_user_id;
manage_session_init (old_current_user_id);
g_free (resource_id);
}
if (free_users)
g_array_free (cache_users, TRUE);
}
/**
* @brief Update permissions cache for a given type and selection of users.
*
* @param[in] type Type.
* @param[in] cache_users GArray of users to create cache for.
*/
static void
cache_permissions_for_users (const char *type, GArray *cache_users)
{
int free_users;
if (type == NULL)
return;
if (cache_users == NULL)
{
g_debug ("%s: Getting all users", __func__);
free_users = 1;
cache_users = all_users_array ();
}
else
free_users = 0;
if (strcmp (type, "task") == 0)
{
iterator_t resources;
init_iterator (&resources, "SELECT id FROM %ss;", type);
while (next (&resources))
{
resource_t resource = iterator_int64 (&resources, 0);
cache_permissions_for_resource (type, resource, cache_users);
}
cleanup_iterator (&resources);
}
if (free_users)
g_array_free (cache_users, TRUE);
}
/**
* @brief Update entire permission cache the given users.
*
* @param[in] cache_users GArray of users to create cache for. NULL means
* all users.
*/
static void
cache_all_permissions_for_users (GArray *cache_users)
{
int free_users;
if (cache_users == NULL)
{
g_debug ("%s: Getting all users", __func__);
free_users = 1;
cache_users = all_users_array ();
}
else
free_users = 0;
cache_permissions_for_users ("task", cache_users);
if (free_users)
g_array_free (cache_users, TRUE);
}
/**
* @brief Delete permission cache a resource.
*
* @param[in] type Resource type.
* @param[in] resource Resource.
*/
void
delete_permissions_cache_for_resource (const char* type, resource_t resource)
{
if (type == NULL || resource == 0)
return;
if (strcmp (type, "task") == 0)
{
sql ("DELETE FROM permissions_get_%ss WHERE \"%s\" = %llu",
type, type, resource);
}
}
/**
* @brief Delete permission cache the given user.
*
* @param[in] user User.
*/
void
delete_permissions_cache_for_user (user_t user)
{
sql ("DELETE FROM permissions_get_tasks WHERE \"user\" = %llu;", user);
}
/* Optimize. */
/**
* @brief Run one of the optimizations.
*
* @param[in] log_config Log configuration.
* @param[in] database Location of manage database.
* @param[in] name Name of optimization.
*
* @return 0 success, 1 error in name, -1 error,
* -2 database is wrong version, -3 database needs to be initialised
* from server.
*/
int
manage_optimize (GSList *log_config, const db_conn_info_t *database,
const gchar *name)
{
gchar *success_text;
int ret;
g_info (" Optimizing: %s.", name);
if (name == NULL)
{
fprintf (stderr, "Name required for optimize.\n");
return 1;
}
ret = manage_option_setup (log_config, database);
if (ret)
return ret;
ret = 0;
if (strcasecmp (name, "vacuum") == 0)
{
gchar *quoted_db_name;
unsigned long long int old_size, new_size;
quoted_db_name = sql_quote (sql_database ());
old_size = sql_int64_0 ("SELECT pg_database_size ('%s')",
quoted_db_name);
sql ("VACUUM;");
new_size = sql_int64_0 ("SELECT pg_database_size ('%s')",
quoted_db_name);
g_free (quoted_db_name);
if (old_size <= 0 || new_size <= 0)
success_text = g_strdup_printf ("Optimized: vacuum.");
else if (new_size <= old_size)
success_text = g_strdup_printf ("Optimized: vacuum."
" Database file size reduced by"
" %llu MiB (%0.1f %%).\n",
(old_size - new_size) / (1024 * 1024),
(old_size - new_size)
* 100.0 / old_size);
else
success_text = g_strdup_printf ("Optimized: vacuum."
" Database file size *increased* by"
" %llu MiB (%0.1f %%).\n",
(new_size - old_size) / (1024 * 1024),
(new_size - old_size)
* 100.0 / old_size);
}
else if (strcasecmp (name, "add-feed-permissions") == 0)
{
int permissions_count, object_count;
permissions_count = 0;
object_count = 0;
sql_begin_immediate ();
add_feed_role_permissions ("config",
"Scan Config / Policy",
&permissions_count,
&object_count);
add_feed_role_permissions ("port_list",
"Port List",
&permissions_count,
&object_count);
add_feed_role_permissions ("report_format",
"Report Format",
&permissions_count,
&object_count);
sql_commit ();
success_text = g_strdup_printf ("Optimized: add-feed-permissions."
" Added %d permissions"
" for %d data objects.",
permissions_count,
object_count);
}
else if (strcasecmp (name, "analyze") == 0)
{
sql ("ANALYZE;");
success_text = g_strdup_printf ("Optimized: analyze.");
}
else if (strcasecmp (name, "cleanup-config-prefs") == 0)
{
int removed, fixed_values;
sql ("DELETE FROM config_preferences WHERE id NOT IN"
" (SELECT min(id) FROM config_preferences"
" GROUP BY config, type, name, value);");
removed = sql_changes();
sql ("UPDATE config_preferences"
" SET value = (SELECT value FROM nvt_preferences"
" WHERE name='scanner_plugins_timeout')"
" WHERE name = 'scanner_plugins_timeout'"
" AND value = 'SCANNER_NVT_TIMEOUT';");
fixed_values = sql_changes();
success_text = g_strdup_printf ("Optimized: cleanup-config-prefs."
" Duplicate config preferences removed:"
" %d. Corrected preference values: %d",
removed, fixed_values);
}
else if (strcasecmp (name, "cleanup-feed-permissions") == 0)
{
int permissions_count, object_count;
permissions_count = 0;
object_count = 0;
sql_begin_immediate ();
clean_feed_role_permissions ("config",
"Scan Config / Policy",
&permissions_count,
&object_count);
clean_feed_role_permissions ("port_list",
"Port List",
&permissions_count,
&object_count);
clean_feed_role_permissions ("report_format",
"Report Format",
&permissions_count,
&object_count);
sql_commit ();
success_text = g_strdup_printf ("Optimized: cleanup-feed-permissions."
" Removed %d permissions"
" for %d data objects.",
permissions_count,
object_count);
}
else if (strcasecmp (name, "cleanup-port-names") == 0)
{
int changes_iana, changes_old_format;
sql_begin_immediate ();
sql ("UPDATE results"
" SET port = substr (port, 1,"
" strpos (port, ' (IANA:') - 1)"
" WHERE port LIKE '% (IANA:%';");
changes_iana = sql_changes();
sql ("UPDATE results"
" SET port = substr (port,"
" strpos (port ,'(') + 1,"
" strpos (port, ')') - strpos (port, '(') - 1)"
" WHERE port LIKE '%(%)%';");
changes_old_format = sql_changes();
sql_commit ();
success_text = g_strdup_printf ("Optimized: cleanup-port-names."
" Ports converted from old format: %d,"
" removed IANA port names: %d.",
changes_old_format, changes_iana);
}
else if (strcasecmp (name, "cleanup-report-formats") == 0)
{
iterator_t alert_data;
int alert_changes = 0;
/* Clean up alerts with missing report formats */
sql_begin_immediate ();
init_iterator (&alert_data,
"SELECT id, data,"
" (SELECT uuid FROM alerts WHERE id = alert)"
" FROM alert_method_data"
" WHERE (name = 'notice_attach_format'"
" OR name = 'notice_report_format'"
" OR name = 'send_report_format')"
" AND data NOT IN (SELECT uuid"
" FROM report_formats)"
" AND data NOT IN (SELECT uuid"
" FROM report_formats_trash)");
while (next (&alert_data))
{
alert_changes ++;
g_message ("Alert %s uses a non-existent report format (%s)"
" and will now use the TXT report format (%s)"
" if TXT exists.",
iterator_string (&alert_data, 2),
iterator_string (&alert_data, 1),
"a3810a62-1f62-11e1-9219-406186ea4fc5");
sql ("UPDATE alert_method_data SET data = '%s'"
" WHERE id = %llu",
"a3810a62-1f62-11e1-9219-406186ea4fc5",
iterator_int64 (&alert_data, 0));
}
cleanup_iterator(&alert_data);
init_iterator (&alert_data,
"SELECT id, data,"
" (SELECT uuid FROM alerts_trash WHERE id = alert)"
" FROM alert_method_data_trash"
" WHERE (name = 'notice_attach_format'"
" OR name = 'notice_report_format'"
" OR name = 'send_report_format')"
" AND data NOT IN (SELECT uuid"
" FROM report_formats)"
" AND data NOT IN (SELECT uuid"
" FROM report_formats_trash)");
while (next (&alert_data))
{
alert_changes ++;
g_warning ("Trash Alert %s uses a non-existent report format (%s)"
" and will now use the TXT report format (%s)"
" if TXT exists.",
iterator_string (&alert_data, 2),
iterator_string (&alert_data, 1),
"a3810a62-1f62-11e1-9219-406186ea4fc5");
sql ("UPDATE alert_method_data_trash SET data = '%s'"
" WHERE id = %llu",
"a3810a62-1f62-11e1-9219-406186ea4fc5",
iterator_int64 (&alert_data, 0));
}
cleanup_iterator(&alert_data);
sql_commit ();
success_text = g_strdup_printf ("Optimized: cleanup-report-formats."
" Cleaned up report format references in"
" %d alert(s).",
alert_changes);
}
else if (strcasecmp (name, "cleanup-result-nvts") == 0)
{
sql_begin_immediate ();
if (cleanup_result_nvts ())
{
sql_rollback();
fprintf (stderr, "Clean-up of result_nvts failed.\n");
manage_option_cleanup ();
return 1;
}
sql_commit ();
success_text = g_strdup_printf ("Optimized: Cleaned up result_nvts.");
}
else if (strcasecmp (name, "cleanup-result-severities") == 0)
{
int missing_severity_changes = 0;
sql_begin_immediate ();
sql ("UPDATE results"
" SET severity"
" = (SELECT CAST (value AS real) FROM settings"
" WHERE uuid = '7eda49c5-096c-4bef-b1ab-d080d87300df'"
" AND (settings.owner = results.owner"
" OR settings.owner IS NULL)"
" ORDER BY settings.owner DESC LIMIT 1)"
" WHERE severity IS NULL;");
missing_severity_changes = sql_changes();
sql_commit ();
success_text = g_strdup_printf ("Optimized: cleanup-result-severities."
" Missing severity scores added: %d.",
missing_severity_changes);
}
else if (strcasecmp (name, "cleanup-result-encoding") == 0)
{
sql_begin_immediate ();
g_debug ("%s: Stripping control chars out of result descriptions",
__func__);
sql ("UPDATE results"
" SET description = regexp_replace (description,"
" '[\x01-\x09\xB-\x1F]',"
" ' ',"
" 'g')"
" WHERE description ~ '[\x01-\x09\xB-\x1F]';");
sql_commit ();
success_text = g_strdup_printf ("Optimized: Cleaned up result encoding.");
}
else if (strcasecmp (name, "cleanup-schedule-times") == 0)
{
int changes;
sql_begin_immediate ();
changes = cleanup_schedule_times ();
sql_commit ();
success_text = g_strdup_printf ("Optimized: cleanup-schedule-times."
" Due date updated for %d tasks.",
changes);
}
else if (strcasecmp (name, "migrate-relay-sensors") == 0)
{
if (get_relay_mapper_path ())
{
sql_begin_immediate ();
success_text = manage_migrate_relay_sensors ();
sql_commit ();
}
else
{
fprintf (stderr,
"No relay mapper found."
" Please check your $PATH or the --relay-mapper option.\n");
success_text = NULL;
ret = -1;
}
}
else if (strcasecmp (name, "rebuild-permissions-cache") == 0)
{
sql_begin_immediate ();
sql ("DELETE FROM permissions_get_tasks");
cache_all_permissions_for_users (NULL);
sql_commit ();
success_text = g_strdup_printf ("Optimized: rebuild-permissions-cache."
" Permission cache rebuilt.");
}
else if (strcasecmp (name, "rebuild-report-cache") == 0)
{
int changes;
sql_begin_immediate ();
reports_build_count_cache (1, &changes);
sql_commit ();
success_text = g_strdup_printf ("Optimized: rebuild-report-cache."
" Result counts recalculated for %d"
" reports.",
changes);
}
else if (strcasecmp (name, "update-report-cache") == 0)
{
int changes;
sql_begin_immediate ();
reports_build_count_cache (0, &changes);
sql_commit ();
success_text = g_strdup_printf ("Optimized: update-report-cache."
" Result counts calculated for %d"
" reports.",
changes);
}
else
{
fprintf (stderr, "Error in optimize name.\n");
ret = 1;
success_text = NULL;
}
if (success_text)
{
printf ("%s\n", success_text);
g_message (" %s", success_text);
g_free (success_text);
}
current_credentials.uuid = NULL;
manage_option_cleanup ();
return ret;
}
/**
* @brief Cancels the current SQL statement.
*
* @return 0 on success, -1 on error.
*/
int
sql_cancel ()
{
g_debug ("%s: cancelling current SQL statement", __func__);
return sql_cancel_internal ();
}
/**
* @brief Get the VT verification collation override.
*
* @return The collation or NULL for automatic.
*/
const char *
get_vt_verification_collation ()
{
return vt_verification_collation;
}
/**
* @brief Sets the VT verification collation override.
*
* This must be done before the SQL functions are created to be effective.
*
* @param[in] new_collation The new collation.
*/
void
set_vt_verification_collation (const char *new_collation)
{
g_free (vt_verification_collation);
if (new_collation && strcmp (new_collation, ""))
vt_verification_collation = g_strdup(new_collation);
else
vt_verification_collation = NULL;
}
]