1 /* Copyright (C) 2009-2021 Greenbone Networks GmbH
2  *
3  * SPDX-License-Identifier: AGPL-3.0-or-later
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Affero General Public License as
7  * published by the Free Software Foundation, either version 3 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Affero General Public License for more details.
14  *
15  * You should have received a copy of the GNU Affero General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /**
20  * @file  manage_sql.c
21  * @brief The Greenbone Vulnerability Manager management library.
22  */
23 
24 /**
25  * @brief Enable extra GNU functions.
26  */
27 #define _GNU_SOURCE
28 
29 #include "manage_sql.h"
30 #include "manage_port_lists.h"
31 #include "manage_report_formats.h"
32 #include "manage_sql_secinfo.h"
33 #include "manage_sql_nvts.h"
34 #include "manage_tickets.h"
35 #include "manage_sql_configs.h"
36 #include "manage_sql_port_lists.h"
37 #include "manage_sql_report_formats.h"
38 #include "manage_sql_tickets.h"
39 #include "manage_sql_tls_certificates.h"
40 #include "manage_acl.h"
41 #include "manage_authentication.h"
42 #include "lsc_user.h"
43 #include "sql.h"
44 #include "utils.h"
45 /* TODO This is for buffer_get_filter_xml, for print_report_xml_start.  We
46  *      should not be generating XML in here, that should be done in gmp_*.c. */
47 #include "gmp_get.h"
48 
49 #include <arpa/inet.h>
50 #include <assert.h>
51 #include <ctype.h>
52 #include <dirent.h>
53 #include <errno.h>
54 #include <glib/gstdio.h>
55 #include <gnutls/x509.h>
56 #include <pwd.h>
57 #include <stdlib.h>
58 #include <sys/socket.h>
59 #include <sys/stat.h>
60 #include <sys/types.h>
61 #include <sys/un.h>
62 #include <sys/wait.h>
63 #include <unistd.h>
64 #include <sys/time.h>
65 #include <grp.h>
66 #include <gpgme.h>
67 
68 #include <gvm/base/hosts.h>
69 #include <gvm/base/pwpolicy.h>
70 #include <gvm/base/logging.h>
71 #include <gvm/base/proctitle.h>
72 #include <gvm/util/fileutils.h>
73 #include <gvm/util/gpgmeutils.h>
74 #include <gvm/util/serverutils.h>
75 #include <gvm/util/uuidutils.h>
76 #include <gvm/util/radiusutils.h>
77 #include <gvm/util/sshutils.h>
78 #include <gvm/util/authutils.h>
79 #include <gvm/util/ldaputils.h>
80 #include <gvm/gmp/gmp.h>
81 
82 #undef G_LOG_DOMAIN
83 /**
84  * @brief GLib log domain.
85  */
86 #define G_LOG_DOMAIN "md manage"
87 
88 #ifdef DEBUG_FUNCTION_NAMES
89 #include <dlfcn.h>
90 
91 void __cyg_profile_func_enter (void *, void *)
92    __attribute__((no_instrument_function));
93 
94 void
__cyg_profile_func_enter(void * func,void * caller)95 __cyg_profile_func_enter (void *func, void *caller)
96 {
97   Dl_info info;
98 
99   if (dladdr (func, &info))
100       g_debug ("TTT: enter %p %s",
101                (int*) func,
102                info.dli_sname ? info.dli_sname : "?");
103   else
104       g_debug ("TTT: enter %p", (int*) func);
105 }
106 
107 void __cyg_profile_func_exit (void *, void *)
108    __attribute__((no_instrument_function));
109 
110 void
__cyg_profile_func_exit(void * func,void * caller)111 __cyg_profile_func_exit (void *func, void *caller)
112 {
113   Dl_info info;
114 
115   if (dladdr (func, &info))
116       g_debug ("TTT: exit  %p %s",
117                (int*) func,
118                info.dli_sname ? info.dli_sname : "?");
119   else
120       g_debug ("TTT: exit  %p", (int*) func);
121 }
122 #endif
123 
124 
125 /* Headers from backend specific manage_xxx.c file. */
126 
127 int
128 manage_create_sql_functions ();
129 
130 void
131 create_tables ();
132 
133 void
134 check_db_sequences ();
135 
136 int
137 check_db_extensions ();
138 
139 static int
140 check_db_encryption_key ();
141 
142 void
143 manage_attach_databases ();
144 
145 
146 /* Headers for symbols defined in manage.c which are private to libmanage. */
147 
148 /**
149  * @brief Flag to force authentication to succeed.
150  *
151  * 1 if set via scheduler, 2 if set via event, else 0.
152  */
153 extern int authenticate_allow_all;
154 
155 const char *threat_message_type (const char *);
156 
157 int delete_reports (task_t);
158 
159 int
160 stop_task_internal (task_t);
161 
162 int
163 validate_username (const gchar *);
164 
165 void
166 set_task_interrupted (task_t, const gchar *);
167 
168 
169 /* Static headers. */
170 
171 static int
172 report_counts_cache_exists (report_t, int, int);
173 
174 static void report_severity_data (report_t, const char *, const get_data_t *,
175                                   severity_data_t*, severity_data_t*);
176 
177 static int cache_report_counts (report_t, int, int, severity_data_t*);
178 
179 static char*
180 task_owner_uuid (task_t);
181 
182 gchar*
183 clean_hosts (const char *, int*);
184 
185 static gboolean
186 find_user_by_name (const char *, user_t *user);
187 
188 static gboolean
189 find_role_with_permission (const char *, role_t *, const char *);
190 
191 static int
192 user_ensure_in_db (const gchar *, const gchar *);
193 
194 static int
195 set_password (const gchar *, const gchar *, const gchar *, gchar **);
196 
197 static void
198 permissions_set_subjects (const char *, resource_t, resource_t, int);
199 
200 static resource_t
201 permission_resource (permission_t);
202 
203 static resource_t
204 permission_subject (permission_t);
205 
206 static char *
207 permission_subject_type (permission_t);
208 
209 static int
210 role_is_predefined (role_t);
211 
212 static int
213 role_is_predefined_id (const char *);
214 
215 static int
216 task_second_last_report (task_t, report_t *);
217 
218 static gchar *
219 new_secinfo_message (event_t, const void*, alert_t);
220 
221 static gchar *
222 new_secinfo_list (event_t, const void*, alert_t, int*);
223 
224 static void
225 check_for_new_scap ();
226 
227 static void
228 check_for_new_cert ();
229 
230 static void
231 check_for_updated_scap ();
232 
233 static void
234 check_for_updated_cert ();
235 
236 static int
237 report_counts_id_full (report_t, int *, int *, int *, int *, int *,
238                        double *, const get_data_t*, const char* ,
239                        int *, int *, int *, int *, int *, double *);
240 
241 static gboolean
242 find_group_with_permission (const char *, group_t *, const char *);
243 
244 static gchar*
245 vulns_extra_where ();
246 
247 static int
248 task_last_report_any_status (task_t, report_t *);
249 
250 static int
251 task_report_previous (task_t task, report_t, report_t *);
252 
253 static gboolean
254 find_trash_task (const char*, task_t*);
255 
256 static gboolean
257 find_trash_report_with_permission (const char *, report_t *, const char *);
258 
259 static int
260 cleanup_schedule_times ();
261 
262 static char *
263 permission_name (permission_t);
264 
265 static void
266 cache_permissions_for_resource (const char *, resource_t, GArray*);
267 
268 static void
269 cache_all_permissions_for_users (GArray*);
270 
271 static void
272 report_cache_counts (report_t, int, int, const char*);
273 
274 static int
275 report_host_dead (report_host_t);
276 
277 static int
278 report_host_result_count (report_host_t);
279 
280 static int
281 set_credential_data (credential_t, const char*, const char*);
282 
283 static void
284 set_credential_name (credential_t, const char *);
285 
286 static void
287 set_credential_comment (credential_t, const char *);
288 
289 static void
290 set_credential_login (credential_t, const char *);
291 
292 static void
293 set_credential_certificate (credential_t, const char *);
294 
295 static void
296 set_credential_auth_algorithm (credential_t, const char *);
297 
298 static void
299 set_credential_private_key (credential_t, const char *, const char *);
300 
301 static void
302 set_credential_password (credential_t, const char *);
303 
304 static void
305 set_credential_snmp_secret (credential_t, const char *, const char *,
306                             const char *);
307 
308 static int
309 setting_value_int (const char *, int *);
310 
311 static int
312 setting_auto_cache_rebuild_int ();
313 
314 static double
315 setting_default_severity_dbl ();
316 
317 static int
318 setting_dynamic_severity_int ();
319 
320 static char *
321 setting_timezone ();
322 
323 static double
324 task_severity_double (task_t, int, int, int);
325 
326 static char*
327 target_comment (target_t);
328 
329 static column_t *
330 type_select_columns (const char *type);
331 
332 static column_t *
333 type_where_columns (const char *type);
334 
335 static char*
336 trash_filter_uuid (filter_t);
337 
338 static char*
339 trash_filter_name (filter_t);
340 
341 static char*
342 trash_target_comment (target_t);
343 
344 static int
345 user_resources_in_use (user_t,
346                        const char *, int(*)(resource_t),
347                        const char *, int(*)(resource_t));
348 
349 static const char**
350 type_filter_columns (const char *);
351 
352 static int
353 type_build_select (const char *, const char *, const get_data_t *,
354                    gboolean, gboolean, const char *, const char *,
355                    const char *, gchar **);
356 
357 
358 /* Variables. */
359 
360 /**
361  * @brief Function to fork a connection that will accept GMP requests.
362  */
363 static manage_connection_forker_t manage_fork_connection;
364 
365 /**
366  * @brief Max number of hosts per target.
367  */
368 static int max_hosts = MANAGE_MAX_HOSTS;
369 
370 /**
371  * @brief Default max number of bytes of reports included in email alerts.
372  */
373 #define MAX_CONTENT_LENGTH 20000
374 
375 /**
376  * @brief Maximum number of bytes of reports included in email alerts.
377  *
378  * A value less or equal to 0 allows any size.
379  */
380 static int max_content_length = MAX_CONTENT_LENGTH;
381 
382 /**
383  * @brief Default max number of bytes of reports attached to email alerts.
384  */
385 #define MAX_ATTACH_LENGTH 1048576
386 
387 /**
388  * @brief Maximum number of bytes of reports attached to email alerts.
389  *
390  * A value less or equal to 0 allows any size.
391  */
392 static int max_attach_length = MAX_ATTACH_LENGTH;
393 
394 /**
395  * @brief Default max number of bytes of user-defined message in email alerts.
396  */
397 #define MAX_EMAIL_MESSAGE_LENGTH 2000
398 
399 /**
400  * @brief Maximum number of bytes of user-defined message text in email alerts.
401  *
402  * A value less or equal to 0 allows any size.
403  */
404 static int max_email_message_length = MAX_EMAIL_MESSAGE_LENGTH;
405 
406 /**
407  * @brief Memory cache of NVT information from the database.
408  */
409 static nvtis_t* nvti_cache = NULL;
410 
411 /**
412  * @brief Name of the database file.
413  */
414 db_conn_info_t gvmd_db_conn_info = { NULL, NULL, NULL };
415 
416 /**
417  * @brief Whether a transaction has been opened and not committed yet.
418  */
419 static gboolean in_transaction;
420 
421 /**
422  * @brief Time of reception of the currently processed message.
423  */
424 static struct timeval last_msg;
425 
426 /**
427  * @brief The VT verification collation override
428  */
429 static gchar *vt_verification_collation = NULL;
430 
431 /* GMP commands. */
432 
433 /**
434  * @brief The GMP command list.
435  */
436 command_t gmp_commands[]
437  = {{"AUTHENTICATE", "Authenticate with the manager." },
438     {"CREATE_ALERT", "Create an alert."},
439     {"CREATE_ASSET", "Create an asset."},
440     {"CREATE_CONFIG", "Create a config."},
441     {"CREATE_CREDENTIAL", "Create a credential."},
442     {"CREATE_FILTER", "Create a filter."},
443     {"CREATE_GROUP", "Create a group."},
444     {"CREATE_NOTE", "Create a note."},
445     {"CREATE_OVERRIDE", "Create an override."},
446     {"CREATE_PERMISSION", "Create a permission."},
447     {"CREATE_PORT_LIST", "Create a port list."},
448     {"CREATE_PORT_RANGE", "Create a port range in a port list."},
449     {"CREATE_REPORT", "Create a report."},
450     {"CREATE_REPORT_FORMAT", "Create a report format."},
451     {"CREATE_ROLE", "Create a role."},
452     {"CREATE_SCANNER", "Create a scanner."},
453     {"CREATE_SCHEDULE", "Create a schedule."},
454     {"CREATE_TAG", "Create a tag."},
455     {"CREATE_TARGET", "Create a target."},
456     {"CREATE_TASK", "Create a task."},
457     {"CREATE_TICKET", "Create a ticket."},
458     {"CREATE_TLS_CERTIFICATE", "Create a TLS certificate."},
459     {"CREATE_USER", "Create a new user."},
460     {"DELETE_ALERT", "Delete an alert."},
461     {"DELETE_ASSET", "Delete an asset."},
462     {"DELETE_CONFIG", "Delete a config."},
463     {"DELETE_CREDENTIAL", "Delete a credential."},
464     {"DELETE_FILTER", "Delete a filter."},
465     {"DELETE_GROUP", "Delete a group."},
466     {"DELETE_NOTE", "Delete a note."},
467     {"DELETE_OVERRIDE", "Delete an override."},
468     {"DELETE_PERMISSION", "Delete a permission."},
469     {"DELETE_PORT_LIST", "Delete a port list."},
470     {"DELETE_PORT_RANGE", "Delete a port range."},
471     {"DELETE_REPORT", "Delete a report."},
472     {"DELETE_REPORT_FORMAT", "Delete a report format."},
473     {"DELETE_ROLE", "Delete a role."},
474     {"DELETE_SCANNER", "Delete a scanner."},
475     {"DELETE_SCHEDULE", "Delete a schedule."},
476     {"DELETE_TAG", "Delete a tag."},
477     {"DELETE_TARGET", "Delete a target."},
478     {"DELETE_TASK", "Delete a task."},
479     {"DELETE_TICKET", "Delete a ticket."},
480     {"DELETE_TLS_CERTIFICATE", "Delete a TLS certificate."},
481     {"DELETE_USER", "Delete an existing user."},
482     {"DESCRIBE_AUTH", "Get details about the used authentication methods."},
483     {"EMPTY_TRASHCAN", "Empty the trashcan."},
484     {"GET_AGGREGATES", "Get aggregates of resources."},
485     {"GET_ALERTS", "Get all alerts."},
486     {"GET_ASSETS", "Get all assets."},
487     {"GET_CONFIGS", "Get all configs."},
488     {"GET_CREDENTIALS", "Get all credentials."},
489     {"GET_FEEDS", "Get details of one or all feeds this Manager uses."},
490     {"GET_FILTERS", "Get all filters."},
491     {"GET_GROUPS", "Get all groups."},
492     {"GET_INFO", "Get raw information for a given item."},
493     {"GET_NOTES", "Get all notes."},
494     {"GET_NVTS", "Get one or all available NVTs."},
495     {"GET_NVT_FAMILIES", "Get a list of all NVT families."},
496     {"GET_OVERRIDES", "Get all overrides."},
497     {"GET_PERMISSIONS", "Get all permissions."},
498     {"GET_PORT_LISTS", "Get all port lists."},
499     {"GET_PREFERENCES", "Get preferences for all available NVTs."},
500     {"GET_REPORTS", "Get all reports."},
501     {"GET_REPORT_FORMATS", "Get all report formats."},
502     {"GET_RESULTS", "Get results."},
503     {"GET_ROLES", "Get all roles."},
504     {"GET_SCANNERS", "Get all scanners."},
505     {"GET_SCHEDULES", "Get all schedules."},
506     {"GET_SETTINGS", "Get all settings."},
507     {"GET_SYSTEM_REPORTS", "Get all system reports."},
508     {"GET_TAGS", "Get all tags."},
509     {"GET_TARGETS", "Get all targets."},
510     {"GET_TASKS", "Get all tasks."},
511     {"GET_TICKETS", "Get all tickets."},
512     {"GET_TLS_CERTIFICATES", "Get all TLS certificates."},
513     {"GET_USERS", "Get all users."},
514     {"GET_VERSION", "Get the Greenbone Management Protocol version."},
515     {"GET_VULNS", "Get all vulnerabilities."},
516     {"HELP", "Get this help text."},
517     {"MODIFY_ALERT", "Modify an existing alert."},
518     {"MODIFY_ASSET", "Modify an existing asset."},
519     {"MODIFY_AUTH", "Modify the authentication methods."},
520     {"MODIFY_CONFIG", "Update an existing config."},
521     {"MODIFY_CREDENTIAL", "Modify an existing credential."},
522     {"MODIFY_FILTER", "Modify an existing filter."},
523     {"MODIFY_GROUP", "Modify an existing group."},
524     {"MODIFY_NOTE", "Modify an existing note."},
525     {"MODIFY_OVERRIDE", "Modify an existing override."},
526     {"MODIFY_PERMISSION", "Modify an existing permission."},
527     {"MODIFY_PORT_LIST", "Modify an existing port list."},
528     {"MODIFY_REPORT_FORMAT", "Modify an existing report format."},
529     {"MODIFY_ROLE", "Modify an existing role."},
530     {"MODIFY_SCANNER", "Modify an existing scanner."},
531     {"MODIFY_SCHEDULE", "Modify an existing schedule."},
532     {"MODIFY_SETTING", "Modify an existing setting."},
533     {"MODIFY_TAG", "Modify an existing tag."},
534     {"MODIFY_TARGET", "Modify an existing target."},
535     {"MODIFY_TASK", "Update an existing task."},
536     {"MODIFY_TICKET", "Modify an existing ticket."},
537     {"MODIFY_TLS_CERTIFICATE", "Modify an existing TLS certificate."},
538     {"MODIFY_USER", "Modify a user."},
539     {"MOVE_TASK", "Assign task to another slave scanner, even while running."},
540     {"RESTORE", "Restore a resource."},
541     {"RESUME_TASK", "Resume a stopped task."},
542     {"RUN_WIZARD", "Run a wizard."},
543     {"START_TASK", "Manually start an existing task."},
544     {"STOP_TASK", "Stop a running task."},
545     {"SYNC_CONFIG", "Synchronize a config with a scanner."},
546     {"TEST_ALERT", "Run an alert."},
547     {"VERIFY_REPORT_FORMAT", "Verify a report format."},
548     {"VERIFY_SCANNER", "Verify a scanner."},
549     {NULL, NULL}};
550 
551 /**
552  * @brief Check whether a command name is valid.
553  *
554  * @param[in]  name  Command name.
555  *
556  * @return 1 yes, 0 no.
557  */
558 int
valid_gmp_command(const char * name)559 valid_gmp_command (const char* name)
560 {
561   command_t *command;
562   command = gmp_commands;
563   while (command[0].name)
564     if (strcasecmp (command[0].name, name) == 0)
565       return 1;
566     else
567       command++;
568   return 0;
569 }
570 
571 /**
572  * @brief Get the type associated with a GMP command.
573  *
574  * @param[in]  name  Command name.
575  *
576  * @return Freshly allocated type name if any, else NULL.
577  */
578 static gchar *
gmp_command_type(const char * name)579 gmp_command_type (const char* name)
580 {
581   const char *under;
582   under = strchr (name, '_');
583   if (under && (strlen (under) > 1))
584     {
585       gchar *command;
586       under++;
587       command = g_strdup (under);
588       if (command[strlen (command) - 1] == 's')
589         command[strlen (command) - 1] = '\0';
590       if (valid_type (command))
591         return command;
592       g_free (command);
593     }
594   return NULL;
595 }
596 
597 /**
598  * @brief Check whether a GMP command takes a resource.
599  *
600  * MODIFY_TARGET, for example, takes a target.
601  *
602  * @param[in]  name  Command name.
603  *
604  * @return 1 if takes resource, else 0.
605  */
606 static int
gmp_command_takes_resource(const char * name)607 gmp_command_takes_resource (const char* name)
608 {
609   assert (name);
610   return strcasecmp (name, "AUTHENTICATE")
611          && strcasestr (name, "CREATE_") != name
612          && strcasestr (name, "DESCRIBE_") != name
613          && strcasecmp (name, "EMPTY_TRASHCAN")
614          && strcasecmp (name, "GET_VERSION")
615          && strcasecmp (name, "HELP")
616          && strcasecmp (name, "RUN_WIZARD")
617          && strcasestr (name, "SYNC_") != name;
618 }
619 
620 
621 /* General helpers. */
622 
623 /**
624  * @brief Check if a resource with a certain name exists already.
625  *
626  * Conflicting resource can be global or owned by the current user.
627  *
628  * @param[in]   name      Name of resource to check for.
629  * @param[in]   type      Type of resource.
630  * @param[in]   resource  Resource to ignore, 0 otherwise.
631  *
632  * @return Whether resource with name exists.
633  */
634 gboolean
resource_with_name_exists(const char * name,const char * type,resource_t resource)635 resource_with_name_exists (const char *name, const char *type,
636                            resource_t resource)
637 {
638   int ret;
639   char *quoted_name, *quoted_type;
640 
641   assert (type);
642   if (!name)
643     return 0;
644   quoted_name = sql_quote (name);
645   quoted_type = sql_quote (type);
646   if (resource)
647     ret = sql_int ("SELECT COUNT(*) FROM %ss"
648                    " WHERE name = '%s'"
649                    " AND id != %llu"
650                    " AND " ACL_USER_OWNS () ";",
651                    quoted_type, quoted_name, resource,
652                    current_credentials.uuid);
653   else
654     ret = sql_int ("SELECT COUNT(*) FROM %ss"
655                    " WHERE name = '%s'"
656                    " AND " ACL_USER_OWNS () ";",
657                    quoted_type, quoted_name, current_credentials.uuid);
658 
659   g_free (quoted_name);
660   g_free (quoted_type);
661   return !!ret;
662 }
663 
664 /**
665  * @brief Check if a resource with a certain name exists already.
666  *
667  * Conflicting resource can be owned by anybody.
668  *
669  * @param[in]   name      Name of resource to check for.
670  * @param[in]   type      Type of resource.
671  * @param[in]   resource  Resource to ignore, 0 otherwise.
672  *
673  * @return Whether resource with name exists.
674  */
675 static gboolean
resource_with_name_exists_global(const char * name,const char * type,resource_t resource)676 resource_with_name_exists_global (const char *name, const char *type,
677                                   resource_t resource)
678 {
679   int ret;
680   char *quoted_name, *quoted_type;
681 
682   assert (type);
683   if (!name)
684     return 0;
685   quoted_name = sql_quote (name);
686   quoted_type = sql_quote (type);
687   if (resource)
688     ret = sql_int ("SELECT COUNT(*) FROM %ss"
689                    " WHERE name = '%s'"
690                    " AND id != %llu;",
691                    quoted_type, quoted_name, resource);
692   else
693     ret = sql_int ("SELECT COUNT(*) FROM %ss"
694                    " WHERE name = '%s';",
695                    quoted_type, quoted_name);
696 
697   g_free (quoted_name);
698   g_free (quoted_type);
699   return !!ret;
700 }
701 
702 /**
703  * @brief Ensure a string is in an array.
704  *
705  * @param[in]  array   Array.
706  * @param[in]  string  String.  Copied into array.
707  */
708 static void
array_add_new_string(array_t * array,const gchar * string)709 array_add_new_string (array_t *array, const gchar *string)
710 {
711   guint index;
712   for (index = 0; index < array->len; index++)
713     if (strcmp (g_ptr_array_index (array, index), string) == 0)
714       return;
715   array_add (array, g_strdup (string));
716 }
717 
718 /**
719  * @brief Find a resource in the trashcan given a UUID.
720  *
721  * @param[in]   type      Type of resource.
722  * @param[in]   uuid      UUID of resource.
723  * @param[out]  resource  Resource return, 0 if successfully failed to find
724  *                        resource.
725  *
726  * @return FALSE on success (including if failed to find resource), TRUE on
727  *         error.
728  */
729 gboolean
find_trash(const char * type,const char * uuid,resource_t * resource)730 find_trash (const char *type, const char *uuid, resource_t *resource)
731 {
732   gchar *quoted_uuid;
733 
734   if (!uuid)
735     return FALSE;
736   quoted_uuid = sql_quote (uuid);
737   if (acl_user_owns_trash_uuid (type, quoted_uuid) == 0)
738     {
739       g_free (quoted_uuid);
740       *resource = 0;
741       return FALSE;
742     }
743   switch (sql_int64 (resource,
744                      "SELECT id FROM %ss_trash WHERE uuid = '%s';",
745                      type,
746                      quoted_uuid))
747     {
748       case 0:
749         break;
750       case 1:        /* Too few rows in result of query. */
751         *resource = 0;
752         break;
753       default:       /* Programming error. */
754         assert (0);
755       case -1:
756         g_free (quoted_uuid);
757         return TRUE;
758         break;
759     }
760 
761   g_free (quoted_uuid);
762   return FALSE;
763 }
764 
765 /**
766  * @brief Convert an ISO time into seconds since epoch.
767  *
768  * If no offset is specified, the timezone of the current user is used.
769  * If there is no current user timezone, UTC is used.
770  *
771  * @param[in]  text_time  Time as text in ISO format: 2011-11-03T09:23:28+02:00.
772  *
773  * @return Time since epoch.  0 on error.
774  */
775 int
parse_iso_time(const char * text_time)776 parse_iso_time (const char *text_time)
777 {
778   return parse_iso_time_tz (text_time, current_credentials.timezone);
779 }
780 
781 /**
782  * @brief Find a string in an array.
783  *
784  * @param[in]  array   Array.
785  * @param[in]  string  String.
786  *
787  * @return The string from the array if found, else NULL.
788  */
789 static gchar*
array_find_string(array_t * array,const gchar * string)790 array_find_string (array_t *array, const gchar *string)
791 {
792   guint index;
793   for (index = 0; index < array->len; index++)
794     {
795       gchar *ele;
796       ele = (gchar*) g_ptr_array_index (array, index);
797       if (ele && (strcmp (ele, string) == 0))
798         return ele;
799     }
800   return NULL;
801 }
802 
803 /**
804  * @brief Find a string in a glib style string vector.
805  *
806  * @param[in]  vector  Vector.
807  * @param[in]  string  String.
808  *
809  * @return The string from the vector if found, else NULL.
810  */
811 static const gchar*
vector_find_string(const gchar ** vector,const gchar * string)812 vector_find_string (const gchar **vector, const gchar *string)
813 {
814   if (vector == NULL)
815     return NULL;
816   while (*vector)
817     if (strcmp (*vector, string) == 0)
818       return *vector;
819     else
820       vector++;
821   return NULL;
822 }
823 
824 /**
825  * @brief Find a filter string in a glib style string vector.
826  *
827  * @param[in]  vector  Vector.
828  * @param[in]  string  String.
829  *
830  * @return 1 if found, 2 if found with underscore prefix, else NULL.
831  */
832 static int
vector_find_filter(const gchar ** vector,const gchar * string)833 vector_find_filter (const gchar **vector, const gchar *string)
834 {
835   gchar *underscore;
836   if (vector_find_string (vector, string))
837     return 1;
838   underscore = g_strdup_printf ("_%s", string);
839   if (vector_find_string (vector, underscore))
840     {
841       g_free (underscore);
842       return 2;
843     }
844   g_free (underscore);
845   return 0;
846 }
847 
848 /**
849  * @brief Get last time NVT alerts were checked.
850  *
851  * @return Last check time.
852  */
853 static int
nvts_check_time()854 nvts_check_time ()
855 {
856   return sql_int ("SELECT"
857                   " CASE WHEN EXISTS (SELECT * FROM meta"
858                   "                   WHERE name = 'nvts_check_time')"
859                   "      THEN CAST ((SELECT value FROM meta"
860                   "                  WHERE name = 'nvts_check_time')"
861                   "                 AS INTEGER)"
862                   "      ELSE 0"
863                   "      END;");
864 }
865 
866 /**
867  * @brief Get last time SCAP SecInfo alerts were checked.
868  *
869  * @return Last time SCAP was checked.
870  */
871 static int
scap_check_time()872 scap_check_time ()
873 {
874   return sql_int ("SELECT"
875                   " CASE WHEN EXISTS (SELECT * FROM meta"
876                   "                   WHERE name = 'scap_check_time')"
877                   "      THEN CAST ((SELECT value FROM meta"
878                   "                  WHERE name = 'scap_check_time')"
879                   "                 AS INTEGER)"
880                   "      ELSE 0"
881                   "      END;");
882 }
883 
884 /**
885  * @brief Get last time CERT SecInfo alerts were checked.
886  *
887  * @return Last time CERT was checked.
888  */
889 static int
cert_check_time()890 cert_check_time ()
891 {
892   return sql_int ("SELECT"
893                   " CASE WHEN EXISTS (SELECT * FROM meta"
894                   "                   WHERE name = 'cert_check_time')"
895                   "      THEN CAST ((SELECT value FROM meta"
896                   "                  WHERE name = 'cert_check_time')"
897                   "                 AS INTEGER)"
898                   "      ELSE 0"
899                   "      END;");
900 }
901 
902 /**
903  * @brief Setup for an option process.
904  *
905  * @param[in]  log_config  Log configuration.
906  * @param[in]  database    Database.
907  *
908  * @return 0 success, -1 error, -2 database is wrong version,
909  *         -3 database needs to be initialised from server.
910  */
911 int
manage_option_setup(GSList * log_config,const db_conn_info_t * database)912 manage_option_setup (GSList *log_config, const db_conn_info_t *database)
913 {
914   int ret;
915 
916   if (gvm_auth_init ())
917     {
918       fprintf (stderr, "Authentication init failed\n");
919       return -1;
920     }
921 
922   ret = init_manage_helper (log_config, database,
923                             MANAGE_ABSOLUTE_MAX_IPS_PER_TARGET);
924   assert (ret != -4);
925   switch (ret)
926     {
927       case 0:
928         break;
929       case -2:
930         fprintf (stderr, "Database is wrong version.\n");
931         return ret;
932       case -3:
933         fprintf (stderr, "Database must be initialised.\n");
934         return ret;
935       default:
936         fprintf (stderr, "Internal error.\n");
937         return ret;
938     }
939 
940   init_manage_process (database);
941 
942   return 0;
943 }
944 
945 /**
946  * @brief Cleanup for an option process.
947  */
948 void
manage_option_cleanup()949 manage_option_cleanup ()
950 {
951   cleanup_manage_process (TRUE);
952 }
953 
954 /**
955  * @brief Copy an array of columns.
956  *
957  * @param[in]  columns  Columns.
958  *
959  * @return Freshly allocated array.
960  */
961 static column_t *
column_array_copy(column_t * columns)962 column_array_copy (column_t *columns)
963 {
964   column_t *point, *start;
965   int count;
966 
967   count = 1;
968   point = columns;
969   while (point->select)
970     {
971       point++;
972       count++;
973     }
974 
975   g_debug ("%s: %i", __func__, count);
976   start = g_malloc0 (sizeof (column_t) * count);
977   point = start;
978 
979   while (columns->select)
980     {
981       point->select = g_strdup (columns->select);
982       point->filter = columns->filter ? g_strdup (columns->filter) : NULL;
983       point->type = columns->type;
984       point++;
985       columns++;
986     }
987   return start;
988 }
989 
990 /**
991  * @brief Free an array of columns.
992  *
993  * @param[in]  columns  Columns.
994  */
995 static void
column_array_free(column_t * columns)996 column_array_free (column_t *columns)
997 {
998   while (columns->filter)
999     {
1000       g_free (columns->select);
1001       g_free (columns->filter);
1002       columns++;
1003     }
1004   g_free (columns);
1005 }
1006 
1007 /**
1008  * @brief Set the select clause of a column in an array of columns.
1009  *
1010  * Frees the existing select clause.
1011  *
1012  * @param[in]  columns  Columns.
1013  * @param[in]  filter   Filter term name.
1014  * @param[in]  select   Select clause.
1015  */
1016 static void
column_array_set(column_t * columns,const gchar * filter,gchar * select)1017 column_array_set (column_t *columns, const gchar *filter, gchar *select)
1018 {
1019   while (columns->select)
1020     {
1021       if (columns->filter && (strcmp (columns->filter, filter) == 0))
1022         {
1023           g_free (columns->select);
1024           columns->select = select;
1025           break;
1026         }
1027       columns++;
1028     }
1029 }
1030 
1031 
1032 /* Filter utilities. */
1033 
1034 /**
1035  * @brief Get the symbol of a keyword relation.
1036  *
1037  * @param[in]  relation  Relation.
1038  *
1039  * @return Relation symbol.
1040  */
1041 const char *
keyword_relation_symbol(keyword_relation_t relation)1042 keyword_relation_symbol (keyword_relation_t relation)
1043 {
1044   switch (relation)
1045     {
1046       case KEYWORD_RELATION_APPROX:        return "~";
1047       case KEYWORD_RELATION_COLUMN_ABOVE:  return ">";
1048       case KEYWORD_RELATION_COLUMN_APPROX: return "~";
1049       case KEYWORD_RELATION_COLUMN_EQUAL:  return "=";
1050       case KEYWORD_RELATION_COLUMN_BELOW:  return "<";
1051       case KEYWORD_RELATION_COLUMN_REGEXP: return ":";
1052       default:                             return "";
1053     }
1054 }
1055 
1056 /**
1057  * @brief Free a keyword.
1058  *
1059  * @param[in]  keyword  Filter keyword.
1060  */
1061 static void
keyword_free(keyword_t * keyword)1062 keyword_free (keyword_t* keyword)
1063 {
1064   g_free (keyword->string);
1065   g_free (keyword->column);
1066 }
1067 
1068 /**
1069  * @brief Get whether a keyword is special (like "and").
1070  *
1071  * @param[in]  keyword  Keyword.
1072  *
1073  * @return 1 if special, else 0.
1074  */
1075 int
keyword_special(keyword_t * keyword)1076 keyword_special (keyword_t *keyword)
1077 {
1078   if (keyword->string)
1079     return (strcmp (keyword->string, "and") == 0)
1080            || (strcmp (keyword->string, "or") == 0)
1081            || (strcmp (keyword->string, "not") == 0)
1082            || (strcmp (keyword->string, "re") == 0)
1083            || (strcmp (keyword->string, "regexp") == 0);
1084   return 0;
1085 }
1086 
1087 /**
1088  * @brief Parse a filter column relation.
1089  *
1090  * @param[in]  relation  Filter relation.
1091  *
1092  * @return keyword relation
1093  */
1094 static keyword_relation_t
parse_column_relation(const char relation)1095 parse_column_relation (const char relation)
1096 {
1097   switch (relation)
1098     {
1099       case '=': return KEYWORD_RELATION_COLUMN_EQUAL;
1100       case '~': return KEYWORD_RELATION_COLUMN_APPROX;
1101       case '>': return KEYWORD_RELATION_COLUMN_ABOVE;
1102       case '<': return KEYWORD_RELATION_COLUMN_BELOW;
1103       case ':': return KEYWORD_RELATION_COLUMN_REGEXP;
1104       default:  return KEYWORD_RELATION_COLUMN_APPROX;
1105     }
1106 }
1107 
1108 /**
1109  * @brief Parse a filter keyword.
1110  *
1111  * @param[in]  keyword  Filter keyword.
1112  */
1113 static void
parse_keyword(keyword_t * keyword)1114 parse_keyword (keyword_t* keyword)
1115 {
1116   gchar *string;
1117   int digits;
1118 
1119   if (keyword->column == NULL && keyword->equal == 0)
1120     {
1121       keyword->relation = KEYWORD_RELATION_APPROX;
1122       keyword->type = KEYWORD_TYPE_STRING;
1123       return;
1124     }
1125 
1126   /* Special values to substitute */
1127 
1128   if (keyword->column
1129       && (strcasecmp (keyword->column, "severity") == 0
1130           || strcasecmp (keyword->column, "new_severity") == 0))
1131     {
1132       if (strcasecmp (keyword->string, "Log") == 0)
1133         {
1134           keyword->double_value = SEVERITY_LOG;
1135           keyword->type = KEYWORD_TYPE_DOUBLE;
1136           return;
1137         }
1138       if (strcasecmp (keyword->string, "False Positive") == 0)
1139         {
1140           keyword->double_value = SEVERITY_FP;
1141           keyword->type = KEYWORD_TYPE_DOUBLE;
1142           return;
1143         }
1144       else if (strcasecmp (keyword->string, "Error") == 0)
1145         {
1146           keyword->double_value = SEVERITY_ERROR;
1147           keyword->type = KEYWORD_TYPE_DOUBLE;
1148           return;
1149         }
1150     }
1151 
1152   /* The type. */
1153 
1154   string = keyword->string;
1155   if (*string == '\0')
1156     {
1157       keyword->type = KEYWORD_TYPE_STRING;
1158       return;
1159     }
1160   if (*string && *string == '-' && strlen (string) > 1) string++;
1161   digits = 0;
1162   while (*string && isdigit (*string))
1163     {
1164       digits = 1;
1165       string++;
1166     }
1167   if (digits == 0)
1168     keyword->type = KEYWORD_TYPE_STRING;
1169   else if (*string)
1170     {
1171       struct tm date;
1172       gchar next;
1173       int parsed_integer;
1174       double parsed_double;
1175       char dummy[2];
1176       memset (&date, 0, sizeof (date));
1177       next = *(string + 1);
1178       if (next == '\0' && *string == 's')
1179         {
1180           time_t now;
1181           now = time (NULL);
1182           keyword->integer_value = now + atoi (keyword->string);
1183           keyword->type = KEYWORD_TYPE_INTEGER;
1184         }
1185       else if (next == '\0' && *string == 'm')
1186         {
1187           time_t now;
1188           now = time (NULL);
1189           keyword->integer_value = now + (atoi (keyword->string) * 60);
1190           keyword->type = KEYWORD_TYPE_INTEGER;
1191         }
1192       else if (next == '\0' && *string == 'h')
1193         {
1194           time_t now;
1195           now = time (NULL);
1196           keyword->integer_value = now + (atoi (keyword->string) * 3600);
1197           keyword->type = KEYWORD_TYPE_INTEGER;
1198         }
1199       else if (next == '\0' && *string == 'd')
1200         {
1201           time_t now;
1202           now = time (NULL);
1203           keyword->integer_value = now + (atoi (keyword->string) * 86400);
1204           keyword->type = KEYWORD_TYPE_INTEGER;
1205         }
1206       else if (next == '\0' && *string == 'w')
1207         {
1208           time_t now;
1209           now = time (NULL);
1210           keyword->integer_value = now + atoi (keyword->string) * 604800;
1211           keyword->type = KEYWORD_TYPE_INTEGER;
1212         }
1213       else if (next == '\0' && *string == 'M')
1214         {
1215           time_t now;
1216           now = time (NULL);
1217           keyword->integer_value = add_months (now, atoi (keyword->string));
1218           keyword->type = KEYWORD_TYPE_INTEGER;
1219         }
1220       else if (next == '\0' && *string == 'y')
1221         {
1222           time_t now;
1223           now = time (NULL);
1224           keyword->integer_value = add_months (now,
1225                                                atoi (keyword->string) * 12);
1226           keyword->type = KEYWORD_TYPE_INTEGER;
1227         }
1228       // Add cases for t%H:%M although it is incorrect sometimes it is easier
1229       // to call filter.lower on the frontend then it can happen that the
1230       // T indicator is lowered as well.
1231       else if (strptime (keyword->string, "%Y-%m-%dt%H:%M", &date))
1232         {
1233           keyword->integer_value = mktime (&date);
1234           keyword->type = KEYWORD_TYPE_INTEGER;
1235           g_debug ("Parsed Y-m-dtH:M %s to timestamp %d.",
1236                    keyword->string, keyword->integer_value);
1237         }
1238       else if (strptime (keyword->string, "%Y-%m-%dt%Hh%M", &date))
1239         {
1240           keyword->integer_value = mktime (&date);
1241           keyword->type = KEYWORD_TYPE_INTEGER;
1242           g_debug ("Parsed Y-m-dtHhM %s to timestamp %d.",
1243                    keyword->string, keyword->integer_value);
1244         }
1245       else if (strptime (keyword->string, "%Y-%m-%dT%H:%M", &date))
1246         {
1247           keyword->integer_value = mktime (&date);
1248           keyword->type = KEYWORD_TYPE_INTEGER;
1249           g_debug ("Parsed Y-m-dTH:M %s to timestamp %d.",
1250                    keyword->string, keyword->integer_value);
1251         }
1252       // Add T%Hh%M for downwards compatible filter
1253       else if (strptime (keyword->string, "%Y-%m-%dT%Hh%M", &date))
1254         {
1255           keyword->integer_value = mktime (&date);
1256           keyword->type = KEYWORD_TYPE_INTEGER;
1257           g_debug ("Parsed Y-m-dTHhM %s to timestamp %d.",
1258                    keyword->string, keyword->integer_value);
1259         }
1260       else if (memset (&date, 0, sizeof (date)),
1261                strptime (keyword->string, "%Y-%m-%d", &date))
1262         {
1263           keyword->integer_value = mktime (&date);
1264           keyword->type = KEYWORD_TYPE_INTEGER;
1265           g_debug ("Parsed Y-m-d %s to timestamp %d.",
1266                    keyword->string, keyword->integer_value);
1267         }
1268       else if (sscanf (keyword->string, "%d%1s", &parsed_integer, dummy) == 1)
1269         {
1270           keyword->integer_value = parsed_integer;
1271           keyword->type = KEYWORD_TYPE_INTEGER;
1272         }
1273       else if (sscanf (keyword->string, "%lf%1s", &parsed_double, dummy) == 1
1274                && parsed_double <= DBL_MAX)
1275         {
1276           keyword->double_value = parsed_double;
1277           keyword->type = KEYWORD_TYPE_DOUBLE;
1278         }
1279       else
1280         keyword->type = KEYWORD_TYPE_STRING;
1281     }
1282   else
1283     {
1284       keyword->integer_value = atoi (keyword->string);
1285       keyword->type = KEYWORD_TYPE_INTEGER;
1286     }
1287 }
1288 
1289 /**
1290  * @brief Cleans up keywords with special conditions and relations.
1291  *
1292  * @param[in]  keyword  Keyword to clean up.
1293  */
1294 static void
cleanup_keyword(keyword_t * keyword)1295 cleanup_keyword (keyword_t *keyword)
1296 {
1297   if (keyword->column == NULL)
1298     return;
1299 
1300   if (strcasecmp (keyword->column, "first") == 0)
1301     {
1302       /* "first" must be >= 1 */
1303       if (keyword->integer_value <= 0)
1304         {
1305           g_free (keyword->string);
1306           keyword->integer_value = 1;
1307           keyword->string = g_strdup ("1");
1308         }
1309       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1310     }
1311   else if (strcasecmp (keyword->column, "rows") == 0)
1312     {
1313       /* rows must be >= 1 or a special value (-1 or -2) */
1314       if (keyword->integer_value == 0)
1315         {
1316           g_free (keyword->string);
1317           keyword->integer_value = 1;
1318           keyword->string = g_strdup ("1");
1319         }
1320       else if (keyword->integer_value < -2)
1321         {
1322           g_free (keyword->string);
1323           keyword->integer_value = -1;
1324           keyword->string = g_strdup ("-1");
1325         }
1326       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1327     }
1328   else if (strcasecmp (keyword->column, "min_qod") == 0)
1329     {
1330       /* min_qod must be a percentage (between 0 and 100) */
1331       if (keyword->integer_value < 0)
1332         {
1333           g_free (keyword->string);
1334           keyword->integer_value = 0;
1335           keyword->string = g_strdup ("0");
1336         }
1337       else if (keyword->integer_value > 100)
1338         {
1339           g_free (keyword->string);
1340           keyword->integer_value = 100;
1341           keyword->string = g_strdup ("100");
1342         }
1343       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1344     }
1345   else if (strcasecmp (keyword->column, "apply_overrides") == 0
1346            || strcasecmp (keyword->column, "overrides") == 0
1347            || strcasecmp (keyword->column, "notes") == 0
1348            || strcasecmp (keyword->column, "result_hosts_only") == 0)
1349     {
1350       /* Boolean options (0 or 1) */
1351       if (keyword->integer_value != 0 && keyword->integer_value != 1)
1352         {
1353           g_free (keyword->string);
1354           keyword->integer_value = 1;
1355           keyword->string = g_strdup ("1");
1356         }
1357       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1358     }
1359   else if (strcasecmp (keyword->column, "delta_states") == 0
1360            || strcasecmp (keyword->column, "levels") == 0
1361            || strcasecmp (keyword->column, "sort") == 0
1362            || strcasecmp (keyword->column, "sort-reverse") == 0)
1363     {
1364       /* Text options */
1365       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1366     }
1367 }
1368 
1369 /**
1370  * @brief Check whether a keyword has any effect in the filter.
1371  *
1372  * Some keywords are redundant, like a second sort= keyword.
1373  *
1374  * @param[in]  array    Array of existing keywords.
1375  * @param[in]  keyword  Keyword under consideration.
1376  *
1377  * @return 0 no, 1 yes.
1378  */
1379 static int
keyword_applies(array_t * array,const keyword_t * keyword)1380 keyword_applies (array_t *array, const keyword_t *keyword)
1381 {
1382   if (keyword->column
1383       && ((strcmp (keyword->column, "sort") == 0)
1384           || (strcmp (keyword->column, "sort-reverse") == 0))
1385       && (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL))
1386     {
1387       int index;
1388 
1389       index = array->len;
1390       while (index--)
1391         {
1392           keyword_t *item;
1393           item = (keyword_t*) g_ptr_array_index (array, index);
1394           if (item->column
1395               && ((strcmp (item->column, "sort") == 0)
1396                   || (strcmp (item->column, "sort-reverse") == 0)))
1397             return 0;
1398         }
1399       return 1;
1400     }
1401 
1402   if (keyword->column
1403       && (strcmp (keyword->column, "first") == 0))
1404     {
1405       int index;
1406 
1407       index = array->len;
1408       while (index--)
1409         {
1410           keyword_t *item;
1411           item = (keyword_t*) g_ptr_array_index (array, index);
1412           if (item->column && (strcmp (item->column, "first") == 0))
1413             return 0;
1414         }
1415     }
1416 
1417   if (keyword->column
1418       && (strcmp (keyword->column, "rows") == 0))
1419     {
1420       int index;
1421 
1422       index = array->len;
1423       while (index--)
1424         {
1425           keyword_t *item;
1426           item = (keyword_t*) g_ptr_array_index (array, index);
1427           if (item->column && (strcmp (item->column, "rows") == 0))
1428             return 0;
1429         }
1430     }
1431 
1432   if (keyword->column
1433       && (strcmp (keyword->column, "apply_overrides") == 0))
1434     {
1435       int index;
1436 
1437       index = array->len;
1438       while (index--)
1439         {
1440           keyword_t *item;
1441           item = (keyword_t*) g_ptr_array_index (array, index);
1442           if (item->column && (strcmp (item->column, "apply_overrides") == 0))
1443             return 0;
1444         }
1445     }
1446 
1447   if (keyword->column
1448       && (strcmp (keyword->column, "delta_states") == 0))
1449     {
1450       int index;
1451 
1452       index = array->len;
1453       while (index--)
1454         {
1455           keyword_t *item;
1456           item = (keyword_t*) g_ptr_array_index (array, index);
1457           if (item->column && (strcmp (item->column, "delta_states") == 0))
1458             return 0;
1459         }
1460     }
1461 
1462   if (keyword->column
1463       && (strcmp (keyword->column, "levels") == 0))
1464     {
1465       int index;
1466 
1467       index = array->len;
1468       while (index--)
1469         {
1470           keyword_t *item;
1471           item = (keyword_t*) g_ptr_array_index (array, index);
1472           if (item->column && (strcmp (item->column, "levels") == 0))
1473             return 0;
1474         }
1475     }
1476 
1477   if (keyword->column
1478       && (strcmp (keyword->column, "min_qod") == 0))
1479     {
1480       int index;
1481 
1482       index = array->len;
1483       while (index--)
1484         {
1485           keyword_t *item;
1486           item = (keyword_t*) g_ptr_array_index (array, index);
1487           if (item->column && (strcmp (item->column, "min_qod") == 0))
1488             return 0;
1489         }
1490     }
1491 
1492   if (keyword->column
1493       && (strcmp (keyword->column, "notes") == 0))
1494     {
1495       int index;
1496 
1497       index = array->len;
1498       while (index--)
1499         {
1500           keyword_t *item;
1501           item = (keyword_t*) g_ptr_array_index (array, index);
1502           if (item->column && (strcmp (item->column, "notes") == 0))
1503             return 0;
1504         }
1505     }
1506 
1507   if (keyword->column
1508       && (strcmp (keyword->column, "overrides") == 0))
1509     {
1510       int index;
1511 
1512       index = array->len;
1513       while (index--)
1514         {
1515           keyword_t *item;
1516           item = (keyword_t*) g_ptr_array_index (array, index);
1517           if (item->column && (strcmp (item->column, "overrides") == 0))
1518             return 0;
1519         }
1520     }
1521 
1522   if (keyword->column
1523       && (strcmp (keyword->column, "result_hosts_only") == 0))
1524     {
1525       int index;
1526 
1527       index = array->len;
1528       while (index--)
1529         {
1530           keyword_t *item;
1531           item = (keyword_t*) g_ptr_array_index (array, index);
1532           if (item->column && (strcmp (item->column, "result_hosts_only") == 0))
1533             return 0;
1534         }
1535     }
1536 
1537   if (keyword->column
1538       && (strcmp (keyword->column, "timezone") == 0))
1539     {
1540       int index;
1541 
1542       index = array->len;
1543       while (index--)
1544         {
1545           keyword_t *item;
1546           item = (keyword_t*) g_ptr_array_index (array, index);
1547           if (item->column && (strcmp (item->column, "timezone") == 0))
1548             return 0;
1549         }
1550     }
1551 
1552   return 1;
1553 }
1554 
1555 /**
1556  * @brief Free a split filter.
1557  *
1558  * @param[in]  split  Split filter.
1559  */
1560 void
filter_free(array_t * split)1561 filter_free (array_t *split)
1562 {
1563   keyword_t **point;
1564   for (point = (keyword_t**) split->pdata; *point; point++)
1565     keyword_free (*point);
1566   array_free (split);
1567 }
1568 
1569 /**
1570  * @brief Flag to control the default sorting produced by split_filter.
1571  *
1572  * If this is true, and the filter does not specify a sort field, then
1573  * split_filter will not insert a default sort term, so that the random
1574  * (and fast) table order in the database will be used.
1575  */
1576 static int table_order_if_sort_not_specified = 0;
1577 
1578 /**
1579  * @brief Ensure filter parts contains the special keywords.
1580  *
1581  * @param[in]  parts         Array of keyword strings.
1582  * @param[in]  given_filter  Filter term.
1583  */
1584 void
split_filter_add_specials(array_t * parts,const gchar * given_filter)1585 split_filter_add_specials (array_t *parts, const gchar* given_filter)
1586 {
1587   int index, first, max, sort;
1588   keyword_t *keyword;
1589 
1590   index = parts->len;
1591   first = max = sort = 0;
1592   while (index--)
1593     {
1594       keyword_t *item;
1595       item = (keyword_t*) g_ptr_array_index (parts, index);
1596       if (item->column && (strcmp (item->column, "first") == 0))
1597         first = 1;
1598       else if (item->column && (strcmp (item->column, "rows") == 0))
1599         max = 1;
1600       else if (item->column
1601                && ((strcmp (item->column, "sort") == 0)
1602                    || (strcmp (item->column, "sort-reverse") == 0)))
1603         sort = 1;
1604     }
1605 
1606   if (first == 0)
1607     {
1608       keyword = g_malloc0 (sizeof (keyword_t));
1609       keyword->column = g_strdup ("first");
1610       keyword->string = g_strdup ("1");
1611       keyword->type = KEYWORD_TYPE_STRING;
1612       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1613       array_add (parts, keyword);
1614     }
1615 
1616   if (max == 0)
1617     {
1618       keyword = g_malloc0 (sizeof (keyword_t));
1619       keyword->column = g_strdup ("rows");
1620       keyword->string = g_strdup ("-2");
1621       keyword->type = KEYWORD_TYPE_STRING;
1622       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1623       array_add (parts, keyword);
1624     }
1625 
1626   if (table_order_if_sort_not_specified == 0 && sort == 0)
1627     {
1628       keyword = g_malloc0 (sizeof (keyword_t));
1629       keyword->column = g_strdup ("sort");
1630       keyword->string = g_strdup ("name");
1631       keyword->type = KEYWORD_TYPE_STRING;
1632       keyword->relation = KEYWORD_RELATION_COLUMN_EQUAL;
1633       array_add (parts, keyword);
1634     }
1635 }
1636 
1637 /**
1638  * @brief Split the filter term into parts.
1639  *
1640  * @param[in]  given_filter  Filter term.
1641  *
1642  * @return Array of strings, the parts.
1643  */
1644 array_t *
split_filter(const gchar * given_filter)1645 split_filter (const gchar* given_filter)
1646 {
1647   int in_quote, between;
1648   array_t *parts;
1649   const gchar *current_part, *filter;
1650   keyword_t *keyword;
1651 
1652   assert (given_filter);
1653 
1654   /* Collect the filter terms in an array. */
1655 
1656   filter = given_filter;
1657   parts = make_array ();
1658   in_quote = 0;
1659   between = 1;
1660   keyword = NULL;
1661   current_part = filter;  /* To silence compiler warning. */
1662   while (*filter)
1663     {
1664       switch (*filter)
1665         {
1666           case '=':
1667           case '~':
1668             if (between)
1669               {
1670                 /* Empty index.  Start a part. */
1671                 keyword = g_malloc0 (sizeof (keyword_t));
1672                 if (*filter == '=')
1673                   keyword->equal = 1;
1674                 else
1675                   keyword->approx = 1;
1676                 current_part = filter + 1;
1677                 between = 0;
1678                 break;
1679               }
1680           case ':':
1681           case '>':
1682           case '<':
1683             if (between)
1684               {
1685                 /* Empty index.  Start a part. */
1686                 keyword = g_malloc0 (sizeof (keyword_t));
1687                 current_part = filter;
1688                 between = 0;
1689                 break;
1690               }
1691             if (in_quote)
1692               break;
1693             /* End of an index. */
1694             if (keyword == NULL)
1695               {
1696                 assert (0);
1697                 break;
1698               }
1699             if (keyword->column)
1700               /* Already had an index char. */
1701               break;
1702             if (filter <= (current_part - 1))
1703               {
1704                 assert (0);
1705                 break;
1706               }
1707             keyword->column = g_strndup (current_part,
1708                                          filter - current_part);
1709             current_part = filter + 1;
1710             keyword->relation = parse_column_relation(*filter);
1711             break;
1712 
1713           case ' ':
1714           case '\t':
1715           case '\n':
1716           case '\r':
1717             if (in_quote || between)
1718               break;
1719             /* End of a part. */
1720             if (keyword == NULL)
1721               {
1722                 assert (0);
1723                 break;
1724               }
1725             keyword->string = g_strndup (current_part, filter - current_part);
1726             parse_keyword (keyword);
1727             cleanup_keyword (keyword);
1728             if (keyword_applies (parts, keyword))
1729               array_add (parts, keyword);
1730             keyword = NULL;
1731             between = 1;
1732             break;
1733 
1734           case '"':
1735             if (in_quote)
1736               {
1737                 /* End of a quoted part. */
1738                 if (keyword == NULL)
1739                   {
1740                     assert (0);
1741                     break;
1742                   }
1743                 keyword->quoted = 1;
1744                 keyword->string = g_strndup (current_part,
1745                                              filter - current_part);
1746                 parse_keyword (keyword);
1747                 cleanup_keyword (keyword);
1748                 if (keyword_applies (parts, keyword))
1749                   array_add (parts, keyword);
1750                 keyword = NULL;
1751                 in_quote = 0;
1752                 between = 1;
1753               }
1754             else if (between)
1755               {
1756                 /* Start of a quoted part. */
1757                 keyword = g_malloc0 (sizeof (keyword_t));
1758                 in_quote = 1;
1759                 current_part = filter + 1;
1760                 between = 0;
1761               }
1762             else if (keyword->column && filter == current_part)
1763               {
1764                 /* A quoted index. */
1765                 in_quote = 1;
1766                 current_part++;
1767               }
1768             else if ((keyword->equal || keyword->approx)
1769                      && filter == current_part)
1770               {
1771                 /* A quoted exact term, like ="abc"
1772                  * or a prefixed approximate term, like ~"abc". */
1773                 in_quote = 1;
1774                 current_part++;
1775               }
1776             /* Else just a quote in a keyword, like ab"cd. */
1777             break;
1778 
1779           default:
1780             if (between)
1781               {
1782                 /* Start of a part. */
1783                 keyword = g_malloc0 (sizeof (keyword_t));
1784                 current_part = filter;
1785                 between = 0;
1786               }
1787             break;
1788         }
1789       filter++;
1790     }
1791   if (between == 0)
1792     {
1793       if (keyword == NULL)
1794         assert (0);
1795       else
1796         {
1797           keyword->quoted = in_quote;
1798           keyword->string = g_strdup (current_part);
1799           parse_keyword (keyword);
1800           cleanup_keyword (keyword);
1801           if (keyword_applies (parts, keyword))
1802             array_add (parts, keyword);
1803           keyword = NULL;
1804         }
1805     }
1806   assert (keyword == NULL);
1807 
1808   /* Make sure the special keywords appear in the array. */
1809 
1810   split_filter_add_specials (parts, given_filter);
1811 
1812   array_add (parts, NULL);
1813 
1814   return parts;
1815 }
1816 
1817 /**
1818  * @brief Get info from a filter.
1819  *
1820  * It's up to the caller to ensure that max is adjusted for Max Rows Per Page
1821  * (by calling manage_max_rows).
1822  *
1823  * @param[in]   filter      Filter.
1824  * @param[out]  first       Number of first item.
1825  * @param[out]  max         Max number of rows.
1826  * @param[out]  sort_field  Sort field.
1827  * @param[out]  sort_order  Sort order.
1828  */
1829 void
manage_filter_controls(const gchar * filter,int * first,int * max,gchar ** sort_field,int * sort_order)1830 manage_filter_controls (const gchar *filter, int *first, int *max,
1831                         gchar **sort_field, int *sort_order)
1832 {
1833   keyword_t **point;
1834   array_t *split;
1835 
1836   if (filter == NULL)
1837     {
1838       if (first)
1839         *first = 1;
1840       if (max)
1841         *max = -2;
1842       if (sort_field)
1843         *sort_field = g_strdup ("name");
1844       if (sort_order)
1845         *sort_order = 1;
1846       return;
1847     }
1848 
1849   split = split_filter (filter);
1850   point = (keyword_t**) split->pdata;
1851   if (first)
1852     {
1853       *first = 1;
1854       while (*point)
1855         {
1856           keyword_t *keyword;
1857 
1858           keyword = *point;
1859           if (keyword->column && (strcmp (keyword->column, "first") == 0))
1860             {
1861               *first = atoi (keyword->string);
1862               if (*first < 0)
1863                 *first = 0;
1864               break;
1865             }
1866           point++;
1867         }
1868     }
1869 
1870   point = (keyword_t**) split->pdata;
1871   if (max)
1872     {
1873       *max = -2;
1874       while (*point)
1875         {
1876           keyword_t *keyword;
1877 
1878           keyword = *point;
1879           if (keyword->column && (strcmp (keyword->column, "rows") == 0))
1880             {
1881               *max = atoi (keyword->string);
1882               if (*max == -2)
1883                 setting_value_int (SETTING_UUID_ROWS_PER_PAGE, max);
1884               else if (*max < 1)
1885                 *max = -1;
1886               break;
1887             }
1888           point++;
1889         }
1890     }
1891 
1892   point = (keyword_t**) split->pdata;
1893   if (sort_field || sort_order)
1894     {
1895       if (sort_field) *sort_field = NULL;
1896       if (sort_order) *sort_order = 1;
1897       while (*point)
1898         {
1899           keyword_t *keyword;
1900 
1901           keyword = *point;
1902           if (keyword->column
1903               && (strcmp (keyword->column, "sort") == 0))
1904             {
1905               if (sort_field) *sort_field = g_strdup (keyword->string);
1906               if (sort_order) *sort_order = 1;
1907               break;
1908             }
1909           if (keyword->column
1910               && (strcmp (keyword->column, "sort-reverse") == 0))
1911             {
1912               if (sort_field) *sort_field = g_strdup (keyword->string);
1913               if (sort_order) *sort_order = 0;
1914               break;
1915             }
1916           point++;
1917         }
1918       if (sort_field && (*sort_field == NULL))
1919         *sort_field = g_strdup ("name");
1920     }
1921 
1922   filter_free (split);
1923   return;
1924 }
1925 
1926 /**
1927  * @brief Get an int column from a filter split.
1928  *
1929  * @param[in]  point   Filter split.
1930  * @param[in]  column  Name of column.
1931  * @param[out] val     Value of column.
1932  *
1933  * @return 0 success, 1 fail.
1934  */
1935 static int
filter_control_int(keyword_t ** point,const char * column,int * val)1936 filter_control_int (keyword_t **point, const char *column, int *val)
1937 {
1938   if (val)
1939     while (*point)
1940       {
1941         keyword_t *keyword;
1942 
1943         keyword = *point;
1944         if (keyword->column
1945             && (strcmp (keyword->column, column) == 0))
1946           {
1947             *val = atoi (keyword->string);
1948             return 0;
1949           }
1950         point++;
1951       }
1952   return 1;
1953 }
1954 
1955 /**
1956  * @brief Get a string column from a filter split.
1957  *
1958  * @param[in]  point   Filter split.
1959  * @param[in]  column  Name of column.
1960  * @param[out] string  Value of column, freshly allocated.
1961  *
1962  * @return 0 success, 1 fail.
1963  */
1964 static int
filter_control_str(keyword_t ** point,const char * column,gchar ** string)1965 filter_control_str (keyword_t **point, const char *column, gchar **string)
1966 {
1967   if (string)
1968     while (*point)
1969       {
1970         keyword_t *keyword;
1971 
1972         keyword = *point;
1973         if (keyword->column
1974             && (strcmp (keyword->column, column) == 0))
1975           {
1976             *string = g_strdup (keyword->string);
1977             return 0;
1978           }
1979         point++;
1980       }
1981   return 1;
1982 }
1983 
1984 /**
1985  * @brief Get info from a result filter for a report.
1986  *
1987  * It's up to the caller to ensure that max is adjusted for Max Rows Per Page
1988  * (by calling manage_max_rows).
1989  *
1990  * @param[in]   filter      Filter.
1991  * @param[out]  first       Number of first item.
1992  * @param[out]  max         Max number of rows.
1993  * @param[out]  sort_field  Sort field.
1994  * @param[out]  sort_order  Sort order.
1995  * @param[out]  result_hosts_only  Whether to show only hosts with results.
1996  * @param[out]  min_qod        Minimum QoD base of included results.  All
1997  *                              results if NULL.
1998  * @param[out]  levels         String describing threat levels (message types)
1999  *                             to include in count (for example, "hmlg" for
2000  *                             High, Medium, Low and loG).  All levels if NULL.
2001  * @param[out]  delta_states   String describing delta states to include in count
2002  *                             (for example, "sngc" Same, New, Gone and Changed).
2003  *                             All levels if NULL.
2004  * @param[out]  search_phrase      Phrase that results must include.  All results
2005  *                                 if NULL or "".
2006  * @param[out]  search_phrase_exact  Whether search phrase is exact.
2007  * @param[out]  notes              Whether to include notes.
2008  * @param[out]  overrides          Whether to include overrides.
2009  * @param[out]  apply_overrides    Whether to apply overrides.
2010  * @param[out]  zone               Timezone.
2011  */
2012 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)2013 manage_report_filter_controls (const gchar *filter, int *first, int *max,
2014                                gchar **sort_field, int *sort_order,
2015                                int *result_hosts_only, gchar **min_qod,
2016                                gchar **levels, gchar **delta_states,
2017                                gchar **search_phrase, int *search_phrase_exact,
2018                                int *notes, int *overrides,
2019                                int *apply_overrides, gchar **zone)
2020 {
2021   keyword_t **point;
2022   array_t *split;
2023   int val;
2024   gchar *string;
2025 
2026   if (filter == NULL)
2027     return;
2028 
2029   split = split_filter (filter);
2030 
2031   point = (keyword_t**) split->pdata;
2032   if (first)
2033     {
2034       *first = 1;
2035       while (*point)
2036         {
2037           keyword_t *keyword;
2038 
2039           keyword = *point;
2040           if (keyword->column && (strcmp (keyword->column, "first") == 0))
2041             {
2042               *first = atoi (keyword->string);
2043               if (*first < 0)
2044                 *first = 0;
2045               break;
2046             }
2047           point++;
2048         }
2049       /* Switch from 1 to 0 indexing. */
2050 
2051       (*first)--;
2052     }
2053 
2054   point = (keyword_t**) split->pdata;
2055   if (max)
2056     {
2057       *max = 100;
2058       while (*point)
2059         {
2060           keyword_t *keyword;
2061 
2062           keyword = *point;
2063           if (keyword->column && (strcmp (keyword->column, "rows") == 0))
2064             {
2065               *max = atoi (keyword->string);
2066               if (*max == -2)
2067                 setting_value_int (SETTING_UUID_ROWS_PER_PAGE, max);
2068               else if (*max < 1)
2069                 *max = -1;
2070               break;
2071             }
2072           point++;
2073         }
2074     }
2075 
2076   point = (keyword_t**) split->pdata;
2077   if (sort_field || sort_order)
2078     {
2079       if (sort_field) *sort_field = NULL;
2080       if (sort_order) *sort_order = 1;
2081       while (*point)
2082         {
2083           keyword_t *keyword;
2084 
2085           keyword = *point;
2086           if (keyword->column
2087               && (strcmp (keyword->column, "sort") == 0))
2088             {
2089               if (sort_field) *sort_field = g_strdup (keyword->string);
2090               if (sort_order) *sort_order = 1;
2091               break;
2092             }
2093           if (keyword->column
2094               && (strcmp (keyword->column, "sort-reverse") == 0))
2095             {
2096               if (sort_field) *sort_field = g_strdup (keyword->string);
2097               if (sort_order) *sort_order = 0;
2098               break;
2099             }
2100           point++;
2101         }
2102       if (sort_field && (*sort_field == NULL))
2103         *sort_field = g_strdup ("name"); /* NVT name. */
2104     }
2105 
2106   if (search_phrase)
2107     {
2108       GString *phrase;
2109       phrase = g_string_new ("");
2110       point = (keyword_t**) split->pdata;
2111       if (search_phrase_exact)
2112         *search_phrase_exact = 0;
2113       while (*point)
2114         {
2115           keyword_t *keyword;
2116 
2117           keyword = *point;
2118           if (keyword->column == NULL)
2119             {
2120               if (search_phrase_exact && keyword->equal)
2121                 /* If one term is "exact" then the search is "exact", because
2122                  * for reports the filter terms are combined into a single
2123                  * search term. */
2124                 *search_phrase_exact = 1;
2125               g_string_append_printf (phrase, "%s ", keyword->string);
2126             }
2127           point++;
2128         }
2129       *search_phrase = g_strchomp (phrase->str);
2130       g_string_free (phrase, FALSE);
2131     }
2132 
2133   if (result_hosts_only)
2134     {
2135       if (filter_control_int ((keyword_t **) split->pdata,
2136                               "result_hosts_only",
2137                               &val))
2138         *result_hosts_only = 1;
2139       else
2140         *result_hosts_only = val;
2141     }
2142 
2143   if (notes)
2144     {
2145       if (filter_control_int ((keyword_t **) split->pdata,
2146                               "notes",
2147                               &val))
2148         *notes = 1;
2149       else
2150         *notes = val;
2151     }
2152 
2153   if (overrides)
2154     {
2155       if (filter_control_int ((keyword_t **) split->pdata,
2156                               "overrides",
2157                               &val))
2158         *overrides = 1;
2159       else
2160         *overrides = val;
2161     }
2162 
2163   if (apply_overrides)
2164     {
2165       if (filter_control_int ((keyword_t **) split->pdata,
2166                               "apply_overrides",
2167                               &val))
2168         {
2169           if (filter_control_int ((keyword_t **) split->pdata,
2170                                   "overrides",
2171                                   &val))
2172             *apply_overrides = 1;
2173           else
2174             *apply_overrides = val;
2175         }
2176       else
2177         *apply_overrides = val;
2178     }
2179 
2180   if (delta_states)
2181     {
2182       if (filter_control_str ((keyword_t **) split->pdata,
2183                               "delta_states",
2184                               &string))
2185         *delta_states = NULL;
2186       else
2187         *delta_states = string;
2188     }
2189 
2190   if (levels)
2191     {
2192       if (filter_control_str ((keyword_t **) split->pdata,
2193                               "levels",
2194                               &string))
2195         *levels = NULL;
2196       else
2197         *levels = string;
2198     }
2199 
2200   if (min_qod)
2201     {
2202       if (filter_control_str ((keyword_t **) split->pdata,
2203                               "min_qod",
2204                               &string))
2205         *min_qod = NULL;
2206       else
2207         *min_qod = string;
2208     }
2209 
2210   if (zone)
2211     {
2212       if (filter_control_str ((keyword_t **) split->pdata,
2213                               "timezone",
2214                               &string))
2215         *zone = NULL;
2216       else
2217         *zone = string;
2218     }
2219 
2220   filter_free (split);
2221   return;
2222 }
2223 
2224 /**
2225  * @brief Append relation to filter.
2226  *
2227  * @param[in]  clean     Filter.
2228  * @param[in]  keyword   Keyword
2229  * @param[in]  relation  Relation char.
2230  */
2231 static void
append_relation(GString * clean,keyword_t * keyword,const char relation)2232 append_relation (GString *clean, keyword_t *keyword, const char relation)
2233 {
2234   if (strcmp (keyword->column, "rows") == 0)
2235     {
2236       int max;
2237 
2238       if (strcmp (keyword->string, "-2") == 0)
2239         setting_value_int (SETTING_UUID_ROWS_PER_PAGE, &max);
2240       else
2241         max = atoi (keyword->string);
2242 
2243       g_string_append_printf (clean,
2244                               " %s%c%i",
2245                               keyword->column,
2246                               relation,
2247                               manage_max_rows (max));
2248     }
2249   else if (keyword->quoted)
2250     g_string_append_printf (clean,
2251                             " %s%c\"%s\"",
2252                             keyword->column,
2253                             relation,
2254                             keyword->string);
2255   else
2256     g_string_append_printf (clean,
2257                             " %s%c%s",
2258                             keyword->column,
2259                             relation,
2260                             keyword->string);
2261 }
2262 
2263 /**
2264  * @brief Clean a filter, removing a keyword in the process.
2265  *
2266  * @param[in]  filter  Filter.
2267  * @param[in]  column  Keyword to remove, or NULL.
2268  *
2269  * @return Cleaned filter.
2270  */
2271 gchar *
manage_clean_filter_remove(const gchar * filter,const gchar * column)2272 manage_clean_filter_remove (const gchar *filter, const gchar *column)
2273 {
2274   GString *clean;
2275   keyword_t **point;
2276   array_t *split;
2277 
2278   if (filter == NULL)
2279     return g_strdup ("");
2280 
2281   clean = g_string_new ("");
2282   split = split_filter (filter);
2283   point = (keyword_t**) split->pdata;
2284   while (*point)
2285     {
2286       keyword_t *keyword;
2287 
2288       keyword = *point;
2289       if (keyword->column
2290           && column
2291           && strlen (column)
2292           && ((strcasecmp (keyword->column, column) == 0)
2293               || (keyword->column[0] == '_'
2294                   && strcasecmp (keyword->column + 1, column) == 0)))
2295         {
2296           /* Remove this keyword. */;
2297         }
2298       else if (keyword->column)
2299         switch (keyword->relation)
2300           {
2301             case KEYWORD_RELATION_COLUMN_EQUAL:
2302               append_relation (clean, keyword, '=');
2303               break;
2304             case KEYWORD_RELATION_COLUMN_APPROX:
2305               append_relation (clean, keyword, '~');
2306               break;
2307             case KEYWORD_RELATION_COLUMN_ABOVE:
2308               append_relation (clean, keyword, '>');
2309               break;
2310             case KEYWORD_RELATION_COLUMN_BELOW:
2311               append_relation (clean, keyword, '<');
2312               break;
2313             case KEYWORD_RELATION_COLUMN_REGEXP:
2314               append_relation (clean, keyword, ':');
2315               break;
2316 
2317             case KEYWORD_RELATION_APPROX:
2318               if (keyword->quoted)
2319                 g_string_append_printf (clean, " \"%s\"", keyword->string);
2320               else
2321                 g_string_append_printf (clean, " %s", keyword->string);
2322               break;
2323           }
2324       else
2325         {
2326           const char *relation_symbol;
2327           if (keyword->equal)
2328             relation_symbol = "=";
2329           else if (keyword->approx)
2330             relation_symbol = "~";
2331           else
2332             relation_symbol = "";
2333 
2334           if (keyword->quoted)
2335             g_string_append_printf (clean, " %s\"%s\"",
2336                                     relation_symbol,
2337                                     keyword->string);
2338           else
2339             g_string_append_printf (clean, " %s%s",
2340                                     relation_symbol,
2341                                     keyword->string);
2342         }
2343       point++;
2344     }
2345   filter_free (split);
2346   return g_strstrip (g_string_free (clean, FALSE));
2347 }
2348 
2349 /**
2350  * @brief Clean a filter.
2351  *
2352  * @param[in]  filter  Filter.
2353  *
2354  * @return Cleaned filter.
2355  */
2356 gchar *
manage_clean_filter(const gchar * filter)2357 manage_clean_filter (const gchar *filter)
2358 {
2359   return manage_clean_filter_remove (filter, NULL);
2360 }
2361 
2362 /**
2363  * @brief Return SQL join words for filter_clause.
2364  *
2365  * @param[in]  first         Whether keyword is first.
2366  * @param[in]  last_was_and  Whether last keyword was "and".
2367  * @param[in]  last_was_not  Whether last keyword was "not".
2368  *
2369  * @return SQL join words.
2370  */
2371 static const char *
get_join(int first,int last_was_and,int last_was_not)2372 get_join (int first, int last_was_and, int last_was_not)
2373 {
2374   const char *pre;
2375   if (first)
2376     {
2377       if (last_was_not)
2378         pre = "NOT ";
2379       else
2380         pre = "";
2381     }
2382   else
2383     {
2384       if (last_was_and)
2385         {
2386           if (last_was_not)
2387             pre = " AND NOT ";
2388           else
2389             pre = " AND ";
2390         }
2391       else
2392         {
2393           if (last_was_not)
2394             pre = " OR NOT ";
2395           else
2396             pre = " OR ";
2397         }
2398     }
2399   return pre;
2400 }
2401 
2402 /**
2403  * @brief Get the column expression for a filter column.
2404  *
2405  * @param[in]  select_columns  SELECT columns.
2406  * @param[in]  filter_column   Filter column.
2407  * @param[out] type            Type of returned column.
2408  *
2409  * @return Column for the SELECT statement.
2410  */
2411 static gchar *
columns_select_column_single(column_t * select_columns,const char * filter_column,keyword_type_t * type)2412 columns_select_column_single (column_t *select_columns,
2413                               const char *filter_column,
2414                               keyword_type_t* type)
2415 {
2416   column_t *columns;
2417   if (type)
2418     *type = KEYWORD_TYPE_UNKNOWN;
2419   if (select_columns == NULL)
2420     return NULL;
2421   columns = select_columns;
2422   while ((*columns).select)
2423     {
2424       if ((*columns).filter
2425           && strcmp ((*columns).filter, filter_column) == 0)
2426         {
2427           if (type)
2428             *type = (*columns).type;
2429           return (*columns).select;
2430         }
2431       if ((*columns).filter
2432           && *((*columns).filter)
2433           && *((*columns).filter) == '_'
2434           && strcmp (((*columns).filter) + 1, filter_column) == 0)
2435         {
2436           if (type)
2437             *type = (*columns).type;
2438           return (*columns).select;
2439         }
2440       columns++;
2441     }
2442   columns = select_columns;
2443   while ((*columns).select)
2444     {
2445       if (strcmp ((*columns).select, filter_column) == 0)
2446         {
2447           if (type)
2448             *type = (*columns).type;
2449           return (*columns).select;
2450         }
2451       columns++;
2452     }
2453   return NULL;
2454 }
2455 
2456 /**
2457  * @brief Get the selection term for a filter column.
2458  *
2459  * @param[in]  select_columns  SELECT columns.
2460  * @param[in]  where_columns   WHERE "columns".
2461  * @param[in]  filter_column   Filter column.
2462  *
2463  * @return Column for the SELECT statement.
2464  */
2465 static gchar *
columns_select_column(column_t * select_columns,column_t * where_columns,const char * filter_column)2466 columns_select_column (column_t *select_columns,
2467                        column_t *where_columns,
2468                        const char *filter_column)
2469 {
2470   gchar *column;
2471   column = columns_select_column_single (select_columns, filter_column, NULL);
2472   if (column)
2473     return column;
2474   return columns_select_column_single (where_columns, filter_column, NULL);
2475 }
2476 
2477 /**
2478  * @brief Get the selection term for a filter column.
2479  *
2480  * @param[in]  select_columns  SELECT columns.
2481  * @param[in]  where_columns   WHERE "columns".
2482  * @param[in]  filter_column   Filter column.
2483  * @param[out] type            Type of the returned column.
2484  *
2485  * @return Column for the SELECT statement.
2486  */
2487 static gchar *
columns_select_column_with_type(column_t * select_columns,column_t * where_columns,const char * filter_column,keyword_type_t * type)2488 columns_select_column_with_type (column_t *select_columns,
2489                                  column_t *where_columns,
2490                                  const char *filter_column,
2491                                  keyword_type_t* type)
2492 {
2493   gchar *column;
2494   column = columns_select_column_single (select_columns, filter_column, type);
2495   if (column)
2496     return column;
2497   return columns_select_column_single (where_columns, filter_column, type);
2498 }
2499 
2500 /**
2501  * @brief Return column list for SELECT statement.
2502  *
2503  * @param[in]  select_columns  SELECT columns.
2504  *
2505  * @return Column list for the SELECT statement.
2506  */
2507 gchar *
columns_build_select(column_t * select_columns)2508 columns_build_select (column_t *select_columns)
2509 {
2510   if (select_columns == NULL)
2511     return g_strdup ("''");
2512 
2513   if ((*select_columns).select)
2514     {
2515       column_t *columns;
2516       GString *select;
2517 
2518       columns = select_columns;
2519       select = g_string_new ("");
2520       g_string_append (select, (*columns).select);
2521       if ((*columns).filter)
2522         g_string_append_printf (select, " AS %s", (*columns).filter);
2523       columns++;
2524       while ((*columns).select)
2525        {
2526          g_string_append_printf (select, ", %s", (*columns).select);
2527          if ((*columns).filter)
2528            g_string_append_printf (select, " AS %s", (*columns).filter);
2529          columns++;
2530        }
2531       return g_string_free (select, FALSE);
2532     }
2533   return g_strdup ("''");
2534 }
2535 
2536 /**
2537  * @brief Check whether a keyword applies to a column.
2538  *
2539  * @param[in]  keyword  Keyword.
2540  * @param[in]  column   Column.
2541  *
2542  * @return 1 if applies, else 0.
2543  */
2544 static int
keyword_applies_to_column(keyword_t * keyword,const char * column)2545 keyword_applies_to_column (keyword_t *keyword, const char* column)
2546 {
2547   if ((strcmp (column, "threat") == 0)
2548       && (strstr ("None", keyword->string) == NULL)
2549       && (strstr ("False Positive", keyword->string) == NULL)
2550       && (strstr ("Error", keyword->string) == NULL)
2551       && (strstr ("Alarm", keyword->string) == NULL)
2552       && (strstr ("High", keyword->string) == NULL)
2553       && (strstr ("Medium", keyword->string) == NULL)
2554       && (strstr ("Low", keyword->string) == NULL)
2555       && (strstr ("Log", keyword->string) == NULL))
2556     return 0;
2557   if ((strcmp (column, "trend") == 0)
2558       && (strstr ("more", keyword->string) == NULL)
2559       && (strstr ("less", keyword->string) == NULL)
2560       && (strstr ("up", keyword->string) == NULL)
2561       && (strstr ("down", keyword->string) == NULL)
2562       && (strstr ("same", keyword->string) == NULL))
2563     return 0;
2564   if ((strcmp (column, "status") == 0)
2565       && (strstr ("Delete Requested", keyword->string) == NULL)
2566       && (strstr ("Ultimate Delete Requested", keyword->string) == NULL)
2567       && (strstr ("Done", keyword->string) == NULL)
2568       && (strstr ("New", keyword->string) == NULL)
2569       && (strstr ("Running", keyword->string) == NULL)
2570       && (strstr ("Queued", keyword->string) == NULL)
2571       && (strstr ("Stop Requested", keyword->string) == NULL)
2572       && (strstr ("Stopped", keyword->string) == NULL)
2573       && (strstr ("Interrupted", keyword->string) == NULL))
2574     return 0;
2575   return 1;
2576 }
2577 
2578 /**
2579  * @brief Append parts for a "tag" keyword to a filter clause.
2580  *
2581  * @param[in,out] clause      Buffer for the filter clause to append to.
2582  * @param[in]  keyword        The keyword to create the filter clause part for.
2583  * @param[in]  type           The resource type.
2584  * @param[in]  first_keyword  Whether keyword is first.
2585  * @param[in]  last_was_and   Whether last keyword was "and".
2586  * @param[in]  last_was_not   Whether last keyword was "not".
2587  */
2588 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)2589 filter_clause_append_tag (GString *clause, keyword_t *keyword,
2590                           const char *type, int first_keyword,
2591                           int last_was_and, int last_was_not)
2592 {
2593   gchar *quoted_keyword;
2594   gchar **tag_split, *tag_name, *tag_value;
2595   int value_given;
2596 
2597   quoted_keyword = sql_quote (keyword->string);
2598   tag_split = g_strsplit (quoted_keyword, "=", 2);
2599   tag_name = g_strdup (tag_split[0] ? tag_split[0] : "");
2600 
2601   if (tag_split[0] && tag_split[1])
2602     {
2603       tag_value = g_strdup (tag_split[1]);
2604       value_given = 1;
2605     }
2606   else
2607     {
2608       tag_value = g_strdup ("");
2609       value_given = 0;
2610     }
2611 
2612   if (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL
2613       || keyword->relation == KEYWORD_RELATION_COLUMN_ABOVE
2614       || keyword->relation == KEYWORD_RELATION_COLUMN_BELOW)
2615     {
2616       g_string_append_printf
2617          (clause,
2618           "%s"
2619           "(EXISTS"
2620           "  (SELECT * FROM tags"
2621           "   WHERE tags.name = '%s'"
2622           "   AND tags.active != 0"
2623           "   AND user_has_access_uuid (CAST ('tag' AS text),"
2624           "                             CAST (tags.uuid AS text),"
2625           "                             CAST ('get_tags' AS text),"
2626           "                             0)"
2627           "   AND EXISTS (SELECT * FROM tag_resources"
2628           "                WHERE tag_resources.resource_uuid"
2629           "                        = %ss.uuid"
2630           "                  AND tag_resources.resource_type"
2631           "                        = '%s'"
2632           "                  AND tag = tags.id)"
2633           "   %s%s%s))",
2634           get_join (first_keyword, last_was_and,
2635                     last_was_not),
2636           tag_name,
2637           type,
2638           type,
2639           (value_given
2640             ? "AND tags.value = '"
2641             : ""),
2642           value_given ? tag_value : "",
2643           (value_given
2644             ? "'"
2645             : ""));
2646     }
2647   else if (keyword->relation == KEYWORD_RELATION_COLUMN_APPROX)
2648     {
2649       g_string_append_printf
2650          (clause,
2651           "%s"
2652           "(EXISTS"
2653           "  (SELECT * FROM tags"
2654           "   WHERE tags.name %s '%%%%%s%%%%'"
2655           "   AND tags.active != 0"
2656           "   AND user_has_access_uuid (CAST ('tag' AS text),"
2657           "                             CAST (tags.uuid AS text),"
2658           "                             CAST ('get_tags' AS text),"
2659           "                             0)"
2660           "   AND EXISTS (SELECT * FROM tag_resources"
2661           "                WHERE tag_resources.resource_uuid"
2662           "                        = %ss.uuid"
2663           "                  AND tag_resources.resource_type"
2664           "                        = '%s'"
2665           "                  AND tag = tags.id)"
2666           "   AND tags.value %s '%%%%%s%%%%'))",
2667           get_join (first_keyword, last_was_and,
2668                     last_was_not),
2669           sql_ilike_op (),
2670           tag_name,
2671           type,
2672           type,
2673           sql_ilike_op (),
2674           tag_value);
2675     }
2676   else if (keyword->relation == KEYWORD_RELATION_COLUMN_REGEXP)
2677     {
2678       g_string_append_printf
2679          (clause,
2680           "%s"
2681           "(EXISTS"
2682           "  (SELECT * FROM tags"
2683           "   WHERE tags.name %s '%s'"
2684           "   AND tags.active != 0"
2685           "   AND user_has_access_uuid (CAST ('tag' AS text),"
2686           "                             CAST (tags.uuid AS text),"
2687           "                             CAST ('get_tags' AS text),"
2688           "                             0)"
2689           "   AND EXISTS (SELECT * FROM tag_resources"
2690           "                WHERE tag_resources.resource_uuid"
2691           "                        = %ss.uuid"
2692           "                  AND tag_resources.resource_type"
2693           "                        = '%s'"
2694           "                  AND tag = tags.id)"
2695           "   AND tags.value"
2696           "       %s '%s'))",
2697           get_join (first_keyword, last_was_and,
2698                     last_was_not),
2699           sql_regexp_op (),
2700           tag_name,
2701           type,
2702           type,
2703           sql_regexp_op (),
2704           tag_value);
2705     }
2706 
2707   g_free (quoted_keyword);
2708   g_strfreev(tag_split);
2709   g_free(tag_name);
2710   g_free(tag_value);
2711 }
2712 
2713 /**
2714  * @brief Append parts for a "tag_id" keyword to a filter clause.
2715  *
2716  * @param[in,out] clause      Buffer for the filter clause to append to.
2717  * @param[in]  keyword        The keyword to create the filter clause part for.
2718  * @param[in]  type           The resource type.
2719  * @param[in]  first_keyword  Whether keyword is first.
2720  * @param[in]  last_was_and   Whether last keyword was "and".
2721  * @param[in]  last_was_not   Whether last keyword was "not".
2722  */
2723 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)2724 filter_clause_append_tag_id (GString *clause, keyword_t *keyword,
2725                              const char *type, int first_keyword,
2726                              int last_was_and, int last_was_not)
2727 {
2728   gchar *quoted_keyword;
2729 
2730   quoted_keyword = sql_quote (keyword->string);
2731 
2732   if (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL
2733       || keyword->relation == KEYWORD_RELATION_COLUMN_ABOVE
2734       || keyword->relation == KEYWORD_RELATION_COLUMN_BELOW)
2735     {
2736       g_string_append_printf
2737          (clause,
2738           "%s"
2739           "(EXISTS"
2740           "  (SELECT * FROM tags"
2741           "   WHERE tags.uuid = '%s'"
2742           "   AND user_has_access_uuid (CAST ('tag' AS text),"
2743           "                             CAST (tags.uuid AS text),"
2744           "                             CAST ('get_tags' AS text),"
2745           "                             0)"
2746           "   AND EXISTS (SELECT * FROM tag_resources"
2747           "                WHERE tag_resources.resource_uuid"
2748           "                        = %ss.uuid"
2749           "                  AND tag_resources.resource_type"
2750           "                        = '%s'"
2751           "                  AND tag = tags.id)))",
2752           get_join (first_keyword, last_was_and,
2753                     last_was_not),
2754           quoted_keyword,
2755           type,
2756           type);
2757     }
2758   else if (keyword->relation == KEYWORD_RELATION_COLUMN_APPROX)
2759     {
2760       g_string_append_printf
2761          (clause,
2762           "%s"
2763           "(EXISTS"
2764           "  (SELECT * FROM tags"
2765           "   WHERE tags.uuid %s '%%%%%s%%%%'"
2766           "   AND tags.active != 0"
2767           "   AND user_has_access_uuid (CAST ('tag' AS text),"
2768           "                             CAST (tags.uuid AS text),"
2769           "                             CAST ('get_tags' AS text),"
2770           "                             0)"
2771           "   AND EXISTS (SELECT * FROM tag_resources"
2772           "                WHERE tag_resources.resource_uuid"
2773           "                        = %ss.uuid"
2774           "                  AND tag_resources.resource_type"
2775           "                        = '%s'"
2776           "                  AND tag = tags.id)))",
2777           get_join (first_keyword, last_was_and,
2778                     last_was_not),
2779           sql_ilike_op (),
2780           quoted_keyword,
2781           type,
2782           type);
2783     }
2784   else if (keyword->relation == KEYWORD_RELATION_COLUMN_REGEXP)
2785     {
2786       g_string_append_printf
2787          (clause,
2788           "%s"
2789           "(EXISTS"
2790           "  (SELECT * FROM tags"
2791           "   WHERE tags.uuid %s '%s'"
2792           "   AND tags.active != 0"
2793           "   AND user_has_access_uuid (CAST ('tag' AS text),"
2794           "                             CAST (tags.uuid AS text),"
2795           "                             CAST ('get_tags' AS text),"
2796           "                             0)"
2797           "   AND EXISTS (SELECT * FROM tag_resources"
2798           "                WHERE tag_resources.resource_uuid"
2799           "                        = %ss.uuid"
2800           "                  AND tag_resources.resource_type"
2801           "                        = '%s'"
2802           "                  AND tag = tags.id)))",
2803           get_join (first_keyword, last_was_and,
2804                     last_was_not),
2805           sql_regexp_op (),
2806           quoted_keyword,
2807           type,
2808           type);
2809     }
2810 
2811   g_free (quoted_keyword);
2812 }
2813 
2814 /**
2815  * @brief Return SQL WHERE clause for restricting a SELECT to a filter term.
2816  *
2817  * @param[in]  type     Resource type.
2818  * @param[in]  filter   Filter term.
2819  * @param[in]  filter_columns  Filter columns.
2820  * @param[in]  select_columns  SELECT columns.
2821  * @param[in]  where_columns   Columns in SQL that only appear in WHERE clause.
2822  * @param[out] trash           Whether the trash table is being queried.
2823  * @param[out] order_return  If given then order clause.
2824  * @param[out] first_return  If given then first row.
2825  * @param[out] max_return    If given then max rows.
2826  * @param[out] permissions   When given then permissions string vector.
2827  * @param[out] owner_filter  When given then value of owner keyword.
2828  *
2829  * @return WHERE clause for filter if one is required, else NULL.
2830  */
2831 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)2832 filter_clause (const char* type, const char* filter,
2833                const char **filter_columns, column_t *select_columns,
2834                column_t *where_columns, int trash, gchar **order_return,
2835                int *first_return, int *max_return, array_t **permissions,
2836                gchar **owner_filter)
2837 {
2838   GString *clause, *order;
2839   keyword_t **point;
2840   int first_keyword, first_order, last_was_and, last_was_not, last_was_re, skip;
2841   array_t *split;
2842 
2843   if (filter == NULL)
2844     filter = "";
2845 
2846   while (*filter && isspace (*filter)) filter++;
2847 
2848   if (permissions)
2849     *permissions = make_array ();
2850 
2851   if (owner_filter)
2852     *owner_filter = NULL;
2853 
2854   /* Add SQL to the clause for each keyword or phrase. */
2855 
2856   if (max_return)
2857     *max_return = -2;
2858 
2859   clause = g_string_new ("");
2860   order = g_string_new ("");
2861   /* NB This may add terms that are missing, like "sort". */
2862   split = split_filter (filter);
2863   point = (keyword_t**) split->pdata;
2864   first_keyword = 1;
2865   last_was_and = 0;
2866   last_was_not = 0;
2867   last_was_re = 0;
2868   first_order = 1;
2869   while (*point)
2870     {
2871       gchar *quoted_keyword;
2872       int index;
2873       keyword_t *keyword;
2874 
2875       skip = 0;
2876 
2877       keyword = *point;
2878 
2879       if ((keyword->column == NULL)
2880           && (strlen (keyword->string) == 0))
2881         {
2882           point++;
2883           continue;
2884         }
2885 
2886       if ((keyword->column == NULL)
2887           && (strcasecmp (keyword->string, "or") == 0))
2888         {
2889           point++;
2890           continue;
2891         }
2892 
2893       if ((keyword->column == NULL)
2894           && (strcasecmp (keyword->string, "and") == 0))
2895         {
2896           last_was_and = 1;
2897           point++;
2898           continue;
2899         }
2900 
2901       if ((keyword->column == NULL)
2902           && (strcasecmp (keyword->string, "not") == 0))
2903         {
2904           last_was_not = 1;
2905           point++;
2906           continue;
2907         }
2908 
2909       if ((keyword->column == NULL)
2910           && (strcasecmp (keyword->string, "re") == 0))
2911         {
2912           last_was_re = 1;
2913           point++;
2914           continue;
2915         }
2916 
2917       if ((keyword->column == NULL)
2918           && (strcasecmp (keyword->string, "regexp") == 0))
2919         {
2920           last_was_re = 1;
2921           point++;
2922           continue;
2923         }
2924 
2925       /* Check for ordering parts, like sort=name or sort-reverse=string. */
2926 
2927       if (keyword->column && (strcasecmp (keyword->column, "sort") == 0))
2928         {
2929           if (vector_find_filter (filter_columns, keyword->string) == 0)
2930             {
2931               point++;
2932               continue;
2933             }
2934 
2935           if (first_order)
2936             {
2937               if ((strcmp (type, "report") == 0)
2938                   && (strcmp (keyword->string, "status") == 0))
2939                 g_string_append_printf
2940                  (order,
2941                   " ORDER BY"
2942                   "  (CASE WHEN (SELECT target = 0 FROM tasks"
2943                   "              WHERE tasks.id = task)"
2944                   "    THEN 'Container'"
2945                   "    ELSE run_status_name (scan_run_status)"
2946                   "         || (SELECT CAST (temp / 100 AS text)"
2947                   "                    || CAST (temp / 10 AS text)"
2948                   "                    || CAST (temp %% 10 as text)"
2949                   "             FROM (SELECT report_progress (id) AS temp)"
2950                   "                  AS temp_sub)"
2951                   "    END)"
2952                   " ASC");
2953               else if ((strcmp (type, "task") == 0)
2954                        && (strcmp (keyword->string, "status") == 0))
2955                 g_string_append_printf
2956                  (order,
2957                   " ORDER BY"
2958                   "  (CASE WHEN target = 0"
2959                   "    THEN 'Container'"
2960                   "    ELSE run_status_name (run_status)"
2961                   "         || (SELECT CAST (temp / 100 AS text)"
2962                   "                    || CAST (temp / 10 AS text)"
2963                   "                    || CAST (temp %% 10 as text)"
2964                   "             FROM (SELECT report_progress (id) AS temp"
2965                   "                   FROM reports"
2966                   "                   WHERE task = tasks.id"
2967                   "                   ORDER BY date DESC LIMIT 1)"
2968                   "                  AS temp_sub)"
2969                   "    END)"
2970                   " ASC");
2971               else if ((strcmp (type, "task") == 0)
2972                        && (strcmp (keyword->string, "threat") == 0))
2973                 {
2974                   gchar *column;
2975                   column = columns_select_column (select_columns,
2976                                                   where_columns,
2977                                                   keyword->string);
2978                   assert (column);
2979                   g_string_append_printf (order,
2980                                           " ORDER BY order_threat (%s) ASC",
2981                                           column);
2982                 }
2983               else if (strcmp (keyword->string, "severity") == 0
2984                        || strcmp (keyword->string, "original_severity") == 0
2985                        || strcmp (keyword->string, "cvss") == 0
2986                        || strcmp (keyword->string, "cvss_base") == 0
2987                        || strcmp (keyword->string, "max_cvss") == 0
2988                        || strcmp (keyword->string, "fp_per_host") == 0
2989                        || strcmp (keyword->string, "log_per_host") == 0
2990                        || strcmp (keyword->string, "low_per_host") == 0
2991                        || strcmp (keyword->string, "medium_per_host") == 0
2992                        || strcmp (keyword->string, "high_per_host") == 0)
2993                 {
2994                   gchar *column;
2995                   column = columns_select_column (select_columns,
2996                                                   where_columns,
2997                                                   keyword->string);
2998                   g_string_append_printf (order,
2999                                           " ORDER BY CASE CAST (%s AS text)"
3000                                           " WHEN '' THEN '-Infinity'::real"
3001                                           " ELSE coalesce(%s::real,"
3002                                           "               '-Infinity'::real)"
3003                                           " END ASC",
3004                                           column,
3005                                           column);
3006                 }
3007               else if (strcmp (keyword->string, "roles") == 0)
3008                 {
3009                   gchar *column;
3010                   column = columns_select_column (select_columns,
3011                                                   where_columns,
3012                                                   keyword->string);
3013                   assert (column);
3014                   g_string_append_printf (order,
3015                                           " ORDER BY"
3016                                           " CASE WHEN %s %s 'Admin.*'"
3017                                           " THEN '0' || %s"
3018                                           " ELSE '1' || %s END ASC",
3019                                           column,
3020                                           sql_regexp_op (),
3021                                           column,
3022                                           column);
3023                 }
3024               else if ((strcmp (keyword->string, "created") == 0)
3025                        || (strcmp (keyword->string, "modified") == 0)
3026                        || (strcmp (keyword->string, "published") == 0)
3027                        || (strcmp (keyword->string, "qod") == 0)
3028                        || (strcmp (keyword->string, "cves") == 0)
3029                        || (strcmp (keyword->string, "high") == 0)
3030                        || (strcmp (keyword->string, "medium") == 0)
3031                        || (strcmp (keyword->string, "low") == 0)
3032                        || (strcmp (keyword->string, "log") == 0)
3033                        || (strcmp (keyword->string, "false_positive") == 0)
3034                        || (strcmp (keyword->string, "hosts") == 0)
3035                        || (strcmp (keyword->string, "result_hosts") == 0)
3036                        || (strcmp (keyword->string, "results") == 0)
3037                        || (strcmp (keyword->string, "latest_severity") == 0)
3038                        || (strcmp (keyword->string, "highest_severity") == 0)
3039                        || (strcmp (keyword->string, "average_severity") == 0))
3040                 {
3041                   gchar *column;
3042                   column = columns_select_column (select_columns,
3043                                                   where_columns,
3044                                                   keyword->string);
3045                   assert (column);
3046                   g_string_append_printf (order,
3047                                           " ORDER BY %s ASC",
3048                                           column);
3049                 }
3050               else if ((strcmp (keyword->string, "ips") == 0)
3051                        || (strcmp (keyword->string, "total") == 0)
3052                        || (strcmp (keyword->string, "tcp") == 0)
3053                        || (strcmp (keyword->string, "udp") == 0))
3054                 {
3055                   gchar *column;
3056                   column = columns_select_column (select_columns,
3057                                                   where_columns,
3058                                                   keyword->string);
3059                   assert (column);
3060                   g_string_append_printf (order,
3061                                           " ORDER BY CAST (%s AS INTEGER) ASC",
3062                                           column);
3063                 }
3064               else if (strcmp (keyword->string, "ip") == 0
3065                        || strcmp (keyword->string, "host") == 0)
3066                 {
3067                   gchar *column;
3068                   column = columns_select_column (select_columns,
3069                                                   where_columns,
3070                                                   keyword->string);
3071                   assert (column);
3072                   g_string_append_printf (order,
3073                                           " ORDER BY order_inet (%s) ASC",
3074                                           column);
3075                 }
3076               else if ((strcmp (type, "note")
3077                         && strcmp (type, "override"))
3078                        || (strcmp (keyword->string, "nvt")
3079                            && strcmp (keyword->string, "name")))
3080                 {
3081                   gchar *column;
3082                   keyword_type_t column_type;
3083                   column = columns_select_column_with_type (select_columns,
3084                                                             where_columns,
3085                                                             keyword->string,
3086                                                             &column_type);
3087                   assert (column);
3088                   if (column_type == KEYWORD_TYPE_INTEGER)
3089                     g_string_append_printf (order,
3090                                             " ORDER BY"
3091                                             " cast (%s AS bigint) ASC",
3092                                             column);
3093                   else if (column_type == KEYWORD_TYPE_DOUBLE)
3094                     g_string_append_printf (order,
3095                                             " ORDER BY"
3096                                             " cast (%s AS real) ASC",
3097                                             column);
3098                   else
3099                     g_string_append_printf (order, " ORDER BY lower (%s) ASC",
3100                                             column);
3101                 }
3102               else
3103                 /* Special case for notes text sorting. */
3104                 g_string_append_printf (order,
3105                                         " ORDER BY nvt ASC,"
3106                                         "          lower (%ss%s.text) ASC",
3107                                         type,
3108                                         trash ? "_trash" : "");
3109               first_order = 0;
3110             }
3111           else
3112             /* To help the client split_filter restricts the filter to one
3113              * sorting term, preventing this from happening. */
3114             g_string_append_printf (order, ", %s ASC",
3115                                     keyword->string);
3116           point++;
3117           continue;
3118         }
3119       else if (keyword->column
3120                && (strcasecmp (keyword->column, "sort-reverse") == 0))
3121         {
3122           if (vector_find_filter (filter_columns, keyword->string) == 0)
3123             {
3124               point++;
3125               continue;
3126             }
3127 
3128           if (first_order)
3129             {
3130               if ((strcmp (type, "report") == 0)
3131                   && (strcmp (keyword->string, "status") == 0))
3132                 g_string_append_printf
3133                  (order,
3134                   " ORDER BY"
3135                   "  (CASE WHEN (SELECT target = 0 FROM tasks"
3136                   "              WHERE tasks.id = task)"
3137                   "    THEN 'Container'"
3138                   "    ELSE run_status_name (scan_run_status)"
3139                   "         || (SELECT CAST (temp / 100 AS text)"
3140                   "                    || CAST (temp / 10 AS text)"
3141                   "                    || CAST (temp %% 10 as text)"
3142                   "             FROM (SELECT report_progress (id) AS temp)"
3143                   "                  AS temp_sub)"
3144                   "    END)"
3145                   " DESC");
3146               else if ((strcmp (type, "task") == 0)
3147                        && (strcmp (keyword->string, "status") == 0))
3148                 g_string_append_printf
3149                  (order,
3150                   " ORDER BY"
3151                   "  (CASE WHEN target = 0"
3152                   "    THEN 'Container'"
3153                   "    ELSE run_status_name (run_status)"
3154                   "         || (SELECT CAST (temp / 100 AS text)"
3155                   "                    || CAST (temp / 10 AS text)"
3156                   "                    || CAST (temp %% 10 as text)"
3157                   "             FROM (SELECT report_progress (id) AS temp"
3158                   "                   FROM reports"
3159                   "                   WHERE task = tasks.id"
3160                   "                   ORDER BY date DESC LIMIT 1)"
3161                   "                  AS temp_sub)"
3162                   "    END)"
3163                   " DESC");
3164               else if ((strcmp (type, "task") == 0)
3165                        && (strcmp (keyword->string, "threat") == 0))
3166                 {
3167                   gchar *column;
3168                   column = columns_select_column (select_columns,
3169                                                   where_columns,
3170                                                   keyword->string);
3171                   assert (column);
3172                   g_string_append_printf (order,
3173                                           " ORDER BY order_threat (%s) DESC",
3174                                           column);
3175                 }
3176               else if (strcmp (keyword->string, "severity") == 0
3177                        || strcmp (keyword->string, "original_severity") == 0
3178                        || strcmp (keyword->string, "cvss") == 0
3179                        || strcmp (keyword->string, "cvss_base") == 0
3180                        || strcmp (keyword->string, "max_cvss") == 0
3181                        || strcmp (keyword->string, "fp_per_host") == 0
3182                        || strcmp (keyword->string, "log_per_host") == 0
3183                        || strcmp (keyword->string, "low_per_host") == 0
3184                        || strcmp (keyword->string, "medium_per_host") == 0
3185                        || strcmp (keyword->string, "high_per_host") == 0)
3186                 {
3187                   gchar *column;
3188                   column = columns_select_column (select_columns,
3189                                                   where_columns,
3190                                                   keyword->string);
3191                   g_string_append_printf (order,
3192                                           " ORDER BY CASE CAST (%s AS text)"
3193                                           " WHEN '' THEN '-Infinity'::real"
3194                                           " ELSE coalesce(%s::real,"
3195                                           "               '-Infinity'::real)"
3196                                           " END DESC",
3197                                           column,
3198                                           column);
3199                 }
3200               else if (strcmp (keyword->string, "roles") == 0)
3201                 {
3202                   gchar *column;
3203                   column = columns_select_column (select_columns,
3204                                                   where_columns,
3205                                                   keyword->string);
3206                   assert (column);
3207                   g_string_append_printf (order,
3208                                           " ORDER BY"
3209                                           " CASE WHEN %s %s 'Admin.*'"
3210                                           " THEN '0' || %s"
3211                                           " ELSE '1' || %s END DESC",
3212                                           column,
3213                                           sql_regexp_op (),
3214                                           column,
3215                                           column);
3216                 }
3217               else if ((strcmp (keyword->string, "created") == 0)
3218                        || (strcmp (keyword->string, "modified") == 0)
3219                        || (strcmp (keyword->string, "published") == 0)
3220                        || (strcmp (keyword->string, "qod") == 0)
3221                        || (strcmp (keyword->string, "cves") == 0)
3222                        || (strcmp (keyword->string, "high") == 0)
3223                        || (strcmp (keyword->string, "medium") == 0)
3224                        || (strcmp (keyword->string, "low") == 0)
3225                        || (strcmp (keyword->string, "log") == 0)
3226                        || (strcmp (keyword->string, "false_positive") == 0)
3227                        || (strcmp (keyword->string, "hosts") == 0)
3228                        || (strcmp (keyword->string, "result_hosts") == 0)
3229                        || (strcmp (keyword->string, "results") == 0)
3230                        || (strcmp (keyword->string, "latest_severity") == 0)
3231                        || (strcmp (keyword->string, "highest_severity") == 0)
3232                        || (strcmp (keyword->string, "average_severity") == 0))
3233                 {
3234                   gchar *column;
3235                   column = columns_select_column (select_columns,
3236                                                   where_columns,
3237                                                   keyword->string);
3238                   assert (column);
3239                   g_string_append_printf (order,
3240                                           " ORDER BY %s DESC",
3241                                           column);
3242                 }
3243               else if ((strcmp (keyword->string, "ips") == 0)
3244                        || (strcmp (keyword->string, "total") == 0)
3245                        || (strcmp (keyword->string, "tcp") == 0)
3246                        || (strcmp (keyword->string, "udp") == 0))
3247                 {
3248                   gchar *column;
3249                   column = columns_select_column (select_columns,
3250                                                   where_columns,
3251                                                   keyword->string);
3252                   assert (column);
3253                   g_string_append_printf (order,
3254                                           " ORDER BY CAST (%s AS INTEGER) DESC",
3255                                           column);
3256                 }
3257               else if (strcmp (keyword->string, "ip") == 0
3258                        || strcmp (keyword->string, "host") == 0)
3259                 {
3260                   gchar *column;
3261                   column = columns_select_column (select_columns,
3262                                                   where_columns,
3263                                                   keyword->string);
3264                   assert (column);
3265                   g_string_append_printf (order,
3266                                           " ORDER BY order_inet (%s) DESC",
3267                                           column);
3268                 }
3269               else if ((strcmp (type, "note")
3270                         && strcmp (type, "override"))
3271                        || (strcmp (keyword->string, "nvt")
3272                            && strcmp (keyword->string, "name")))
3273                 {
3274                   gchar *column;
3275                   keyword_type_t column_type;
3276                   column = columns_select_column_with_type (select_columns,
3277                                                             where_columns,
3278                                                             keyword->string,
3279                                                             &column_type);
3280                   assert (column);
3281                   if (column_type == KEYWORD_TYPE_INTEGER)
3282                     g_string_append_printf (order,
3283                                             " ORDER BY"
3284                                             " cast (%s AS bigint) DESC",
3285                                             column);
3286                   else if (column_type == KEYWORD_TYPE_DOUBLE)
3287                     g_string_append_printf (order,
3288                                             " ORDER BY"
3289                                             " cast (%s AS real) DESC",
3290                                             column);
3291                   else
3292                     g_string_append_printf (order, " ORDER BY lower (%s) DESC",
3293                                             column);
3294                 }
3295               else
3296                 /* Special case for notes text sorting. */
3297                 g_string_append_printf (order,
3298                                         " ORDER BY nvt DESC,"
3299                                         "          lower (%ss%s.text) DESC",
3300                                         type,
3301                                         trash ? "_trash" : "");
3302               first_order = 0;
3303             }
3304           else
3305             /* To help the client split_filter restricts the filter to one
3306              * sorting term, preventing this from happening. */
3307             g_string_append_printf (order, ", %s DESC",
3308                                     keyword->string);
3309           point++;
3310           continue;
3311         }
3312       else if (keyword->column
3313                && (strcasecmp (keyword->column, "first") == 0))
3314         {
3315           if (first_return)
3316             {
3317               /* Subtract 1 to switch from 1 to 0 indexing. */
3318               *first_return = atoi (keyword->string) - 1;
3319               if (*first_return < 0)
3320                 *first_return = 0;
3321             }
3322 
3323           point++;
3324           continue;
3325         }
3326       else if (keyword->column
3327                && (strcasecmp (keyword->column, "rows") == 0))
3328         {
3329           if (max_return)
3330             *max_return = atoi (keyword->string);
3331 
3332           point++;
3333           continue;
3334         }
3335       else if (keyword->column
3336                && (strcasecmp (keyword->column, "permission") == 0))
3337         {
3338           if (permissions)
3339             array_add (*permissions, g_strdup (keyword->string));
3340 
3341           point++;
3342           continue;
3343         }
3344       /* Add tag criteria to clause: tag name with optional value */
3345       else if (keyword->column
3346                && (strcasecmp (keyword->column, "tag") == 0))
3347         {
3348           quoted_keyword = NULL;
3349 
3350           filter_clause_append_tag (clause, keyword, type,
3351                                     first_keyword, last_was_and, last_was_not);
3352 
3353           first_keyword = 0;
3354           last_was_and = 0;
3355           last_was_not = 0;
3356 
3357           point++;
3358           continue;
3359         }
3360       /* Add criteria for tag_id to clause */
3361       else if (keyword->column
3362                && (strcasecmp (keyword->column, "tag_id") == 0))
3363         {
3364           quoted_keyword = NULL;
3365 
3366           filter_clause_append_tag_id (clause, keyword, type, first_keyword,
3367                                        last_was_and, last_was_not);
3368 
3369           first_keyword = 0;
3370           last_was_and = 0;
3371           last_was_not = 0;
3372 
3373           point++;
3374           continue;
3375         }
3376 
3377       /* Add SQL to the clause for each column name. */
3378 
3379       quoted_keyword = NULL;
3380 
3381       if (keyword->relation == KEYWORD_RELATION_COLUMN_EQUAL)
3382         {
3383           if (vector_find_filter (filter_columns, keyword->column) == 0)
3384             {
3385               last_was_and = 0;
3386               last_was_not = 0;
3387               point++;
3388               continue;
3389             }
3390 
3391           if (keyword->column
3392               && (strlen (keyword->column) > 3)
3393               && (strcmp (keyword->column + strlen (keyword->column) - 3, "_id")
3394                   == 0)
3395               && strcasecmp (keyword->column, "nvt_id")
3396               /* Tickets have a custom result_id column. */
3397               && strcasecmp (keyword->column, "result_id"))
3398             {
3399               gchar *type_term;
3400 
3401               type_term = g_strndup (keyword->column,
3402                                      strlen (keyword->column) - 3);
3403               if (valid_type (type_term) == 0)
3404                 {
3405                   g_free (type_term);
3406                   last_was_and = 0;
3407                   last_was_not = 0;
3408                   point++;
3409                   continue;
3410                 }
3411 
3412               quoted_keyword = sql_quote (keyword->string);
3413               if (strcmp (quoted_keyword, ""))
3414                 g_string_append_printf (clause,
3415                                         "%s(((SELECT id FROM %ss"
3416                                         "     WHERE %ss.uuid = '%s')"
3417                                         "     = %ss.%s"
3418                                         "     OR %ss.%s IS NULL"
3419                                         "     OR %ss.%s = 0)",
3420                                         get_join (first_keyword,
3421                                                   last_was_and,
3422                                                   last_was_not),
3423                                         type_term,
3424                                         type_term,
3425                                         quoted_keyword,
3426                                         type,
3427                                         type_term,
3428                                         type,
3429                                         type_term,
3430                                         type,
3431                                         type_term);
3432               else
3433                 g_string_append_printf (clause,
3434                                         "%s((%ss.%s IS NULL"
3435                                         "   OR %ss.%s = 0)",
3436                                         get_join (first_keyword,
3437                                                   last_was_and,
3438                                                   last_was_not),
3439                                         type,
3440                                         type_term,
3441                                         type,
3442                                         type_term);
3443 
3444               g_free (type_term);
3445             }
3446           else if (keyword->column && strcmp (keyword->column, "owner"))
3447             {
3448               gchar *column;
3449               keyword_type_t column_type;
3450               quoted_keyword = sql_quote (keyword->string);
3451               column = columns_select_column_with_type (select_columns,
3452                                                         where_columns,
3453                                                         keyword->column,
3454                                                         &column_type);
3455               assert (column);
3456               if (keyword->type == KEYWORD_TYPE_INTEGER
3457                   && (column_type == KEYWORD_TYPE_INTEGER
3458                       || column_type == KEYWORD_TYPE_DOUBLE))
3459                 g_string_append_printf (clause,
3460                                         "%s(CAST (%s AS NUMERIC) = %i",
3461                                         get_join (first_keyword, last_was_and,
3462                                                   last_was_not),
3463                                         column,
3464                                         keyword->integer_value);
3465           else if (keyword->type == KEYWORD_TYPE_DOUBLE
3466                    && (column_type == KEYWORD_TYPE_DOUBLE
3467                        || column_type == KEYWORD_TYPE_INTEGER))
3468                 g_string_append_printf (clause,
3469                                         "%s(CAST (%s AS REAL)"
3470                                         " = CAST (%f AS REAL)",
3471                                         get_join (first_keyword, last_was_and,
3472                                                   last_was_not),
3473                                         column,
3474                                         keyword->double_value);
3475               else if (strcmp (quoted_keyword, ""))
3476                 g_string_append_printf (clause,
3477                                         "%s(CAST (%s AS TEXT) = '%s'",
3478                                         get_join (first_keyword, last_was_and,
3479                                                   last_was_not),
3480                                         column,
3481                                         quoted_keyword);
3482               else
3483                 g_string_append_printf (clause,
3484                                         "%s((%s IS NULL OR CAST (%s AS TEXT) = '%s')",
3485                                         get_join (first_keyword, last_was_and,
3486                                                   last_was_not),
3487                                         column,
3488                                         column,
3489                                         quoted_keyword);
3490             }
3491           else
3492             {
3493               /* Skip term.  Owner filtering is done via where_owned. */
3494               skip = 1;
3495               if (owner_filter && (*owner_filter == NULL))
3496                 *owner_filter = g_strdup (keyword->string);
3497             }
3498         }
3499       else if (keyword->relation == KEYWORD_RELATION_COLUMN_APPROX)
3500         {
3501           gchar *column;
3502 
3503           if (vector_find_filter (filter_columns, keyword->column) == 0)
3504             {
3505               last_was_and = 0;
3506               last_was_not = 0;
3507               point++;
3508               continue;
3509             }
3510 
3511           quoted_keyword = sql_quote (keyword->string);
3512           column = columns_select_column (select_columns,
3513                                           where_columns,
3514                                           keyword->column);
3515           assert (column);
3516           g_string_append_printf (clause,
3517                                   "%s(CAST (%s AS TEXT) %s '%%%%%s%%%%'",
3518                                   get_join (first_keyword, last_was_and,
3519                                             last_was_not),
3520                                   column,
3521                                   sql_ilike_op (),
3522                                   quoted_keyword);
3523         }
3524       else if (keyword->relation == KEYWORD_RELATION_COLUMN_ABOVE)
3525         {
3526           gchar *column;
3527           keyword_type_t column_type;
3528 
3529           if (vector_find_filter (filter_columns, keyword->column) == 0)
3530             {
3531               last_was_and = 0;
3532               last_was_not = 0;
3533               point++;
3534               continue;
3535             }
3536 
3537           quoted_keyword = sql_quote (keyword->string);
3538           column = columns_select_column_with_type (select_columns,
3539                                                     where_columns,
3540                                                     keyword->column,
3541                                                     &column_type);
3542           assert (column);
3543           if (keyword->type == KEYWORD_TYPE_INTEGER
3544               && (column_type == KEYWORD_TYPE_INTEGER
3545                   || column_type == KEYWORD_TYPE_DOUBLE))
3546             g_string_append_printf (clause,
3547                                     "%s(CAST (%s AS NUMERIC) > %i",
3548                                     get_join (first_keyword, last_was_and,
3549                                               last_was_not),
3550                                     column,
3551                                     keyword->integer_value);
3552           else if (keyword->type == KEYWORD_TYPE_DOUBLE
3553                    && (column_type == KEYWORD_TYPE_DOUBLE
3554                        || column_type == KEYWORD_TYPE_INTEGER))
3555             g_string_append_printf (clause,
3556                                     "%s(CAST (%s AS REAL)"
3557                                     " > CAST (%f AS REAL)",
3558                                     get_join (first_keyword, last_was_and,
3559                                               last_was_not),
3560                                     column,
3561                                     keyword->double_value);
3562           else
3563             g_string_append_printf (clause,
3564                                     "%s(CAST (%s AS TEXT) > '%s'",
3565                                     get_join (first_keyword, last_was_and,
3566                                               last_was_not),
3567                                     column,
3568                                     quoted_keyword);
3569         }
3570       else if (keyword->relation == KEYWORD_RELATION_COLUMN_BELOW)
3571         {
3572           gchar *column;
3573           keyword_type_t column_type;
3574 
3575           if (vector_find_filter (filter_columns, keyword->column) == 0)
3576             {
3577               last_was_and = 0;
3578               last_was_not = 0;
3579               point++;
3580               continue;
3581             }
3582 
3583           quoted_keyword = sql_quote (keyword->string);
3584           column = columns_select_column_with_type (select_columns,
3585                                                     where_columns,
3586                                                     keyword->column,
3587                                                     &column_type);
3588           assert (column);
3589           if (keyword->type == KEYWORD_TYPE_INTEGER
3590               && (column_type == KEYWORD_TYPE_INTEGER
3591                   || column_type == KEYWORD_TYPE_DOUBLE))
3592             g_string_append_printf (clause,
3593                                     "%s(CAST (%s AS NUMERIC) < %i",
3594                                     get_join (first_keyword, last_was_and,
3595                                               last_was_not),
3596                                     column,
3597                                     keyword->integer_value);
3598           else if (keyword->type == KEYWORD_TYPE_DOUBLE
3599                    && (column_type == KEYWORD_TYPE_DOUBLE
3600                        || column_type == KEYWORD_TYPE_INTEGER))
3601             g_string_append_printf (clause,
3602                                     "%s(CAST (%s AS REAL)"
3603                                     " < CAST (%f AS REAL)",
3604                                     get_join (first_keyword, last_was_and,
3605                                               last_was_not),
3606                                     column,
3607                                     keyword->double_value);
3608           else
3609             g_string_append_printf (clause,
3610                                     "%s(CAST (%s AS TEXT) < '%s'",
3611                                     get_join (first_keyword, last_was_and,
3612                                               last_was_not),
3613                                     column,
3614                                     quoted_keyword);
3615         }
3616       else if (keyword->relation == KEYWORD_RELATION_COLUMN_REGEXP)
3617         {
3618           gchar *column;
3619 
3620           if (vector_find_filter (filter_columns, keyword->column) == 0)
3621             {
3622               last_was_and = 0;
3623               last_was_not = 0;
3624               point++;
3625               continue;
3626             }
3627 
3628           quoted_keyword = sql_quote (keyword->string);
3629           column = columns_select_column (select_columns,
3630                                           where_columns,
3631                                           keyword->column);
3632           assert (column);
3633           g_string_append_printf (clause,
3634                                   "%s(CAST (%s AS TEXT) %s '%s'",
3635                                   get_join (first_keyword, last_was_and,
3636                                             last_was_not),
3637                                   column,
3638                                   sql_regexp_op (),
3639                                   quoted_keyword);
3640         }
3641       else if (keyword->equal)
3642         {
3643           const char *filter_column;
3644 
3645           /* Keyword like "=example". */
3646 
3647           g_string_append_printf (clause,
3648                                   "%s(",
3649                                   (first_keyword
3650                                     ? ""
3651                                     : (last_was_and ? " AND " : " OR ")));
3652 
3653           quoted_keyword = sql_quote (keyword->string);
3654           if (last_was_not)
3655             for (index = 0;
3656                  (filter_column = filter_columns[index]) != NULL;
3657                  index++)
3658               {
3659                 gchar *select_column;
3660                 keyword_type_t column_type;
3661 
3662                 select_column = columns_select_column_with_type (select_columns,
3663                                                                  where_columns,
3664                                                                  filter_column,
3665                                                                  &column_type);
3666                 assert (select_column);
3667 
3668                 if (keyword->type == KEYWORD_TYPE_INTEGER
3669                     && (column_type == KEYWORD_TYPE_INTEGER
3670                         || column_type == KEYWORD_TYPE_DOUBLE))
3671                   g_string_append_printf (clause,
3672                                           "%s"
3673                                           "(%s IS NULL"
3674                                           " OR CAST (%s AS NUMERIC)"
3675                                           "    != %i)",
3676                                           (index ? " AND " : ""),
3677                                           select_column,
3678                                           select_column,
3679                                           keyword->integer_value);
3680                 else if (keyword->type == KEYWORD_TYPE_DOUBLE
3681                          && (column_type == KEYWORD_TYPE_DOUBLE
3682                              || column_type == KEYWORD_TYPE_INTEGER))
3683                   g_string_append_printf (clause,
3684                                           "%s"
3685                                           "(%s IS NULL"
3686                                           " OR CAST (%s AS REAL)"
3687                                           "    != CAST (%f AS REAL))",
3688                                           (index ? " AND " : ""),
3689                                           select_column,
3690                                           select_column,
3691                                           keyword->double_value);
3692                 else
3693                   g_string_append_printf (clause,
3694                                           "%s"
3695                                           "(%s IS NULL"
3696                                           " OR CAST (%s AS TEXT)"
3697                                           "    != '%s')",
3698                                           (index ? " AND " : ""),
3699                                           select_column,
3700                                           select_column,
3701                                           quoted_keyword);
3702               }
3703           else
3704             for (index = 0;
3705                  (filter_column = filter_columns[index]) != NULL;
3706                  index++)
3707               {
3708                 gchar *select_column;
3709                 keyword_type_t column_type;
3710 
3711                 select_column = columns_select_column_with_type (select_columns,
3712                                                                  where_columns,
3713                                                                  filter_column,
3714                                                                  &column_type);
3715                 assert (select_column);
3716 
3717                 if (keyword->type == KEYWORD_TYPE_INTEGER
3718                     && (column_type == KEYWORD_TYPE_INTEGER
3719                         || column_type == KEYWORD_TYPE_DOUBLE))
3720                   g_string_append_printf (clause,
3721                                           "%sCAST (%s AS NUMERIC)"
3722                                           " = %i",
3723                                           (index ? " OR " : ""),
3724                                           select_column,
3725                                           keyword->integer_value);
3726                 else if (keyword->type == KEYWORD_TYPE_DOUBLE
3727                          && (column_type == KEYWORD_TYPE_DOUBLE
3728                              || column_type == KEYWORD_TYPE_INTEGER))
3729                   g_string_append_printf (clause,
3730                                           "%sCAST (%s AS REAL)"
3731                                           " = CAST (%f AS REAL)",
3732                                           (index ? " OR " : ""),
3733                                           select_column,
3734                                           keyword->double_value);
3735                 else
3736                   g_string_append_printf (clause,
3737                                           "%sCAST (%s AS TEXT)"
3738                                           " = '%s'",
3739                                           (index ? " OR " : ""),
3740                                           select_column,
3741                                           quoted_keyword);
3742               }
3743         }
3744       else
3745         {
3746           const char *filter_column;
3747 
3748           g_string_append_printf (clause,
3749                                   "%s(",
3750                                   (first_keyword
3751                                     ? ""
3752                                     : (last_was_and ? " AND " : " OR ")));
3753 
3754           quoted_keyword = sql_quote (keyword->string);
3755           if (last_was_not)
3756             for (index = 0;
3757                  (filter_column = filter_columns[index]) != NULL;
3758                  index++)
3759               {
3760                 gchar *select_column;
3761                 keyword_type_t column_type;
3762                 int column_type_matches = 0;
3763 
3764                 select_column = columns_select_column_with_type (select_columns,
3765                                                                  where_columns,
3766                                                                  filter_column,
3767                                                                  &column_type);
3768 
3769                 if (column_type != KEYWORD_TYPE_INTEGER
3770                     && column_type != KEYWORD_TYPE_DOUBLE)
3771                   column_type_matches = 1;
3772 
3773                 if (keyword_applies_to_column (keyword, filter_column)
3774                     && select_column && column_type_matches)
3775                   {
3776                     if (last_was_re)
3777                       g_string_append_printf (clause,
3778                                               "%s"
3779                                               "(%s IS NULL"
3780                                               " OR NOT (CAST (%s AS TEXT)"
3781                                               "         %s '%s'))",
3782                                               (index ? " AND " : ""),
3783                                               select_column,
3784                                               select_column,
3785                                               sql_regexp_op (),
3786                                               quoted_keyword);
3787                     else
3788                       g_string_append_printf (clause,
3789                                               "%s"
3790                                               "(%s IS NULL"
3791                                               " OR CAST (%s AS TEXT)"
3792                                               "    NOT %s '%%%s%%')",
3793                                               (index ? " AND " : ""),
3794                                               select_column,
3795                                               select_column,
3796                                               sql_ilike_op (),
3797                                               quoted_keyword);
3798                   }
3799                 else
3800                   g_string_append_printf (clause,
3801                                           "%s t ()",
3802                                           (index ? " AND " : ""));
3803               }
3804           else
3805             for (index = 0;
3806                  (filter_column = filter_columns[index]) != NULL;
3807                  index++)
3808               {
3809                 gchar *select_column;
3810                 keyword_type_t column_type;
3811                 int column_type_matches = 0;
3812 
3813                 select_column = columns_select_column_with_type (select_columns,
3814                                                                  where_columns,
3815                                                                  filter_column,
3816                                                                  &column_type);
3817                 if (column_type != KEYWORD_TYPE_INTEGER
3818                     && column_type != KEYWORD_TYPE_DOUBLE)
3819                   column_type_matches = 1;
3820 
3821                 if (keyword_applies_to_column (keyword, filter_column)
3822                     && select_column && column_type_matches)
3823                   g_string_append_printf (clause,
3824                                           "%sCAST (%s AS TEXT)"
3825                                           " %s '%s%s%s'",
3826                                           (index ? " OR " : ""),
3827                                           select_column,
3828                                           last_was_re
3829                                            ? sql_regexp_op ()
3830                                            : sql_ilike_op (),
3831                                           last_was_re ? "" : "%%",
3832                                           quoted_keyword,
3833                                           last_was_re ? "" : "%%");
3834                 else
3835                   g_string_append_printf (clause,
3836                                           "%snot t ()",
3837                                           (index ? " OR " : ""));
3838               }
3839         }
3840 
3841       if (skip == 0)
3842         {
3843           g_string_append (clause, ")");
3844           first_keyword = 0;
3845           last_was_and = 0;
3846           last_was_not = 0;
3847           last_was_re = 0;
3848         }
3849       g_free (quoted_keyword);
3850       point++;
3851     }
3852   filter_free (split);
3853 
3854   if (order_return)
3855     *order_return = g_string_free (order, FALSE);
3856   else
3857     g_string_free (order, TRUE);
3858 
3859   if (max_return)
3860     {
3861       if (*max_return == -2)
3862         setting_value_int (SETTING_UUID_ROWS_PER_PAGE, max_return);
3863       else if (*max_return < 1)
3864         *max_return = -1;
3865 
3866       *max_return = manage_max_rows (*max_return);
3867     }
3868 
3869   if (strlen (clause->str))
3870     return g_string_free (clause, FALSE);
3871   g_string_free (clause, TRUE);
3872   return NULL;
3873 }
3874 
3875 
3876 /* Resources. */
3877 
3878 /**
3879  * @brief Check whether a resource type name is valid.
3880  *
3881  * @param[in]  type  Type of resource.
3882  *
3883  * @return 1 yes, 0 no.
3884  */
3885 int
valid_type(const char * type)3886 valid_type (const char* type)
3887 {
3888   return (strcasecmp (type, "alert") == 0)
3889          || (strcasecmp (type, "asset") == 0)
3890          || (strcasecmp (type, "config") == 0)
3891          || (strcasecmp (type, "credential") == 0)
3892          || (strcasecmp (type, "filter") == 0)
3893          || (strcasecmp (type, "group") == 0)
3894          || (strcasecmp (type, "host") == 0)
3895          || (strcasecmp (type, "info") == 0)
3896          || (strcasecmp (type, "note") == 0)
3897          || (strcasecmp (type, "os") == 0)
3898          || (strcasecmp (type, "override") == 0)
3899          || (strcasecmp (type, "permission") == 0)
3900          || (strcasecmp (type, "port_list") == 0)
3901          || (strcasecmp (type, "report") == 0)
3902          || (strcasecmp (type, "report_format") == 0)
3903          || (strcasecmp (type, "result") == 0)
3904          || (strcasecmp (type, "role") == 0)
3905          || (strcasecmp (type, "scanner") == 0)
3906          || (strcasecmp (type, "schedule") == 0)
3907          || (strcasecmp (type, "tag") == 0)
3908          || (strcasecmp (type, "target") == 0)
3909          || (strcasecmp (type, "task") == 0)
3910          || (strcasecmp (type, "ticket") == 0)
3911          || (strcasecmp (type, "tls_certificate") == 0)
3912          || (strcasecmp (type, "user") == 0)
3913          || (strcasecmp (type, "vuln") == 0);
3914 }
3915 
3916 /**
3917  * @brief Return DB name of type.
3918  *
3919  * @param[in]  type  Database or pretty name.
3920  *
3921  * @return Database name of type if possible, else NULL.
3922  */
3923 static const char *
type_db_name(const char * type)3924 type_db_name (const char* type)
3925 {
3926   if (type == NULL)
3927     return NULL;
3928 
3929   if (valid_type (type))
3930     return type;
3931 
3932   if (strcasecmp (type, "Alert") == 0)
3933     return "alert";
3934   if (strcasecmp (type, "Asset") == 0)
3935     return "asset";
3936   if (strcasecmp (type, "Config") == 0)
3937     return "config";
3938   if (strcasecmp (type, "Credential") == 0)
3939     return "credential";
3940   if (strcasecmp (type, "Filter") == 0)
3941     return "filter";
3942   if (strcasecmp (type, "Note") == 0)
3943     return "note";
3944   if (strcasecmp (type, "Override") == 0)
3945     return "override";
3946   if (strcasecmp (type, "Permission") == 0)
3947     return "permission";
3948   if (strcasecmp (type, "Port List") == 0)
3949     return "port_list";
3950   if (strcasecmp (type, "Report") == 0)
3951     return "report";
3952   if (strcasecmp (type, "Report Format") == 0)
3953     return "report_format";
3954   if (strcasecmp (type, "Result") == 0)
3955     return "result";
3956   if (strcasecmp (type, "Role") == 0)
3957     return "role";
3958   if (strcasecmp (type, "Scanner") == 0)
3959     return "scanner";
3960   if (strcasecmp (type, "Schedule") == 0)
3961     return "schedule";
3962   if (strcasecmp (type, "Tag") == 0)
3963     return "tag";
3964   if (strcasecmp (type, "Target") == 0)
3965     return "target";
3966   if (strcasecmp (type, "Task") == 0)
3967     return "task";
3968   if (strcasecmp (type, "Ticket") == 0)
3969     return "ticket";
3970   if (strcasecmp (type, "TLS Certificate") == 0)
3971     return "tls_certificate";
3972   if (strcasecmp (type, "SecInfo") == 0)
3973     return "info";
3974   return NULL;
3975 }
3976 
3977 /**
3978  * @brief Check whether a resource type is an asset subtype.
3979  *
3980  * @param[in]  type  Type of resource.
3981  *
3982  * @return 1 yes, 0 no.
3983  */
3984 static int
type_is_asset_subtype(const char * type)3985 type_is_asset_subtype (const char *type)
3986 {
3987   return (strcasecmp (type, "host")
3988           && strcasecmp (type, "os"))
3989          == 0;
3990 }
3991 
3992 /**
3993  * @brief Check whether a resource type is an info subtype.
3994  *
3995  * @param[in]  type  Type of resource.
3996  *
3997  * @return 1 yes, 0 no.
3998  */
3999 static int
type_is_info_subtype(const char * type)4000 type_is_info_subtype (const char *type)
4001 {
4002   return (strcasecmp (type, "nvt")
4003           && strcasecmp (type, "cve")
4004           && strcasecmp (type, "cpe")
4005           && strcasecmp (type, "ovaldef")
4006           && strcasecmp (type, "cert_bund_adv")
4007           && strcasecmp (type, "dfn_cert_adv"))
4008          == 0;
4009 }
4010 
4011 /**
4012  * @brief Check whether a type has a name and comment.
4013  *
4014  * @param[in]  type          Type of resource.
4015  *
4016  * @return 1 yes, 0 no.
4017  */
4018 static int
type_named(const char * type)4019 type_named (const char *type)
4020 {
4021   return strcasecmp (type, "note")
4022          && strcasecmp (type, "override");
4023 }
4024 
4025 /**
4026  * @brief Check whether a type must have globally unique names.
4027  *
4028  * @param[in]  type          Type of resource.
4029  *
4030  * @return 1 yes, 0 no.
4031  */
4032 static int
type_globally_unique(const char * type)4033 type_globally_unique (const char *type)
4034 {
4035   if (strcasecmp (type, "user") == 0)
4036     return 1;
4037   else
4038     return 0;
4039 }
4040 
4041 /**
4042  * @brief Check whether a type has a comment.
4043  *
4044  * @param[in]  type  Type of resource.
4045  *
4046  * @return 1 yes, 0 no.
4047  */
4048 static int
type_has_comment(const char * type)4049 type_has_comment (const char *type)
4050 {
4051   return strcasecmp (type, "report_format");
4052 }
4053 
4054 /**
4055  * @brief Check whether a resource type uses the trashcan.
4056  *
4057  * @param[in]  type  Type of resource.
4058  *
4059  * @return 1 yes, 0 no.
4060  */
4061 static int
type_has_trash(const char * type)4062 type_has_trash (const char *type)
4063 {
4064   return strcasecmp (type, "report")
4065          && strcasecmp (type, "result")
4066          && strcasecmp (type, "info")
4067          && type_is_info_subtype (type) == 0
4068          && strcasecmp (type, "vuln")
4069          && strcasecmp (type, "user")
4070          && strcasecmp (type, "tls_certificate");
4071 }
4072 
4073 /**
4074  * @brief Check whether a resource type has an owner.
4075  *
4076  * @param[in]  type  Type of resource.
4077  *
4078  * @return 1 yes, 0 no.
4079  */
4080 static int
type_owned(const char * type)4081 type_owned (const char* type)
4082 {
4083   return strcasecmp (type, "info")
4084          && type_is_info_subtype (type) == 0
4085          && strcasecmp (type, "vuln");
4086 }
4087 
4088 /**
4089  * @brief Check whether the trash is in the real table.
4090  *
4091  * @param[in]  type  Type of resource.
4092  *
4093  * @return 1 yes, 0 no.
4094  */
4095 static int
type_trash_in_table(const char * type)4096 type_trash_in_table (const char *type)
4097 {
4098   return strcasecmp (type, "task") == 0;
4099 }
4100 
4101 /* TODO Only used by find_scanner, find_permission and check_permission_args. */
4102 /**
4103  * @brief Find a resource given a UUID.
4104  *
4105  * This only looks for resources owned (or effectively owned) by the current user.
4106  * So no shared resources and no globals.
4107  *
4108  * @param[in]   type       Type of resource.
4109  * @param[in]   uuid       UUID of resource.
4110  * @param[out]  resource   Resource return, 0 if successfully failed to find resource.
4111  *
4112  * @return FALSE on success (including if failed to find resource), TRUE on error.
4113  */
4114 gboolean
find_resource(const char * type,const char * uuid,resource_t * resource)4115 find_resource (const char* type, const char* uuid, resource_t* resource)
4116 {
4117   gchar *quoted_uuid;
4118   quoted_uuid = sql_quote (uuid);
4119   if (acl_user_owns_uuid (type, quoted_uuid, 0) == 0)
4120     {
4121       g_free (quoted_uuid);
4122       *resource = 0;
4123       return FALSE;
4124     }
4125   // TODO should really check type
4126   switch (sql_int64 (resource,
4127                      "SELECT id FROM %ss WHERE uuid = '%s'%s;",
4128                      type,
4129                      quoted_uuid,
4130                      strcmp (type, "task") ? "" : " AND hidden < 2"))
4131     {
4132       case 0:
4133         break;
4134       case 1:        /* Too few rows in result of query. */
4135         *resource = 0;
4136         break;
4137       default:       /* Programming error. */
4138         assert (0);
4139       case -1:
4140         g_free (quoted_uuid);
4141         return TRUE;
4142         break;
4143     }
4144 
4145   g_free (quoted_uuid);
4146   return FALSE;
4147 }
4148 
4149 /**
4150  * @brief Find a resource given a UUID and a permission.
4151  *
4152  * @param[in]   type        Type of resource.
4153  * @param[in]   uuid        UUID of resource.
4154  * @param[out]  resource    Resource return, 0 if successfully failed to find
4155  *                          resource.
4156  * @param[in]   permission  Permission.
4157  * @param[in]   trash       Whether resource is in trashcan.
4158  *
4159  * @return FALSE on success (including if failed to find resource), TRUE on
4160  *         error.
4161  */
4162 gboolean
find_resource_with_permission(const char * type,const char * uuid,resource_t * resource,const char * permission,int trash)4163 find_resource_with_permission (const char* type, const char* uuid,
4164                                resource_t* resource, const char *permission,
4165                                int trash)
4166 {
4167   gchar *quoted_uuid;
4168   if (uuid == NULL)
4169     return TRUE;
4170   if ((type == NULL) || (valid_db_resource_type (type) == 0))
4171     return TRUE;
4172   quoted_uuid = sql_quote (uuid);
4173   if (acl_user_has_access_uuid (type, quoted_uuid, permission, trash) == 0)
4174     {
4175       g_free (quoted_uuid);
4176       *resource = 0;
4177       return FALSE;
4178     }
4179   switch (sql_int64 (resource,
4180                      "SELECT id FROM %ss%s WHERE uuid = '%s'%s%s;",
4181                      type,
4182                      (trash && strcmp (type, "task") && strcmp (type, "report"))
4183                       ? "_trash"
4184                       : "",
4185                      quoted_uuid,
4186                      strcmp (type, "task")
4187                       ? ""
4188                       : (trash ? " AND hidden = 2" : " AND hidden < 2"),
4189                      strcmp (type, "report")
4190                       ? ""
4191                       : (trash
4192                           ? " AND (SELECT hidden FROM tasks"
4193                             "      WHERE tasks.id = task)"
4194                             "     = 2"
4195                           : " AND (SELECT hidden FROM tasks"
4196                           "        WHERE tasks.id = task)"
4197                           "       = 0")))
4198     {
4199       case 0:
4200         break;
4201       case 1:        /* Too few rows in result of query. */
4202         *resource = 0;
4203         break;
4204       default:       /* Programming error. */
4205         assert (0);
4206       case -1:
4207         g_free (quoted_uuid);
4208         return TRUE;
4209         break;
4210     }
4211 
4212   g_free (quoted_uuid);
4213   return FALSE;
4214 }
4215 
4216 /**
4217  * @brief Find a resource given a name.
4218  *
4219  * @param[in]   type      Type of resource.
4220  * @param[in]   name      A resource name.
4221  * @param[out]  resource  Resource return, 0 if successfully failed to find
4222  *                        resource.
4223  *
4224  * @return FALSE on success (including if failed to find resource), TRUE on
4225  *         error.
4226  */
4227 static gboolean
find_resource_by_name(const char * type,const char * name,resource_t * resource)4228 find_resource_by_name (const char* type, const char* name, resource_t *resource)
4229 {
4230   gchar *quoted_name;
4231   quoted_name = sql_quote (name);
4232   // TODO should really check type
4233   switch (sql_int64 (resource,
4234                      "SELECT id FROM %ss WHERE name = '%s'"
4235                      " ORDER BY id DESC;",
4236                      type,
4237                      quoted_name))
4238     {
4239       case 0:
4240         break;
4241       case 1:        /* Too few rows in result of query. */
4242         *resource = 0;
4243         break;
4244       default:       /* Programming error. */
4245         assert (0);
4246       case -1:
4247         g_free (quoted_name);
4248         return TRUE;
4249         break;
4250     }
4251 
4252   g_free (quoted_name);
4253   return FALSE;
4254 }
4255 
4256 /**
4257  * @brief Find a resource given a UUID and a permission.
4258  *
4259  * @param[in]   type        Type of resource.
4260  * @param[in]   name        Name of resource.
4261  * @param[out]  resource    Resource return, 0 if successfully failed to find
4262  *                          resource.
4263  * @param[in]   permission  Permission.
4264  *
4265  * @return FALSE on success (including if failed to find resource), TRUE on
4266  *         error.
4267  */
4268 static gboolean
find_resource_by_name_with_permission(const char * type,const char * name,resource_t * resource,const char * permission)4269 find_resource_by_name_with_permission (const char *type, const char *name,
4270                                        resource_t *resource,
4271                                        const char *permission)
4272 {
4273   gchar *quoted_name;
4274   assert (strcmp (type, "task"));
4275   if (name == NULL)
4276     return TRUE;
4277   quoted_name = sql_quote (name);
4278   // TODO should really check type
4279   switch (sql_int64 (resource,
4280                      "SELECT id FROM %ss WHERE name = '%s'"
4281                      " ORDER BY id DESC;",
4282                      type,
4283                      quoted_name))
4284     {
4285       case 0:
4286         {
4287           gchar *uuid;
4288 
4289           uuid = sql_string ("SELECT uuid FROM %ss WHERE id = %llu;",
4290                              type, *resource);
4291           if (acl_user_has_access_uuid (type, uuid, permission, 0) == 0)
4292             {
4293               g_free (uuid);
4294               g_free (quoted_name);
4295               *resource = 0;
4296               return FALSE;
4297             }
4298           g_free (uuid);
4299         }
4300         break;
4301       case 1:        /* Too few rows in result of query. */
4302         *resource = 0;
4303         break;
4304       default:       /* Programming error. */
4305         assert (0);
4306       case -1:
4307         g_free (quoted_name);
4308         return TRUE;
4309         break;
4310     }
4311 
4312   g_free (quoted_name);
4313   return FALSE;
4314 }
4315 
4316 /**
4317  * @brief Create a resource from an existing resource.
4318  *
4319  * @param[in]  type          Type of resource.
4320  * @param[in]  name          Name of new resource.  NULL to copy from existing.
4321  * @param[in]  comment       Comment on new resource.  NULL to copy from existing.
4322  * @param[in]  resource_id   UUID of existing resource.
4323  * @param[in]  columns       Extra columns in resource.
4324  * @param[in]  make_name_unique  When name NULL, whether to make existing name
4325  *                               unique.
4326  * @param[out] new_resource  Address for new resource, or NULL.
4327  * @param[out] old_resource  Address for existing resource, or NULL.
4328  *
4329  * @return 0 success, 1 resource exists already, 2 failed to find existing
4330  *         resource, 99 permission denied, -1 error.
4331  */
4332 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)4333 copy_resource_lock (const char *type, const char *name, const char *comment,
4334                     const char *resource_id, const char *columns,
4335                     int make_name_unique, resource_t* new_resource,
4336                     resource_t *old_resource)
4337 {
4338   gchar *quoted_name, *quoted_uuid, *uniquify, *command;
4339   int named, globally_unique;
4340   user_t owner;
4341   resource_t resource;
4342   resource_t new;
4343   int ret = -1;
4344 
4345   if (resource_id == NULL)
4346     return -1;
4347 
4348   command = g_strdup_printf ("create_%s", type);
4349   if (acl_user_may (command) == 0)
4350     {
4351       g_free (command);
4352       return 99;
4353     }
4354   g_free (command);
4355 
4356   command = g_strdup_printf ("get_%ss", type);
4357   if (find_resource_with_permission (type, resource_id, &resource, command, 0))
4358     {
4359       g_free (command);
4360       return -1;
4361     }
4362   g_free (command);
4363 
4364   if (resource == 0)
4365     return 2;
4366 
4367   if (find_user_by_name (current_credentials.username, &owner)
4368       || owner == 0)
4369     {
4370       return -1;
4371     }
4372 
4373   if (strcmp (type, "permission") == 0)
4374     {
4375       resource_t perm_resource;
4376       perm_resource = permission_resource (resource);
4377       if ((perm_resource == 0)
4378           && (acl_user_can_everything (current_credentials.uuid) == 0))
4379         /* Only admins can copy permissions that apply to whole commands. */
4380         return 99;
4381     }
4382 
4383   named = type_named (type);
4384   globally_unique = type_globally_unique (type);
4385 
4386   if (named && name && *name && resource_with_name_exists (name, type, 0))
4387     return 1;
4388 
4389   if ((strcmp (type, "tls_certificate") == 0)
4390       && user_has_tls_certificate (resource, owner))
4391     return 1;
4392 
4393   if (name && *name)
4394     quoted_name = sql_quote (name);
4395   else
4396     quoted_name = NULL;
4397   quoted_uuid = sql_quote (resource_id);
4398 
4399   /* Copy the existing resource. */
4400 
4401   if (globally_unique && make_name_unique)
4402     uniquify = g_strdup_printf ("uniquify ('%s', name, NULL, '%cClone')",
4403                                 type,
4404                                 strcmp (type, "user") ? ' ' : '_');
4405   else if (make_name_unique)
4406     uniquify = g_strdup_printf ("uniquify ('%s', name, %llu, ' Clone')",
4407                                 type,
4408                                 owner);
4409   else
4410     uniquify = g_strdup ("name");
4411   if (named && comment && strlen (comment))
4412     {
4413       gchar *quoted_comment;
4414       quoted_comment = sql_nquote (comment, strlen (comment));
4415       ret = sql_error ("INSERT INTO %ss"
4416                        " (uuid, owner, name, comment,"
4417                        "  creation_time, modification_time%s%s)"
4418                        " SELECT make_uuid (),"
4419                        "        (SELECT id FROM users"
4420                        "         where users.uuid = '%s'),"
4421                        "        %s%s%s, '%s', m_now (), m_now ()%s%s"
4422                        " FROM %ss WHERE uuid = '%s';",
4423                        type,
4424                        columns ? ", " : "",
4425                        columns ? columns : "",
4426                        current_credentials.uuid,
4427                        quoted_name ? "'" : "",
4428                        quoted_name ? quoted_name : uniquify,
4429                        quoted_name ? "'" : "",
4430                        quoted_comment,
4431                        columns ? ", " : "",
4432                        columns ? columns : "",
4433                        type,
4434                        quoted_uuid);
4435       g_free (quoted_comment);
4436     }
4437   else if (named)
4438     ret = sql_error ("INSERT INTO %ss"
4439                       " (uuid, owner, name%s,"
4440                       "  creation_time, modification_time%s%s)"
4441                       " SELECT make_uuid (),"
4442                       "        (SELECT id FROM users where users.uuid = '%s'),"
4443                       "        %s%s%s%s, m_now (), m_now ()%s%s"
4444                       " FROM %ss WHERE uuid = '%s';",
4445                       type,
4446                       type_has_comment (type) ? ", comment" : "",
4447                       columns ? ", " : "",
4448                       columns ? columns : "",
4449                       current_credentials.uuid,
4450                       quoted_name ? "'" : "",
4451                       quoted_name ? quoted_name : uniquify,
4452                       quoted_name ? "'" : "",
4453                       type_has_comment (type) ? ", comment" : "",
4454                       columns ? ", " : "",
4455                       columns ? columns : "",
4456                       type,
4457                       quoted_uuid);
4458   else
4459     ret = sql_error ("INSERT INTO %ss"
4460                      " (uuid, owner, creation_time, modification_time%s%s)"
4461                      " SELECT make_uuid (),"
4462                      "        (SELECT id FROM users where users.uuid = '%s'),"
4463                      "        m_now (), m_now ()%s%s"
4464                      " FROM %ss WHERE uuid = '%s';",
4465                      type,
4466                      columns ? ", " : "",
4467                      columns ? columns : "",
4468                      current_credentials.uuid,
4469                      columns ? ", " : "",
4470                      columns ? columns : "",
4471                      type,
4472                      quoted_uuid);
4473 
4474   if (ret == 3)
4475     {
4476       g_free (quoted_uuid);
4477       g_free (quoted_name);
4478       g_free (uniquify);
4479       return 1;
4480     }
4481   else if (ret)
4482     {
4483       g_free (quoted_uuid);
4484       g_free (quoted_name);
4485       g_free (uniquify);
4486       return -1;
4487     }
4488 
4489   new = sql_last_insert_id ();
4490 
4491   /* Copy attached tags */
4492   sql ("INSERT INTO tag_resources"
4493        " (tag, resource_type, resource, resource_uuid, resource_location)"
4494        " SELECT tag, resource_type, %llu,"
4495        "        (SELECT uuid FROM %ss WHERE id = %llu),"
4496        "        resource_location"
4497        "   FROM tag_resources"
4498        "  WHERE resource_type = '%s' AND resource = %llu"
4499        "    AND resource_location = " G_STRINGIFY (LOCATION_TABLE) ";",
4500        new,
4501        type, new,
4502        type, resource);
4503 
4504   if (new_resource)
4505     *new_resource = new;
4506 
4507   if (old_resource)
4508     *old_resource = resource;
4509 
4510   g_free (quoted_uuid);
4511   g_free (quoted_name);
4512   g_free (uniquify);
4513   if (sql_last_insert_id () == 0)
4514     return -1;
4515   return 0;
4516 }
4517 
4518 /**
4519  * @brief Create a resource from an existing resource.
4520  *
4521  * @param[in]  type          Type of resource.
4522  * @param[in]  name          Name of new resource.  NULL to copy from existing.
4523  * @param[in]  comment       Comment on new resource.  NULL to copy from existing.
4524  * @param[in]  resource_id   UUID of existing resource.
4525  * @param[in]  columns       Extra columns in resource.
4526  * @param[in]  make_name_unique  When name NULL, whether to make existing name
4527  *                               unique.
4528  * @param[out] new_resource  New resource.
4529  * @param[out] old_resource  Address for existing resource, or NULL.
4530  *
4531  * @return 0 success, 1 resource exists already, 2 failed to find existing
4532  *         resource, 99 permission denied, -1 error.
4533  */
4534 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)4535 copy_resource (const char *type, const char *name, const char *comment,
4536                const char *resource_id, const char *columns,
4537                int make_name_unique, resource_t* new_resource,
4538                resource_t *old_resource)
4539 {
4540   int ret;
4541 
4542   assert (current_credentials.uuid);
4543 
4544   sql_begin_immediate ();
4545 
4546   ret = copy_resource_lock (type, name, comment, resource_id, columns,
4547                             make_name_unique, new_resource, old_resource);
4548 
4549   if (ret)
4550     sql_rollback ();
4551   else
4552     sql_commit ();
4553 
4554   return ret;
4555 }
4556 
4557 /**
4558  * @brief Get whether a resource exists.
4559  *
4560  * @param[in]  type      Type.
4561  * @param[in]  resource  Resource.
4562  * @param[in]  location  Location.
4563  *
4564  * @return 1 yes, 0 no, -1 error in type.
4565  */
4566 int
resource_exists(const char * type,resource_t resource,int location)4567 resource_exists (const char *type, resource_t resource, int location)
4568 {
4569   if (valid_db_resource_type (type) == 0)
4570     return -1;
4571 
4572   if (location == LOCATION_TABLE)
4573     return sql_int ("SELECT EXISTS (SELECT id FROM %ss WHERE id = %llu);",
4574                     type,
4575                     resource);
4576   return sql_int ("SELECT EXISTS (SELECT id FROM %ss%s WHERE id = %llu);",
4577                   type,
4578                   strcmp (type, "task") ? "_trash" : "",
4579                   resource);
4580 }
4581 
4582 /**
4583  * @brief Get the name of a resource.
4584  *
4585  * @param[in]  type      Type.
4586  * @param[in]  uuid      UUID.
4587  * @param[in]  location  Location.
4588  * @param[out] name      Return for freshly allocated name.
4589  *
4590  * @return 0 success, 1 error in type.
4591  */
4592 int
resource_name(const char * type,const char * uuid,int location,char ** name)4593 resource_name (const char *type, const char *uuid, int location, char **name)
4594 {
4595   if (valid_db_resource_type (type) == 0)
4596     return 1;
4597 
4598   if (strcasecmp (type, "note") == 0)
4599     *name = sql_string ("SELECT 'Note for: '"
4600                         " || (SELECT name"
4601                         "     FROM nvts"
4602                         "     WHERE nvts.uuid = notes%s.nvt)"
4603                         " FROM notes%s"
4604                         " WHERE uuid = '%s';",
4605                         location == LOCATION_TABLE ? "" : "_trash",
4606                         location == LOCATION_TABLE ? "" : "_trash",
4607                         uuid);
4608   else if (strcasecmp (type, "override") == 0)
4609     *name = sql_string ("SELECT 'Override for: '"
4610                         " || (SELECT name"
4611                         "     FROM nvts"
4612                         "     WHERE nvts.uuid = overrides%s.nvt)"
4613                         " FROM overrides%s"
4614                         " WHERE uuid = '%s';",
4615                         location == LOCATION_TABLE ? "" : "_trash",
4616                         location == LOCATION_TABLE ? "" : "_trash",
4617                         uuid);
4618   else if (strcasecmp (type, "report") == 0)
4619     *name = sql_string ("SELECT (SELECT name FROM tasks WHERE id = task)"
4620                         " || ' - '"
4621                         " || (SELECT"
4622                         "       CASE (SELECT end_time FROM tasks"
4623                         "             WHERE id = task)"
4624                         "       WHEN 0 THEN 'N/A'"
4625                         "       ELSE (SELECT iso_time (end_time)"
4626                         "             FROM tasks WHERE id = task)"
4627                         "    END)"
4628                         " FROM reports"
4629                         " WHERE uuid = '%s';",
4630                         uuid);
4631   else if (strcasecmp (type, "result") == 0)
4632     *name = sql_string ("SELECT (SELECT name FROM tasks WHERE id = task)"
4633                         " || ' - '"
4634                         " || (SELECT name FROM nvts WHERE oid = nvt)"
4635                         " || ' - '"
4636                         " || (SELECT"
4637                         "       CASE (SELECT end_time FROM tasks"
4638                         "             WHERE id = task)"
4639                         "       WHEN 0 THEN 'N/A'"
4640                         "       ELSE (SELECT iso_time (end_time)"
4641                         "             FROM tasks WHERE id = task)"
4642                         "    END)"
4643                         " FROM results"
4644                         " WHERE uuid = '%s';",
4645                         uuid);
4646   else if (location == LOCATION_TABLE)
4647     *name = sql_string ("SELECT name"
4648                         " FROM %ss"
4649                         " WHERE uuid = '%s';",
4650                         type,
4651                         uuid);
4652   else if (type_has_trash (type))
4653     *name = sql_string ("SELECT name"
4654                         " FROM %ss%s"
4655                         " WHERE uuid = '%s';",
4656                         type,
4657                         strcmp (type, "task") ? "_trash" : "",
4658                         uuid);
4659   else
4660     *name = NULL;
4661 
4662   return 0;
4663 }
4664 
4665 /**
4666  * @brief Get the name of a resource.
4667  *
4668  * @param[in]  type      Type.
4669  * @param[in]  uuid      UUID.
4670  * @param[out] name      Return for freshly allocated name.
4671  *
4672  * @return 0 success, 1 error in type.
4673  */
4674 int
manage_resource_name(const char * type,const char * uuid,char ** name)4675 manage_resource_name (const char *type, const char *uuid, char **name)
4676 {
4677   return resource_name (type, uuid, LOCATION_TABLE, name);
4678 }
4679 
4680 /**
4681  * @brief Get the name of a trashcan resource.
4682  *
4683  * @param[in]  type      Type.
4684  * @param[in]  uuid      UUID.
4685  * @param[out] name      Return for freshly allocated name.
4686  *
4687  * @return 0 success, 1 error in type.
4688  */
4689 int
manage_trash_resource_name(const char * type,const char * uuid,char ** name)4690 manage_trash_resource_name (const char *type, const char *uuid, char **name)
4691 {
4692   return resource_name (type, uuid, LOCATION_TRASH, name);
4693 }
4694 
4695 /**
4696  * @brief Get the UUID of a resource.
4697  *
4698  * @param[in]  type      Type.
4699  * @param[in]  resource  Resource.
4700  *
4701  * @return Freshly allocated UUID on success, else NULL.
4702  */
4703 gchar *
resource_uuid(const gchar * type,resource_t resource)4704 resource_uuid (const gchar *type, resource_t resource)
4705 {
4706   assert (valid_db_resource_type (type));
4707 
4708   return sql_string ("SELECT uuid FROM %ss WHERE id = %llu;",
4709                      type,
4710                      resource);
4711 }
4712 
4713 /**
4714  * @brief Initialise a GET iterator, including observed resources.
4715  *
4716  * This version includes the extra_with arg.
4717  *
4718  * @param[in]  iterator        Iterator.
4719  * @param[in]  type            Type of resource.
4720  * @param[in]  get             GET data.
4721  * @param[in]  select_columns         Columns for SQL.
4722  * @param[in]  trash_select_columns   Columns for SQL trash case.
4723  * @param[in]  where_columns          WHERE columns.  These are columns that
4724  *                                    can be used for filtering and searching,
4725  *                                    but are not accessed (so column has no
4726  *                                    iterator access function).
4727  * @param[in]  trash_where_columns    WHERE columns for trashcan.
4728  * @param[in]  filter_columns  Columns for filter.
4729  * @param[in]  distinct        Whether the query should be distinct.  Skipped
4730  *                             for trash and single resource.
4731  * @param[in]  extra_tables    Extra tables to join in FROM clause.
4732  * @param[in]  extra_where     Extra WHERE clauses.  Skipped for single
4733  *                             resource.
4734  * @param[in]  extra_where_single  Extra WHERE clauses.  Used for single
4735  *                                 resource.
4736  * @param[in]  owned           Only get items owned by the current user.
4737  * @param[in]  ignore_id       Whether to ignore id (e.g. for report results).
4738  * @param[in]  extra_order     Extra ORDER clauses.
4739  * @param[in]  extra_with      Extra WITH clauses.
4740  * @param[in]  acl_with_optional  Whether default permission WITH clauses are
4741  *                                 optional.
4742  * @param[in]  assume_permitted   Whether to skip permission checks.
4743  *
4744  * @return 0 success, 1 failed to find resource, 2 failed to find filter, -1
4745  *         error.
4746  */
4747 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)4748 init_get_iterator2_with (iterator_t* iterator, const char *type,
4749                          const get_data_t *get, column_t *select_columns,
4750                          column_t *trash_select_columns,
4751                          column_t *where_columns,
4752                          column_t *trash_where_columns,
4753                          const char **filter_columns, int distinct,
4754                          const char *extra_tables,
4755                          const char *extra_where,
4756                          const char *extra_where_single, int owned,
4757                          int ignore_id,
4758                          const char *extra_order,
4759                          const char *extra_with,
4760                          int acl_with_optional,
4761                          int assume_permitted)
4762 {
4763   int first, max;
4764   gchar *clause, *order, *filter, *owned_clause, *with_clause;
4765   array_t *permissions;
4766   resource_t resource = 0;
4767   gchar *owner_filter;
4768   gchar *columns;
4769 
4770   assert (get);
4771 
4772   if (select_columns == NULL)
4773     {
4774       assert (0);
4775       return -1;
4776     }
4777 
4778   if (ignore_id)
4779     {
4780       resource = 0;
4781     }
4782   else if (get->id && (owned == 0 || (current_credentials.uuid == NULL)))
4783     {
4784       gchar *quoted_uuid = sql_quote (get->id);
4785       switch (sql_int64 (&resource,
4786                          "SELECT id FROM %ss WHERE uuid = '%s';",
4787                          type, quoted_uuid))
4788         {
4789           case 0:
4790             break;
4791           case 1:        /* Too few rows in result of query. */
4792             g_free (quoted_uuid);
4793             return 1;
4794             break;
4795           default:       /* Programming error. */
4796             assert (0);
4797           case -1:
4798             g_free (quoted_uuid);
4799             return -1;
4800             break;
4801         }
4802       g_free (quoted_uuid);
4803     }
4804   else if (get->id && owned)
4805     {
4806       /* For now assume that the permission is "get_<type>".  Callers wishing
4807        * to iterate over a single resource with other permissions can use
4808        * uuid= in the filter (instead of passing get->id). */
4809       const char* permission;
4810       /* Special case: "get_assets" subtypes */
4811       if (type_is_asset_subtype (type))
4812         permission = "get_assets";
4813       else
4814         permission = NULL;
4815 
4816       if (find_resource_with_permission (type, get->id, &resource, permission,
4817                                          get->trash))
4818         return -1;
4819       if (resource == 0)
4820         return 1;
4821     }
4822 
4823   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
4824     {
4825       if (get->filter_replacement)
4826         /* Replace the filter term with one given by the caller.  This is
4827          * used by GET_REPORTS to use the default filter with any task (when
4828          * given the special value of -3 in filt_id). */
4829         filter = g_strdup (get->filter_replacement);
4830       else
4831         filter = filter_term (get->filt_id);
4832       if (filter == NULL)
4833         return 2;
4834     }
4835   else
4836     filter = NULL;
4837 
4838   clause = filter_clause (type, filter ? filter : get->filter, filter_columns,
4839                           (get->trash && trash_select_columns)
4840                            ? trash_select_columns
4841                            : select_columns,
4842                           (get->trash && trash_where_columns)
4843                            ? trash_where_columns
4844                            : where_columns,
4845                           get->trash, &order, &first, &max, &permissions,
4846                           &owner_filter);
4847 
4848   g_free (filter);
4849 
4850   with_clause = NULL;
4851 
4852   if (resource || assume_permitted)
4853     /* Ownership test of single resources is done above by find function
4854      * but acl_where_owned has to be called to generate WITH clause
4855      * in case subqueries depend on it.
4856      */
4857     owned_clause = acl_where_owned (type, get, 0, owner_filter, resource,
4858                                     permissions, acl_with_optional,
4859                                     &with_clause);
4860   else
4861     owned_clause = acl_where_owned (type, get, owned, owner_filter, resource,
4862                                     permissions, acl_with_optional,
4863                                     &with_clause);
4864 
4865   if (extra_with)
4866     {
4867       if (with_clause)
4868         {
4869           gchar *old_with;
4870 
4871           old_with = with_clause;
4872           with_clause = g_strdup_printf ("%s, %s", old_with, extra_with);
4873           g_free (old_with);
4874         }
4875       else
4876         with_clause = g_strdup_printf ("WITH %s", extra_with);
4877     }
4878 
4879   g_free (owner_filter);
4880   array_free (permissions);
4881 
4882   if (get->trash && trash_select_columns)
4883     columns = columns_build_select (trash_select_columns);
4884   else
4885     columns = columns_build_select (select_columns);
4886 
4887   if (get->ignore_pagination
4888       && ((strcmp (type, "host") == 0)
4889           || (strcmp (type, "os") == 0)
4890           || (strcmp (type, "task") == 0)
4891           || (strcmp (type, "report") == 0)
4892           || (strcmp (type, "result") == 0)))
4893     {
4894       first = 0;
4895       max = -1;
4896     }
4897 
4898   if (strlen (order) == 0)
4899     {
4900       g_free (order);
4901       order = NULL;
4902     }
4903 
4904   if (resource && get->trash)
4905     init_iterator (iterator,
4906                    "%sSELECT %s"
4907                    " FROM %ss%s %s"
4908                    " WHERE %ss.id = %llu"
4909                    " AND %s%s"
4910                    "%s%s;",
4911                    with_clause ? with_clause : "",
4912                    columns,
4913                    type,
4914                    type_trash_in_table (type) ? "" : "_trash",
4915                    extra_tables ? extra_tables : "",
4916                    type,
4917                    resource,
4918                    owned_clause,
4919                    extra_where_single ? extra_where_single : "",
4920                    order ? order : "",
4921                    order ? (extra_order ? extra_order : "") : "");
4922   else if (get->trash)
4923     init_iterator (iterator,
4924                    "%sSELECT %s"
4925                    " FROM %ss%s %s"
4926                    " WHERE"
4927                    "%s"
4928                    "%s"
4929                    "%s%s;",
4930                    with_clause ? with_clause : "",
4931                    columns,
4932                    type,
4933                    type_trash_in_table (type) ? "" : "_trash",
4934                    extra_tables ? extra_tables : "",
4935                    owned_clause,
4936                    extra_where ? extra_where : "",
4937                    order ? order : "",
4938                    order ? (extra_order ? extra_order : "") : "");
4939   else if (resource)
4940     init_iterator (iterator,
4941                    "%sSELECT %s"
4942                    " FROM %ss %s"
4943                    " WHERE %ss.id = %llu"
4944                    " AND %s%s"
4945                    "%s%s;",
4946                    with_clause ? with_clause : "",
4947                    columns,
4948                    type,
4949                    extra_tables ? extra_tables : "",
4950                    type,
4951                    resource,
4952                    owned_clause,
4953                    extra_where_single ? extra_where_single : "",
4954                    order ? order : "",
4955                    order ? (extra_order ? extra_order : "") : "");
4956   else
4957     init_iterator (iterator,
4958                    "%s%sSELECT %s"
4959                    " FROM %ss %s"
4960                    " WHERE"
4961                    " %s%s%s%s%s%s%s"
4962                    " LIMIT %s OFFSET %i%s;",
4963                    with_clause ? with_clause : "",
4964                    distinct ? "SELECT DISTINCT * FROM (" : "",
4965                    columns,
4966                    type,
4967                    extra_tables ? extra_tables : "",
4968                    owned_clause,
4969                    clause ? " AND (" : "",
4970                    clause ? clause : "",
4971                    clause ? ")" : "",
4972                    extra_where ? extra_where : "",
4973                    order ? order : "",
4974                    order ? (extra_order ? extra_order : "") : "",
4975                    sql_select_limit (max),
4976                    first,
4977                    distinct ? ") AS subquery_for_distinct" : "");
4978 
4979   g_free (columns);
4980   g_free (with_clause);
4981   g_free (owned_clause);
4982   g_free (order);
4983   g_free (clause);
4984   return 0;
4985 }
4986 
4987 /**
4988  * @brief Initialise a GET iterator, including observed resources.
4989  *
4990  * @param[in]  iterator        Iterator.
4991  * @param[in]  type            Type of resource.
4992  * @param[in]  get             GET data.
4993  * @param[in]  select_columns         Columns for SQL.
4994  * @param[in]  trash_select_columns   Columns for SQL trash case.
4995  * @param[in]  where_columns          WHERE columns.  These are columns that
4996  *                                    can be used for filtering and searching,
4997  *                                    but are not accessed (so column has no
4998  *                                    iterator access function).
4999  * @param[in]  trash_where_columns    WHERE columns for trashcan.
5000  * @param[in]  filter_columns  Columns for filter.
5001  * @param[in]  distinct        Whether the query should be distinct.  Skipped
5002  *                             for trash and single resource.
5003  * @param[in]  extra_tables    Extra tables to join in FROM clause.
5004  * @param[in]  extra_where     Extra WHERE clauses.  Skipped for single
5005  *                             resource.
5006  * @param[in]  extra_where_single  Extra WHERE clauses.  Used for single
5007  *                                 resource.
5008  * @param[in]  owned           Only get items owned by the current user.
5009  * @param[in]  ignore_id       Whether to ignore id (e.g. for report results).
5010  * @param[in]  extra_order     Extra ORDER clauses.
5011  *
5012  * @return 0 success, 1 failed to find resource, 2 failed to find filter, -1
5013  *         error.
5014  */
5015 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)5016 init_get_iterator2 (iterator_t* iterator, const char *type,
5017                     const get_data_t *get, column_t *select_columns,
5018                     column_t *trash_select_columns,
5019                     column_t *where_columns,
5020                     column_t *trash_where_columns,
5021                     const char **filter_columns, int distinct,
5022                     const char *extra_tables,
5023                     const char *extra_where, const char *extra_where_single,
5024                     int owned, int ignore_id,
5025                     const char *extra_order)
5026 {
5027   return init_get_iterator2_with (iterator, type, get, select_columns,
5028                                  trash_select_columns, where_columns,
5029                                  trash_where_columns, filter_columns, distinct,
5030                                  extra_tables, extra_where, extra_where_single,
5031                                  owned, ignore_id, extra_order, NULL, 0, 0);
5032 }
5033 
5034 /**
5035  * @brief Initialise a GET iterator, including observed resources.
5036  *
5037  * @param[in]  iterator        Iterator.
5038  * @param[in]  type            Type of resource.
5039  * @param[in]  get             GET data.
5040  * @param[in]  select_columns         Columns for SQL.
5041  * @param[in]  trash_select_columns   Columns for SQL trash case.
5042  * @param[in]  filter_columns  Columns for filter.
5043  * @param[in]  distinct        Whether the query should be distinct.  Skipped
5044  *                             for trash and single resource.
5045  * @param[in]  extra_tables    Extra tables to join in FROM clause.
5046  * @param[in]  extra_where     Extra WHERE clauses.  Skipped for single
5047  *                             resource.
5048  * @param[in]  owned           Only get items owned by the current user.
5049  *
5050  * @return 0 success, 1 failed to find resource, 2 failed to find filter, -1
5051  *         error.
5052  */
5053 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)5054 init_get_iterator (iterator_t* iterator, const char *type,
5055                    const get_data_t *get, column_t *select_columns,
5056                    column_t *trash_select_columns,
5057                    const char **filter_columns, int distinct,
5058                    const char *extra_tables,
5059                    const char *extra_where, int owned)
5060 {
5061   return init_get_iterator2 (iterator, type, get, select_columns,
5062                              trash_select_columns, NULL, NULL, filter_columns,
5063                              distinct, extra_tables, extra_where, NULL, owned,
5064                              FALSE, NULL);
5065 }
5066 
5067 /**
5068  * @brief Append expression for a column to an array.
5069  *
5070  * @param[in]  columns         Array.
5071  * @param[in]  column_name     Name of column.
5072  * @param[in]  select_columns  Definition of "SELECT" columns.
5073  * @param[in]  where_columns   Definition of "WHERE" columns.
5074  */
5075 static void
append_column(GArray * columns,const gchar * column_name,column_t * select_columns,column_t * where_columns)5076 append_column (GArray *columns, const gchar *column_name,
5077                column_t *select_columns, column_t *where_columns)
5078 {
5079   int i = 0;
5080   while (select_columns[i].select != NULL)
5081     {
5082       gchar *select = NULL;
5083       if (strcmp (select_columns[i].select, column_name) == 0
5084           || (select_columns[i].filter
5085               && strcmp (select_columns[i].filter, column_name) == 0))
5086         {
5087           select = g_strdup (select_columns[i].select);
5088           g_array_append_val (columns, select);
5089           break;
5090         }
5091       i++;
5092     }
5093   if (select_columns[i].select == NULL && where_columns)
5094     {
5095       i = 0;
5096       while (where_columns[i].select != NULL)
5097         {
5098           gchar *select = NULL;
5099           if (strcmp (where_columns[i].select, column_name) == 0
5100               || (where_columns[i].filter
5101                   && strcmp (where_columns[i].filter, column_name) == 0))
5102             {
5103               select = g_strdup (where_columns[i].select);
5104               g_array_append_val (columns, select);
5105               break;
5106             }
5107           i++;
5108         }
5109     }
5110 }
5111 
5112 /**
5113  * @brief Initialise a GET_AGGREGATES iterator, including observed resources.
5114  *
5115  * @param[in]  iterator        Iterator.
5116  * @param[in]  type            Type of resource.
5117  * @param[in]  get             GET data.
5118  * @param[in]  distinct        Whether the query should be distinct.  Skipped
5119  *                             for trash and single resource.
5120  * @param[in]  data_columns    Columns to calculate statistics for.
5121  * @param[in]  group_column    Column to group data by.
5122  * @param[in]  subgroup_column Column to further group data by.
5123  * @param[in]  text_columns    Columns to get text from.
5124  * @param[in]  sort_data       GArray of sorting data.
5125  * @param[in]  first_group     Row number to start iterating from.
5126  * @param[in]  max_groups      Maximum number of rows.
5127  * @param[in]  extra_tables    Join tables.  Skipped for trash and single
5128  *                             resource.
5129  * @param[in]  given_extra_where  Extra WHERE clauses.  Skipped for single
5130  *                                resource.
5131  *
5132  * @return 0 success, 1 failed to find resource, 2 failed to find filter,
5133  *         3 invalid stat_column, 4 invalid group_column, 5 invalid type,
5134  *         6 trashcan not used by type, 7 invalid text column, 8 invalid
5135  *         subgroup_column, -1 error.
5136  */
5137 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)5138 init_aggregate_iterator (iterator_t* iterator, const char *type,
5139                          const get_data_t *get, int distinct,
5140                          GArray *data_columns,
5141                          const char *group_column, const char *subgroup_column,
5142                          GArray *text_columns, GArray *sort_data,
5143                          int first_group, int max_groups,
5144                          const char *extra_tables,
5145                          const char *given_extra_where)
5146 {
5147   column_t *select_columns, *where_columns;
5148   const char **filter_columns;
5149   GString *aggregate_select, *outer_col_select;
5150   gchar *aggregate_group_by;
5151   gchar *outer_group_by_column, *outer_subgroup_column;
5152   gchar *select_group_column, *select_subgroup_column;
5153   GArray *select_data_columns, *select_text_columns;
5154   GString *order_clause;
5155   gchar *inner_select;
5156   int build_select_ret;
5157 
5158   assert (get);
5159 
5160   if (get->id)
5161     g_warning ("%s: Called with an id parameter", __func__);
5162 
5163   if ((manage_scap_loaded () == FALSE
5164        && (strcmp (type, "cve") == 0
5165            || strcmp (type, "cpe") == 0
5166            || strcmp (type, "ovaldef") == 0))
5167       || (manage_cert_loaded () == FALSE
5168           && (strcmp (type, "cert_bund_adv") == 0
5169               || strcmp (type, "dfn_cert_adv") == 0)))
5170     {
5171       // Init a dummy iterator if SCAP or CERT DB is required but unavailable.
5172       init_iterator (iterator,
5173                      "SELECT NULL LIMIT %s",
5174                      sql_select_limit (0));
5175       return 0;
5176     }
5177 
5178   select_columns = type_select_columns (type);
5179   where_columns = type_where_columns (type);
5180   filter_columns = type_filter_columns (type);
5181 
5182   if (filter_columns == NULL)
5183     return 5;
5184   if (get->trash && type_has_trash (type) == 0)
5185     return 6;
5186 
5187   if (data_columns && data_columns->len > 0)
5188     {
5189       int i;
5190       for (i = 0; i < data_columns->len; i++)
5191         {
5192           if (vector_find_filter (filter_columns,
5193                                   g_array_index (data_columns, gchar*, i))
5194               == 0)
5195             {
5196               return 3;
5197             }
5198         }
5199     }
5200   if (text_columns && text_columns->len > 0)
5201     {
5202       int i;
5203       for (i = 0; i < text_columns->len; i++)
5204         {
5205           if (vector_find_filter (filter_columns,
5206                                   g_array_index (text_columns, gchar*, i))
5207               == 0)
5208             {
5209               return 7;
5210             }
5211         }
5212     }
5213   if (group_column && vector_find_filter (filter_columns, group_column) == 0)
5214     {
5215       return 4;
5216     }
5217   if (subgroup_column
5218       && vector_find_filter (filter_columns, subgroup_column) == 0)
5219     {
5220       return 8;
5221     }
5222   select_data_columns = g_array_new (TRUE, TRUE, sizeof (gchar*));
5223   select_text_columns = g_array_new (TRUE, TRUE, sizeof (gchar*));
5224 
5225   select_group_column = NULL;
5226   select_subgroup_column = NULL;
5227 
5228   if (group_column)
5229     {
5230       select_group_column
5231         = g_strdup (columns_select_column (select_columns,
5232                                            where_columns,
5233                                            group_column));
5234       if (subgroup_column)
5235         {
5236           select_subgroup_column
5237             = g_strdup (columns_select_column (select_columns,
5238                                                where_columns,
5239                                                subgroup_column));
5240         }
5241     }
5242 
5243   if (data_columns && data_columns->len > 0)
5244     {
5245       int column_index;
5246       for (column_index = 0; column_index < data_columns->len; column_index++)
5247         append_column (select_data_columns,
5248                        g_array_index (data_columns, gchar*, column_index),
5249                        select_columns,
5250                        where_columns);
5251     }
5252 
5253   if (text_columns && text_columns->len > 0)
5254     {
5255       int column_index;
5256       for (column_index = 0; column_index < text_columns->len; column_index++)
5257         append_column (select_text_columns,
5258                        g_array_index (text_columns, gchar*, column_index),
5259                        select_columns,
5260                        where_columns);
5261     }
5262 
5263   /* Round time fields to the next day to reduce amount of rows returned
5264    * This returns "pseudo-UTC" dates which are used by the GSA charts because
5265    *  the JavaScript Date objects do not support setting the timezone.
5266    */
5267   if (column_is_timestamp (group_column))
5268     outer_group_by_column
5269       = g_strdup_printf ("EXTRACT (EPOCH FROM"
5270                          "           date_trunc ('day',"
5271                          "           TIMESTAMP WITH TIME ZONE 'epoch'"
5272                          "           + (%s) * INTERVAL '1 second'))"
5273                          "  :: integer",
5274                          "aggregate_group_value");
5275   else
5276     outer_group_by_column = g_strdup ("aggregate_group_value");
5277 
5278   if (column_is_timestamp (subgroup_column))
5279     outer_subgroup_column
5280       = g_strdup_printf ("EXTRACT (EPOCH FROM"
5281                          "           date_trunc ('day',"
5282                          "           TIMESTAMP WITH TIME ZONE 'epoch'"
5283                          "           + (%s) * INTERVAL '1 second'))"
5284                          "  :: integer",
5285                          "aggregate_subgroup_value");
5286   else
5287     outer_subgroup_column = g_strdup ("aggregate_subgroup_value");
5288 
5289   if (sort_data)
5290     {
5291       order_clause = g_string_new ("ORDER BY");
5292 
5293       int sort_index;
5294       for (sort_index = 0; sort_index < sort_data->len; sort_index++) {
5295         sort_data_t *sort_data_item;
5296         const gchar *sort_field, *sort_stat;
5297         int sort_order;
5298         gchar *order_column;
5299 
5300         sort_data_item = g_array_index (sort_data, sort_data_t*, sort_index);
5301         sort_field = sort_data_item->field;
5302         sort_stat = sort_data_item->stat;
5303         sort_order = sort_data_item->order;
5304 
5305         if (sort_stat && strcmp (sort_stat, "count") == 0)
5306           order_column = g_strdup ("outer_count");
5307         else if (sort_stat && strcmp (sort_stat, "value") == 0)
5308           order_column = g_strdup ("outer_group_column");
5309         else if (sort_field
5310                  && group_column
5311                  && strcmp (sort_field, "")
5312                  && strcmp (sort_field, group_column)
5313                  && (subgroup_column == NULL
5314                      || strcmp (sort_field, subgroup_column)))
5315           {
5316             int index;
5317             order_column = NULL;
5318             for (index = 0;
5319                  data_columns && index < data_columns->len && order_column == NULL;
5320                  index++)
5321               {
5322                 gchar *column = g_array_index (data_columns, gchar*, index);
5323                 if (strcmp (column, sort_field) == 0)
5324                   {
5325                     if (sort_stat == NULL || strcmp (sort_stat, "") == 0
5326                         || (   strcmp (sort_stat, "min")
5327                             && strcmp (sort_stat, "max")
5328                             && strcmp (sort_stat, "mean")
5329                             && strcmp (sort_stat, "sum")))
5330                       order_column = g_strdup_printf ("max (aggregate_max_%d)",
5331                                                       index);
5332                     else if (strcmp (sort_stat, "mean") == 0)
5333                       order_column = g_strdup_printf ("sum (aggregate_sum_%d)"
5334                                                       " / sum(aggregate_count)",
5335                                                       index);
5336                     else
5337                       order_column = g_strdup_printf ("%s (aggregate_%s_%d)",
5338                                                       sort_stat, sort_stat,
5339                                                       index);
5340                   }
5341               }
5342 
5343             for (index = 0;
5344                 text_columns && index < text_columns->len && order_column == NULL;
5345                 index++)
5346               {
5347                 gchar *column = g_array_index (text_columns, gchar*, index);
5348                 if (strcmp (column, sort_field) == 0)
5349                   {
5350                     order_column = g_strdup_printf ("max (text_column_%d)",
5351                                                     index);
5352                   }
5353               }
5354           }
5355         else if (sort_field && subgroup_column
5356                 && strcmp (sort_field, subgroup_column) == 0)
5357           order_column = g_strdup ("outer_subgroup_column");
5358         else
5359           order_column = g_strdup ("outer_group_column");
5360 
5361         if (subgroup_column && sort_index == 0)
5362           {
5363             xml_string_append (order_clause,
5364                                " outer_group_column %s, %s %s",
5365                                sort_order ? "ASC" : "DESC",
5366                                order_column,
5367                                sort_order ? "ASC" : "DESC");
5368           }
5369         else
5370           {
5371             xml_string_append (order_clause,
5372                                "%s %s %s",
5373                                sort_index > 0 ? "," : "",
5374                                order_column,
5375                                sort_order ? "ASC" : "DESC");
5376           }
5377         g_free (order_column);
5378       }
5379 
5380       if (sort_data->len == 0)
5381         g_string_append (order_clause, " outer_group_column ASC");
5382     }
5383   else
5384     order_clause = g_string_new ("");
5385 
5386   aggregate_select = g_string_new ("");
5387   outer_col_select = g_string_new ("");
5388   if (group_column && strcmp (group_column, ""))
5389     {
5390       if (subgroup_column && strcmp (subgroup_column, ""))
5391         {
5392           g_string_append_printf (aggregate_select,
5393                              " count(*) AS aggregate_count,"
5394                              " %s AS aggregate_group_value,"
5395                              " %s AS aggregate_subgroup_value",
5396                              select_group_column,
5397                              select_subgroup_column);
5398 
5399           aggregate_group_by = g_strdup_printf ("%s, %s",
5400                                                 select_group_column,
5401                                                 select_subgroup_column);
5402         }
5403       else
5404         {
5405           g_string_append_printf (aggregate_select,
5406                              " count(*) AS aggregate_count,"
5407                              " %s AS aggregate_group_value,"
5408                              " CAST (NULL AS TEXT) AS aggregate_subgroup_value",
5409                              select_group_column);
5410 
5411           aggregate_group_by = g_strdup (select_group_column);
5412         }
5413 
5414 
5415     }
5416   else
5417     {
5418       g_string_append_printf (aggregate_select,
5419                          " count(*) AS aggregate_count,"
5420                          " CAST (NULL AS TEXT) AS aggregate_group_value,"
5421                          " CAST (NULL AS TEXT) AS aggregate_subgroup_value");
5422 
5423       aggregate_group_by = NULL;
5424     }
5425 
5426   int col_index;
5427   for (col_index = 0; col_index < select_data_columns->len; col_index ++)
5428     {
5429       gchar *select_data_column = g_array_index (select_data_columns, gchar*,
5430                                                  col_index);
5431       // TODO: Test type of column (string, number, timestamp)
5432       g_string_append_printf (aggregate_select,
5433                               ","
5434                               " min(CAST (%s AS real)) AS aggregate_min_%d,"
5435                               " max(CAST (%s AS real)) AS aggregate_max_%d,"
5436                               " avg(CAST (%s AS real)) * count(*)"
5437                               "   AS aggregate_avg_%d,"
5438                               " sum(CAST (%s AS real))"
5439                               "   AS aggregate_sum_%d",
5440                               select_data_column,
5441                               col_index,
5442                               select_data_column,
5443                               col_index,
5444                               select_data_column,
5445                               col_index,
5446                               select_data_column,
5447                               col_index);
5448       g_string_append_printf (outer_col_select,
5449                               ", min(aggregate_min_%d),"
5450                               " max (aggregate_max_%d),"
5451                               " sum (aggregate_avg_%d) / sum(aggregate_count),"
5452                               " sum (aggregate_sum_%d)",
5453                               col_index, col_index, col_index, col_index);
5454     }
5455   for (col_index = 0; col_index < select_text_columns->len; col_index ++)
5456     {
5457       gchar *select_text_column = g_array_index (select_text_columns, gchar*,
5458                                                  col_index);
5459       g_string_append_printf (aggregate_select,
5460                               ", max (%s) as text_column_%d",
5461                               select_text_column,
5462                               col_index);
5463       g_string_append_printf (outer_col_select,
5464                               ", max (text_column_%d)",
5465                               col_index);
5466     }
5467 
5468   inner_select = NULL;
5469   build_select_ret = type_build_select (type, aggregate_select->str, get,
5470                                         distinct, 0, extra_tables,
5471                                         given_extra_where, aggregate_group_by,
5472                                         &inner_select);
5473 
5474   if (build_select_ret == 0)
5475     {
5476       init_iterator (iterator,
5477                     "SELECT sum(aggregate_count) AS outer_count,"
5478                     " %s AS outer_group_column,"
5479                     " %s AS outer_subgroup_column"
5480                     " %s"
5481                     " FROM (%s)"
5482                     "      AS agg_sub"
5483                     " GROUP BY outer_group_column, outer_subgroup_column"
5484                     " %s"
5485                     " LIMIT %s OFFSET %d;",
5486                     outer_group_by_column,
5487                     outer_subgroup_column,
5488                     outer_col_select->str,
5489                     inner_select,
5490                     order_clause->str,
5491                     sql_select_limit (max_groups),
5492                     first_group);
5493     }
5494 
5495   g_string_free (order_clause, TRUE);
5496   g_free (aggregate_group_by);
5497   g_string_free (aggregate_select, TRUE);
5498   g_string_free (outer_col_select, TRUE);
5499   g_free (outer_group_by_column);
5500   g_free (outer_subgroup_column);
5501   g_free (select_group_column);
5502   g_free (select_subgroup_column);
5503   g_free (inner_select);
5504 
5505   switch (build_select_ret)
5506     {
5507       case 0:
5508         break;
5509       case 1:
5510         return 2;
5511       default:
5512         return -1;
5513     }
5514 
5515   return 0;
5516 }
5517 
5518 /**
5519  * @brief Offset for aggregate iterator.
5520  */
5521 #define AGGREGATE_ITERATOR_OFFSET 3
5522 
5523 /**
5524  * @brief Number of stats, for aggregate iterator.
5525  */
5526 #define AGGREGATE_ITERATOR_N_STATS 4
5527 
5528 /**
5529  * @brief Get the count from an aggregate iterator.
5530  *
5531  * @param[in]  iterator  Iterator.
5532  *
5533  * @return The count of resources in the current group.
5534  */
5535 int
aggregate_iterator_count(iterator_t * iterator)5536 aggregate_iterator_count (iterator_t* iterator)
5537 {
5538   return iterator_int (iterator, 0);
5539 }
5540 
5541 /**
5542  * @brief Get the minimum from an aggregate iterator.
5543  *
5544  * @param[in]  iterator           Iterator.
5545  * @param[in]  data_column_index  Index of the data column to get min of.
5546  *
5547  * @return The minimum value in the current group.
5548  */
5549 double
aggregate_iterator_min(iterator_t * iterator,int data_column_index)5550 aggregate_iterator_min (iterator_t* iterator, int data_column_index)
5551 {
5552   return iterator_double (iterator,
5553                           AGGREGATE_ITERATOR_OFFSET
5554                           + data_column_index * AGGREGATE_ITERATOR_N_STATS);
5555 }
5556 
5557 /**
5558  * @brief Get the maximum from an aggregate iterator.
5559  *
5560  * @param[in]  iterator           Iterator.
5561  * @param[in]  data_column_index  Index of the data column to get max of.
5562  *
5563  * @return The maximum value in the current group.
5564  */
5565 double
aggregate_iterator_max(iterator_t * iterator,int data_column_index)5566 aggregate_iterator_max (iterator_t* iterator, int data_column_index)
5567 {
5568   return iterator_double (iterator,
5569                           AGGREGATE_ITERATOR_OFFSET + 1
5570                           + data_column_index * AGGREGATE_ITERATOR_N_STATS);
5571 }
5572 
5573 /**
5574  * @brief Get the mean from an aggregate iterator.
5575  *
5576  * @param[in]  iterator           Iterator.
5577  * @param[in]  data_column_index  Index of the data column to get mean of.
5578  *
5579  * @return The mean value in the current group.
5580  */
5581 double
aggregate_iterator_mean(iterator_t * iterator,int data_column_index)5582 aggregate_iterator_mean (iterator_t* iterator, int data_column_index)
5583 {
5584   return iterator_double (iterator,
5585                           AGGREGATE_ITERATOR_OFFSET + 2
5586                           + data_column_index * AGGREGATE_ITERATOR_N_STATS);
5587 }
5588 
5589 /**
5590  * @brief Get the sum from a statistics iterator.
5591  *
5592  * @param[in]  iterator           Iterator.
5593  * @param[in]  data_column_index  Index of the data column to get sum of.
5594  *
5595  * @return The sum of values in the current group.
5596  */
5597 double
aggregate_iterator_sum(iterator_t * iterator,int data_column_index)5598 aggregate_iterator_sum (iterator_t* iterator, int data_column_index)
5599 {
5600   return iterator_double (iterator,
5601                           AGGREGATE_ITERATOR_OFFSET + 3
5602                           + data_column_index * AGGREGATE_ITERATOR_N_STATS);
5603 }
5604 
5605 /**
5606  * @brief Get the value of a text column from an aggregate iterator.
5607  *
5608  * @param[in]  iterator           Iterator.
5609  * @param[in]  text_column_index  Index of the text column to get.
5610  * @param[in]  data_columns       Number of data columns.
5611  *
5612  * @return The value, or NULL if iteration is complete.  Freed by
5613  *         cleanup_iterator.
5614  */
5615 const char*
aggregate_iterator_text(iterator_t * iterator,int text_column_index,int data_columns)5616 aggregate_iterator_text (iterator_t* iterator, int text_column_index,
5617                          int data_columns)
5618 {
5619   const char *ret;
5620   if (iterator->done) return NULL;
5621   ret = (const char*) iterator_string (iterator,
5622                                        AGGREGATE_ITERATOR_OFFSET
5623                                         + (data_columns
5624                                            * AGGREGATE_ITERATOR_N_STATS)
5625                                         + text_column_index);
5626   return ret;
5627 }
5628 
5629 /**
5630  * @brief Get the value of the group column from a statistics iterator.
5631  *
5632  * @param[in]  iterator  Iterator.
5633  *
5634  * @return The value, or NULL if iteration is complete.  Freed by
5635  *         cleanup_iterator.
5636  */
5637 const char*
aggregate_iterator_value(iterator_t * iterator)5638 aggregate_iterator_value (iterator_t* iterator)
5639 {
5640   const char *ret;
5641   if (iterator->done) return NULL;
5642   ret = (const char*) iterator_string (iterator, 1);
5643   return ret;
5644 }
5645 
5646 /**
5647  * @brief Get the value of the subgroup column from an aggregate iterator.
5648  *
5649  * @param[in]  iterator  Iterator.
5650  *
5651  * @return The value, or NULL if iteration is complete.  Freed by
5652  *         cleanup_iterator.
5653  */
5654 const char*
aggregate_iterator_subgroup_value(iterator_t * iterator)5655 aggregate_iterator_subgroup_value (iterator_t* iterator)
5656 {
5657   const char *ret;
5658   if (iterator->done) return NULL;
5659   ret = (const char*) iterator_string (iterator, 2);
5660   return ret;
5661 }
5662 
5663 /**
5664  * @brief Count number of a particular resource.
5665  *
5666  * @param[in]  type              Type of resource.
5667  * @param[in]  get               GET params.
5668  * @param[in]  select_columns    SELECT columns.
5669  * @param[in]  trash_select_columns  SELECT columns for trashcan.
5670  * @param[in]  where_columns     WHERE columns.
5671  * @param[in]  trash_where_columns   WHERE columns for trashcan.
5672  * @param[in]  filter_columns        Extra columns.
5673  * @param[in]  distinct          Whether the query should be distinct.  Skipped
5674  *                               for trash and single resource.
5675  * @param[in]  extra_tables      Join tables.  Skipped for trash and single
5676  *                               resource.
5677  * @param[in]  extra_where       Extra WHERE clauses.  Skipped for trash and
5678  *                               single resource.
5679  * @param[in]  extra_with        Extra WITH clauses.
5680  * @param[in]  owned             Only count items owned by current user.
5681  *
5682  * @return Total number of resources in filtered set.
5683  */
5684 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)5685 count2 (const char *type, const get_data_t *get, column_t *select_columns,
5686         column_t *trash_select_columns, column_t *where_columns,
5687         column_t *trash_where_columns, const char **filter_columns,
5688         int distinct, const char *extra_tables, const char *extra_where,
5689         const char *extra_with, int owned)
5690 {
5691   int ret;
5692   gchar *clause, *owned_clause, *owner_filter, *columns, *filter, *with;
5693   array_t *permissions;
5694 
5695   assert (get);
5696 
5697   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
5698     {
5699       filter = filter_term (get->filt_id);
5700       if (filter == NULL)
5701         return -1;
5702     }
5703   else
5704     filter = NULL;
5705 
5706   g_debug ("%s", __func__);
5707 
5708   clause = filter_clause (type, filter ? filter : get->filter, filter_columns,
5709                           get->trash && trash_select_columns
5710                            ? trash_select_columns
5711                            : select_columns,
5712                           get->trash && trash_where_columns
5713                            ? trash_where_columns
5714                            : where_columns,
5715                           get->trash, NULL, NULL, NULL, &permissions,
5716                           &owner_filter);
5717 
5718   g_free (filter);
5719 
5720   owned_clause = acl_where_owned (type, get, owned, owner_filter, 0,
5721                                   permissions, 0, &with);
5722 
5723   if (extra_with)
5724     {
5725       if (with)
5726         {
5727           gchar *old_with;
5728 
5729           old_with = with;
5730           with = g_strdup_printf ("%s, %s", old_with, extra_with);
5731           g_free (old_with);
5732         }
5733       else
5734         with = g_strdup_printf ("WITH %s", extra_with);
5735     }
5736 
5737   g_free (owner_filter);
5738   array_free (permissions);
5739 
5740   if (get->trash && trash_select_columns)
5741     columns = columns_build_select (trash_select_columns);
5742   else
5743     columns = columns_build_select (select_columns);
5744 
5745   if ((distinct == 0)
5746       && (extra_tables == NULL)
5747       && (clause == NULL)
5748       && (extra_where == NULL)
5749       && (strcmp (owned_clause, " t ()") == 0))
5750     ret = sql_int ("%sSELECT count (*) FROM %ss%s;",
5751                    with ? with : "", type,
5752                    get->trash && strcmp (type, "task") ? "_trash" : "");
5753   else
5754     ret = sql_int ("%sSELECT count (%scount_id)"
5755                    " FROM (SELECT %ss%s.id AS count_id"
5756                    "       FROM %ss%s%s"
5757                    "       WHERE %s"
5758                    "       %s%s%s%s) AS subquery;",
5759                    with ? with : "",
5760                    distinct ? "DISTINCT " : "",
5761                    type,
5762                    get->trash && strcmp (type, "task") ? "_trash" : "",
5763                    type,
5764                    get->trash && strcmp (type, "task") ? "_trash" : "",
5765                    extra_tables ? extra_tables : "",
5766                    owned_clause,
5767                    clause ? " AND (" : "",
5768                    clause ? clause : "",
5769                    clause ? ") " : "",
5770                    extra_where ? extra_where : "");
5771 
5772   g_free (with);
5773   g_free (columns);
5774   g_free (owned_clause);
5775   g_free (clause);
5776 
5777   g_debug ("%s: done", __func__);
5778 
5779   return ret;
5780 }
5781 
5782 /**
5783  * @brief Count number of a particular resource.
5784  *
5785  * @param[in]  type              Type of resource.
5786  * @param[in]  get               GET params.
5787  * @param[in]  select_columns    SELECT columns.
5788  * @param[in]  trash_select_columns  SELECT columns for trashcan.
5789  * @param[in]  filter_columns        Extra columns.
5790  * @param[in]  distinct          Whether the query should be distinct.  Skipped
5791  *                               for trash and single resource.
5792  * @param[in]  extra_tables      Join tables.  Skipped for trash and single
5793  *                               resource.
5794  * @param[in]  extra_where       Extra WHERE clauses.  Skipped for trash and
5795  *                               single resource.
5796  * @param[in]  owned             Only count items owned by current user.
5797  *
5798  * @return Total number of resources in filtered set.
5799  */
5800 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)5801 count (const char *type, const get_data_t *get, column_t *select_columns,
5802        column_t *trash_select_columns, const char **filter_columns,
5803        int distinct, const char *extra_tables, const char *extra_where,
5804        int owned)
5805 {
5806   return count2 (type, get, select_columns, trash_select_columns, NULL, NULL,
5807                  filter_columns, distinct, extra_tables, extra_where, NULL,
5808                  owned);
5809 }
5810 
5811 /**
5812  * @brief Count number of info of a given subtype with a given name.
5813  *
5814  * @param[in]  type  GET_INFO subtype.
5815  * @param[out] name  Name of the info item.
5816  *
5817  * @return Total number of get_info items of given type, -1 on error.
5818  */
5819 int
info_name_count(const char * type,const char * name)5820 info_name_count (const char *type, const char *name)
5821 {
5822   gchar *quoted_name;
5823   int count;
5824   assert(type);
5825   assert(name);
5826 
5827   quoted_name = sql_quote (name);
5828   count =  sql_int ("SELECT COUNT(id)"
5829                     " FROM %ss"
5830                     " WHERE name = '%s';",
5831                     type,
5832                     quoted_name);
5833   g_free (quoted_name);
5834 
5835   return count;
5836 }
5837 
5838 
5839 
5840 /**
5841  * @brief Return the database version supported by this manager.
5842  *
5843  * @return Database version supported by this manager.
5844  */
5845 int
manage_db_supported_version()5846 manage_db_supported_version ()
5847 {
5848   return GVMD_DATABASE_VERSION;
5849 }
5850 
5851 /**
5852  * @brief Return the database version of the actual database.
5853  *
5854  * @return Database version read from database, -2 if database is empty,
5855  *         -1 on error.
5856  */
5857 int
manage_db_version()5858 manage_db_version ()
5859 {
5860   int number;
5861   char *version;
5862 
5863   if (manage_db_empty ())
5864     return -2;
5865 
5866   version = sql_string ("SELECT value FROM %s.meta"
5867                         " WHERE name = 'database_version' LIMIT 1;",
5868                         sql_schema ());
5869   if (version)
5870     {
5871       number = atoi (version);
5872       free (version);
5873       return number;
5874     }
5875   return -1;
5876 }
5877 
5878 /**
5879  * @brief Return the database version supported by this manager.
5880  *
5881  * @return Database version supported by this manager.
5882  */
5883 int
manage_scap_db_supported_version()5884 manage_scap_db_supported_version ()
5885 {
5886   return GVMD_SCAP_DATABASE_VERSION;
5887 }
5888 
5889 /**
5890  * @brief Return the database version of the actual database.
5891  *
5892  * @return Database version read from database if possible, else -1.
5893  */
5894 int
manage_scap_db_version()5895 manage_scap_db_version ()
5896 {
5897   if (manage_scap_loaded () == 0)
5898     return -1;
5899 
5900   int number;
5901   char *version = sql_string ("SELECT value FROM scap.meta"
5902                               " WHERE name = 'database_version' LIMIT 1;");
5903   if (version)
5904     {
5905       number = atoi (version);
5906       free (version);
5907       return number;
5908     }
5909   return -1;
5910 }
5911 
5912 /**
5913  * @brief Return the database version supported by this manager.
5914  *
5915  * @return Database version supported by this manager.
5916  */
5917 int
manage_cert_db_supported_version()5918 manage_cert_db_supported_version ()
5919 {
5920   return GVMD_CERT_DATABASE_VERSION;
5921 }
5922 
5923 /**
5924  * @brief Return the database version of the actual database.
5925  *
5926  * @return Database version read from database if possible, else -1.
5927  */
5928 int
manage_cert_db_version()5929 manage_cert_db_version ()
5930 {
5931   if (manage_cert_loaded () == 0)
5932     return -1;
5933 
5934   int number;
5935   char *version = sql_string ("SELECT value FROM cert.meta"
5936                               " WHERE name = 'database_version' LIMIT 1;");
5937   if (version)
5938     {
5939       number = atoi (version);
5940       free (version);
5941       return number;
5942     }
5943   return -1;
5944 }
5945 
5946 /**
5947  * @brief Set the database version of the actual database.
5948  *
5949  * Caller must organise transaction.
5950  *
5951  * @param  version  New version number.
5952  */
5953 void
set_db_version(int version)5954 set_db_version (int version)
5955 {
5956   sql ("DELETE FROM %s.meta WHERE name = 'database_version';",
5957        sql_schema ());
5958   sql ("INSERT INTO %s.meta (name, value)"
5959        " VALUES ('database_version', '%i');",
5960        sql_schema (),
5961        version);
5962 }
5963 
5964 
5965 /**
5966  * @brief Encrypt, re-encrypt or decrypt all credentials
5967  *
5968  * All plaintext credentials in the credentials table are
5969  * encrypted, all already encrypted credentials are encrypted again
5970  * using the latest key.
5971  *
5972  * @param[in] decrypt_flag  If true decrypt all credentials.
5973  *
5974  * @return 0 success, -1 error.
5975  */
5976 static int
encrypt_all_credentials(gboolean decrypt_flag)5977 encrypt_all_credentials (gboolean decrypt_flag)
5978 {
5979   iterator_t iterator;
5980   unsigned long ntotal, nencrypted, nreencrypted, ndecrypted;
5981 
5982   init_iterator (&iterator,
5983                  "SELECT id,"
5984                  " (SELECT value FROM credentials_data"
5985                  "  WHERE credential = credentials.id"
5986                  "  AND type = 'secret'),"
5987                  " (SELECT value FROM credentials_data"
5988                  "  WHERE credential = credentials.id"
5989                  "  AND type = 'password'),"
5990                  " (SELECT value FROM credentials_data"
5991                  "  WHERE credential = credentials.id"
5992                  "  AND type = 'private_key')"
5993                  " FROM credentials");
5994   iterator.crypt_ctx = lsc_crypt_new ();
5995 
5996   sql_begin_immediate ();
5997 
5998   ntotal = nencrypted = nreencrypted = ndecrypted = 0;
5999   while (next (&iterator))
6000     {
6001       long long int rowid;
6002       const char *secret, *password, *privkey;
6003 
6004       ntotal++;
6005       if (!(ntotal % 10))
6006         g_message ("  %lu credentials so far processed", ntotal);
6007 
6008       rowid    = iterator_int64 (&iterator, 0);
6009       secret   = iterator_string (&iterator, 1);
6010       password = iterator_string (&iterator, 2);
6011       privkey  = iterator_string (&iterator, 3);
6012 
6013       /* If there is no secret, password or private key, skip the row.  */
6014       if (!secret && !password && !privkey)
6015         continue;
6016 
6017       if (secret)
6018         {
6019           lsc_crypt_flush (iterator.crypt_ctx);
6020           password = lsc_crypt_get_password (iterator.crypt_ctx, secret);
6021           privkey  = lsc_crypt_get_private_key (iterator.crypt_ctx, secret);
6022 
6023           /* If there is no password or private key, skip the row.  */
6024           if (!password && !privkey)
6025             continue;
6026 
6027           nreencrypted++;
6028         }
6029       else
6030         {
6031           if (decrypt_flag)
6032             continue; /* Skip non-encrypted rows.  */
6033 
6034           nencrypted++;
6035         }
6036 
6037       if (decrypt_flag)
6038         {
6039           set_credential_data (rowid, "password", password);
6040           set_credential_data (rowid, "private_key", privkey);
6041           set_credential_data (rowid, "secret", NULL);
6042           sql ("UPDATE credentials SET"
6043                " modification_time = m_now ()"
6044                " WHERE id = %llu;", rowid);
6045           ndecrypted++;
6046         }
6047       else
6048         {
6049           char *encblob;
6050 
6051           if (password && privkey)
6052             encblob = lsc_crypt_encrypt (iterator.crypt_ctx,
6053                                          "password", password,
6054                                          "private_key", privkey, NULL);
6055           else if (password)
6056             encblob = lsc_crypt_encrypt (iterator.crypt_ctx,
6057                                          "password", password, NULL);
6058           else
6059             encblob = lsc_crypt_encrypt (iterator.crypt_ctx,
6060                                          "private_key", privkey, NULL);
6061 
6062           if (!encblob)
6063             {
6064               sql_rollback ();
6065               cleanup_iterator (&iterator);
6066               return -1;
6067             }
6068           set_credential_data (rowid, "password", NULL);
6069           set_credential_data (rowid, "private_key", NULL);
6070           set_credential_data (rowid, "secret", encblob);
6071           sql ("UPDATE credentials SET"
6072                " modification_time = m_now ()"
6073                " WHERE id = %llu;", rowid);
6074           g_free (encblob);
6075         }
6076     }
6077 
6078   sql_commit ();
6079 
6080   if (decrypt_flag)
6081     g_message ("%lu out of %lu credentials decrypted",
6082                ndecrypted, ntotal);
6083   else
6084     g_message ("%lu out of %lu credentials encrypted and %lu re-encrypted",
6085                nencrypted, ntotal, nreencrypted);
6086   cleanup_iterator (&iterator);
6087   return 0;
6088 }
6089 
6090 /**
6091  * @brief Encrypt or re-encrypt all credentials
6092  *
6093  * All plaintext credentials in the credentials table are
6094  * encrypted, all already encrypted credentials are encrypted again
6095  * using the latest key.
6096  *
6097  * @param[in] log_config    Log configuration.
6098  * @param[in] database      Location of manage database.
6099  *
6100  * @return 0 success, -1 error,
6101  *         -2 database is wrong version, -3 database needs to be initialised
6102  *         from server.
6103  */
6104 int
manage_encrypt_all_credentials(GSList * log_config,const db_conn_info_t * database)6105 manage_encrypt_all_credentials (GSList *log_config,
6106                                 const db_conn_info_t *database)
6107 {
6108   int ret;
6109 
6110   g_info ("   (Re-)encrypting all credentials.");
6111 
6112   ret = manage_option_setup (log_config, database);
6113   if (ret)
6114     return ret;
6115 
6116   ret = encrypt_all_credentials (FALSE);
6117   if (ret)
6118     printf ("Encryption failed.\n");
6119   else
6120     printf ("Encryption succeeded.\n");
6121 
6122   manage_option_cleanup ();
6123 
6124   return ret;
6125 }
6126 
6127 /**
6128  * @brief Decrypt all credentials
6129  *
6130  * @param[in] log_config    Log configuration.
6131  * @param[in] database      Location of manage database.
6132  *
6133  * @return 0 success, -1 error,
6134  *         -2 database is wrong version, -3 database needs to be initialised
6135  *         from server.
6136  */
6137 int
manage_decrypt_all_credentials(GSList * log_config,const db_conn_info_t * database)6138 manage_decrypt_all_credentials (GSList *log_config,
6139                                 const db_conn_info_t *database)
6140 {
6141   int ret;
6142 
6143   g_info ("   Decrypting all credentials.");
6144 
6145   ret = manage_option_setup (log_config, database);
6146   if (ret)
6147     return ret;
6148 
6149   ret = encrypt_all_credentials (TRUE);
6150   if (ret)
6151     printf ("Decryption failed.\n");
6152   else
6153     printf ("Decryption succeeded.\n");
6154 
6155   manage_option_cleanup ();
6156 
6157   return ret;
6158 }
6159 
6160 
6161 /* Collation. */
6162 
6163 /**
6164  * @brief Compare two number strings for collate_ip.
6165  *
6166  * @param[in]  one_arg  First string.
6167  * @param[in]  two_arg  Second string.
6168  *
6169  * @return -1, 0 or 1 if first is less than, equal to or greater than second.
6170  */
6171 static int
collate_ip_compare(const char * one_arg,const char * two_arg)6172 collate_ip_compare (const char *one_arg, const char *two_arg)
6173 {
6174   int one = atoi (one_arg);
6175   int two = atoi (two_arg);
6176   return one == two ? 0 : (one < two ? -1 : 1);
6177 }
6178 
6179 /**
6180  * @brief Collate two IP addresses.
6181  *
6182  * For example, 127.0.0.2 is less than 127.0.0.3 and 127.0.0.10.
6183  *
6184  * Only works correctly for IPv4 addresses.
6185  *
6186  * @param[in]  data     Dummy for callback.
6187  * @param[in]  one_len  Length of first IP (a string).
6188  * @param[in]  arg_one  First string.
6189  * @param[in]  two_len  Length of second IP (a string).
6190  * @param[in]  arg_two  Second string.
6191  *
6192  * @return -1, 0 or 1 if first is less than, equal to or greater than second.
6193  */
6194 static int
collate_ip(void * data,int one_len,const void * arg_one,int two_len,const void * arg_two)6195 collate_ip (void* data,
6196             int one_len, const void* arg_one,
6197             int two_len, const void* arg_two)
6198 {
6199   int ret, one_dot, two_dot;
6200   char one_a[4], one_b[4], one_c[4], one_d[4];
6201   char two_a[4], two_b[4], two_c[4], two_d[4];
6202   const char* one = (const char*) arg_one;
6203   const char* two = (const char*) arg_two;
6204 
6205   if ((sscanf (one, "%3[0-9].%3[0-9].%3[0-9].%n%3[0-9]",
6206                one_a, one_b, one_c, &one_dot, one_d)
6207        == 4)
6208       && (sscanf (two, "%3[0-9].%3[0-9].%3[0-9].%n%3[0-9]",
6209                   two_a, two_b, two_c, &two_dot, two_d)
6210           == 4))
6211     {
6212       ret = collate_ip_compare (one_a, two_a);
6213       if (ret) return ret < 0 ? -1 : 1;
6214 
6215       ret = collate_ip_compare (one_b, two_b);
6216       if (ret) return ret < 0 ? -1 : 1;
6217 
6218       ret = collate_ip_compare (one_c, two_c);
6219       if (ret) return ret < 0 ? -1 : 1;
6220 
6221       /* Ensure that the last number is limited to digits in the arg. */
6222       one_d[one_len - one_dot] = '\0';
6223       two_d[two_len - two_dot] = '\0';
6224 
6225       ret = collate_ip_compare (one_d, two_d);
6226       if (ret) return ret < 0 ? -1 : 1;
6227 
6228       return 0;
6229     }
6230 
6231   ret = strncmp (one, two, MIN (one_len, two_len));
6232   return ret == 0 ? 0 : (ret < 0 ? -1 : 1);
6233 }
6234 
6235 
6236 /* Task subject iterators. */
6237 
6238 /**
6239  * @brief Initialise a task user iterator.
6240  *
6241  * @param[in]  iterator  Iterator.
6242  * @param[in]  task      Task.
6243  */
6244 static void
init_task_user_iterator(iterator_t * iterator,task_t task)6245 init_task_user_iterator (iterator_t *iterator, task_t task)
6246 {
6247   init_iterator (iterator,
6248                  "SELECT DISTINCT 1, resource, subject,"
6249                  " (SELECT name FROM users"
6250                  "  WHERE users.id = permissions.subject)"
6251                  " FROM permissions"
6252                  /* Any permission implies 'get_tasks'. */
6253                  " WHERE resource_type = 'task'"
6254                  " AND resource = %llu"
6255                  " AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
6256                  " AND subject_type = 'user'"
6257                  " AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
6258                  task);
6259 }
6260 
6261 static
6262 DEF_ACCESS (task_user_iterator_name, 3);
6263 
6264 /**
6265  * @brief Initialise a task group iterator.
6266  *
6267  * @param[in]  iterator  Iterator.
6268  * @param[in]  task      Task.
6269  */
6270 void
init_task_group_iterator(iterator_t * iterator,task_t task)6271 init_task_group_iterator (iterator_t *iterator, task_t task)
6272 {
6273   init_iterator (iterator,
6274                  "SELECT DISTINCT 1, resource, subject,"
6275                  " (SELECT name FROM groups"
6276                  "  WHERE groups.id = permissions.subject),"
6277                  " (SELECT uuid FROM groups"
6278                  "  WHERE groups.id = permissions.subject)"
6279                  " FROM permissions"
6280                  /* Any permission implies 'get_tasks'. */
6281                  " WHERE resource_type = 'task'"
6282                  " AND resource = %llu"
6283                  " AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
6284                  " AND subject_type = 'group'"
6285                  " AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
6286                  task);
6287 }
6288 
6289 DEF_ACCESS (task_group_iterator_name, 3);
6290 
6291 DEF_ACCESS (task_group_iterator_uuid, 4);
6292 
6293 /**
6294  * @brief Initialise a task role iterator.
6295  *
6296  * @param[in]  iterator  Iterator.
6297  * @param[in]  task      Task.
6298  */
6299 void
init_task_role_iterator(iterator_t * iterator,task_t task)6300 init_task_role_iterator (iterator_t *iterator, task_t task)
6301 {
6302   init_iterator (iterator,
6303                  "SELECT DISTINCT 1, resource, subject,"
6304                  " (SELECT name FROM roles"
6305                  "  WHERE roles.id = permissions.subject),"
6306                  " (SELECT uuid FROM roles"
6307                  "  WHERE roles.id = permissions.subject)"
6308                  " FROM permissions"
6309                  /* Any permission implies 'get'. */
6310                  " WHERE resource_type = 'task'"
6311                  " AND resource = %llu"
6312                  " AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
6313                  " AND subject_type = 'role'",
6314                  task);
6315 }
6316 
6317 DEF_ACCESS (task_role_iterator_name, 3);
6318 
6319 DEF_ACCESS (task_role_iterator_uuid, 4);
6320 
6321 
6322 /* Events and Alerts. */
6323 
6324 /**
6325  * @brief Check if any SecInfo alerts are due.
6326  */
6327 void
check_alerts()6328 check_alerts ()
6329 {
6330   if (manage_scap_loaded ())
6331     {
6332       int max_time;
6333 
6334       max_time
6335        = sql_int ("SELECT %s"
6336                   "        ((SELECT max (modification_time) FROM scap.cves),"
6337                   "         (SELECT max (modification_time) FROM scap.cpes),"
6338                   "         (SELECT max (modification_time) FROM scap.ovaldefs),"
6339                   "         (SELECT max (creation_time) FROM scap.cves),"
6340                   "         (SELECT max (creation_time) FROM scap.cpes),"
6341                   "         (SELECT max (creation_time) FROM scap.ovaldefs));",
6342                   sql_greatest ());
6343 
6344       if (sql_int ("SELECT NOT EXISTS (SELECT * FROM meta"
6345                    "                   WHERE name = 'scap_check_time')"))
6346         sql ("INSERT INTO meta (name, value)"
6347              " VALUES ('scap_check_time', %i);",
6348              max_time);
6349       else if (sql_int ("SELECT value = '0' FROM meta"
6350                         " WHERE name = 'scap_check_time';"))
6351         sql ("UPDATE meta SET value = %i"
6352              " WHERE name = 'scap_check_time';",
6353              max_time);
6354       else
6355         {
6356           check_for_new_scap ();
6357           check_for_updated_scap ();
6358           sql ("UPDATE meta SET value = %i"
6359                " WHERE name = 'scap_check_time';",
6360                max_time);
6361         }
6362     }
6363 
6364   if (manage_cert_loaded ())
6365     {
6366       int max_time;
6367 
6368       max_time
6369        = sql_int ("SELECT"
6370                   " %s"
6371                   "  ((SELECT max (modification_time) FROM cert.cert_bund_advs),"
6372                   "   (SELECT max (modification_time) FROM cert.dfn_cert_advs),"
6373                   "   (SELECT max (creation_time) FROM cert.cert_bund_advs),"
6374                   "   (SELECT max (creation_time) FROM cert.dfn_cert_advs));",
6375                   sql_greatest ());
6376 
6377       if (sql_int ("SELECT NOT EXISTS (SELECT * FROM meta"
6378                    "                   WHERE name = 'cert_check_time')"))
6379         sql ("INSERT INTO meta (name, value)"
6380              " VALUES ('cert_check_time', %i);",
6381              max_time);
6382       else if (sql_int ("SELECT value = '0' FROM meta"
6383                         " WHERE name = 'cert_check_time';"))
6384         sql ("UPDATE meta SET value = %i"
6385              " WHERE name = 'cert_check_time';",
6386              max_time);
6387       else
6388         {
6389           check_for_new_cert ();
6390           check_for_updated_cert ();
6391           sql ("UPDATE meta SET value = %i"
6392                " WHERE name = 'cert_check_time';",
6393                max_time);
6394         }
6395     }
6396 }
6397 
6398 /**
6399  * @brief Check if any SecInfo alerts are due.
6400  *
6401  * @param[in]  log_config  Log configuration.
6402  * @param[in]  database    Location of manage database.
6403  *
6404  * @return 0 success, -1 error,
6405  *         -2 database is wrong version, -3 database needs to be initialised
6406  *         from server.
6407  */
6408 int
manage_check_alerts(GSList * log_config,const db_conn_info_t * database)6409 manage_check_alerts (GSList *log_config, const db_conn_info_t *database)
6410 {
6411   int ret;
6412 
6413   g_info ("   Checking alerts.");
6414 
6415   ret = manage_option_setup (log_config, database);
6416   if (ret)
6417     return ret;
6418 
6419   /* Setup a dummy user, so that create_user will work. */
6420   current_credentials.uuid = "";
6421 
6422   check_alerts ();
6423 
6424   current_credentials.uuid = NULL;
6425 
6426   manage_option_cleanup ();
6427 
6428   return ret;
6429 }
6430 
6431 /**
6432  * @brief Find a alert for a specific permission, given a UUID.
6433  *
6434  * @param[in]   uuid        UUID of alert.
6435  * @param[out]  alert       Alert return, 0 if successfully failed to find alert.
6436  * @param[in]   permission  Permission.
6437  *
6438  * @return FALSE on success (including if failed to find alert), TRUE on error.
6439  */
6440 gboolean
find_alert_with_permission(const char * uuid,alert_t * alert,const char * permission)6441 find_alert_with_permission (const char* uuid, alert_t* alert,
6442                             const char *permission)
6443 {
6444   return find_resource_with_permission ("alert", uuid, alert, permission, 0);
6445 }
6446 
6447 /**
6448  * @brief Validate an email address.
6449  *
6450  * @param[in]  address  Email address.
6451  *
6452  * @return 0 success, 1 failure.
6453  */
6454 static int
validate_email(const char * address)6455 validate_email (const char* address)
6456 {
6457   gchar **split, *point;
6458 
6459   assert (address);
6460 
6461   split = g_strsplit (address, "@", 0);
6462 
6463   if (split[0] == NULL || split[1] == NULL || split[2])
6464     {
6465       g_strfreev (split);
6466       return 1;
6467     }
6468 
6469   /* Local part. */
6470   point = split[0];
6471   while (*point)
6472     if (isalnum (*point)
6473         || strchr ("!#$%&'*+-/=?^_`{|}~", *point)
6474         || ((*point == '.')
6475             && (point > split[0])
6476             && point[1]
6477             && (point[1] != '.')
6478             && (point[-1] != '.')))
6479       point++;
6480     else
6481       {
6482         g_strfreev (split);
6483         return 1;
6484       }
6485 
6486   /* Domain. */
6487   point = split[1];
6488   while (*point)
6489     if (isalnum (*point)
6490         || strchr ("-_", *point)  /* RFC actually forbids _. */
6491         || ((*point == '.')
6492             && (point > split[1])
6493             && point[1]
6494             && (point[1] != '.')
6495             && (point[-1] != '.')))
6496       point++;
6497     else
6498       {
6499         g_strfreev (split);
6500         return 1;
6501       }
6502 
6503   g_strfreev (split);
6504   return 0;
6505 }
6506 
6507 /**
6508  * @brief Validate an email address list.
6509  *
6510  * @param[in]  list  Comma separated list of email addresses.
6511  *
6512  * @return 0 success, 1 failure.
6513  */
6514 static int
validate_email_list(const char * list)6515 validate_email_list (const char *list)
6516 {
6517   gchar **split, **point;
6518 
6519   assert (list);
6520 
6521   split = g_strsplit (list, ",", 0);
6522 
6523   if (split[0] == NULL)
6524     {
6525       g_strfreev (split);
6526       return 1;
6527     }
6528 
6529   point = split;
6530   while (*point)
6531     {
6532       const char *address;
6533       address = *point;
6534       while (*address && (*address == ' ')) address++;
6535       if (validate_email (address))
6536         {
6537           g_strfreev (split);
6538           return 1;
6539         }
6540       point++;
6541     }
6542 
6543   g_strfreev (split);
6544   return 0;
6545 }
6546 
6547 /**
6548  * @brief Validate condition data for an alert.
6549  *
6550  * @param[in]  name      Name.
6551  * @param[in]  data      Data to validate.
6552  * @param[in]  condition The condition.
6553  *
6554  * @return 0 on success, 1 unexpected data name, 2 syntax error in data,
6555  *         3 failed to find filter for condition, -1 internal error.
6556  */
6557 static int
validate_alert_condition_data(gchar * name,gchar * data,alert_condition_t condition)6558 validate_alert_condition_data (gchar *name, gchar* data,
6559                                alert_condition_t condition)
6560 {
6561   if (condition == ALERT_CONDITION_ALWAYS)
6562     return 1;
6563   if (condition == ALERT_CONDITION_SEVERITY_AT_LEAST)
6564     {
6565       if (strcmp (name, "severity"))
6566         return 1;
6567 
6568       if (g_regex_match_simple ("^(-1(\\.0)?|[0-9](\\.[0-9])?|10(\\.0))$",
6569                                 data ? data : "",
6570                                 0,
6571                                 0)
6572           == 0)
6573         return 2;
6574     }
6575   else if (condition == ALERT_CONDITION_SEVERITY_CHANGED)
6576     {
6577       if (strcmp (name, "direction"))
6578         return 1;
6579 
6580       if (g_regex_match_simple ("^(increased|decreased|changed)$",
6581                                 data ? data : "",
6582                                 0,
6583                                 0)
6584           == 0)
6585         return 2;
6586     }
6587   else if (condition == ALERT_CONDITION_FILTER_COUNT_AT_LEAST)
6588     {
6589       if (strcmp (name, "filter_id") == 0)
6590         {
6591           filter_t filter;
6592           if (data == NULL)
6593             return 3;
6594           filter = 0;
6595           if (find_filter_with_permission (data, &filter, "get_filters"))
6596             return -1;
6597           if (filter == 0)
6598             return 3;
6599           return 0;
6600         }
6601 
6602       if (strcmp (name, "count"))
6603         return 1;
6604     }
6605   else if (condition == ALERT_CONDITION_FILTER_COUNT_CHANGED)
6606     {
6607       if (strcmp (name, "filter_id") == 0)
6608         {
6609           filter_t filter;
6610           if (data == NULL)
6611             return 3;
6612           filter = 0;
6613           if (find_filter_with_permission (data, &filter, "get_filters"))
6614             return -1;
6615           if (filter == 0)
6616             return 3;
6617           return 0;
6618         }
6619 
6620       if (strcmp (name, "direction")
6621           && strcmp (name, "count"))
6622         return 1;
6623 
6624       if (strcmp (name, "direction") == 0
6625           && g_regex_match_simple ("^(increased|decreased|changed)$",
6626                                    data ? data : "",
6627                                    0,
6628                                    0)
6629              == 0)
6630         return 2;
6631     }
6632 
6633 
6634   return 0;
6635 }
6636 
6637 /**
6638  * @brief Validate event data for an alert.
6639  *
6640  * @param[in]  name   Name.
6641  * @param[in]  data   Data to validate.
6642  * @param[in]  event  The event.
6643  *
6644  * @return 0 on success, 1 unexpected data name, 2 syntax error in data.
6645  */
6646 static int
validate_alert_event_data(gchar * name,gchar * data,event_t event)6647 validate_alert_event_data (gchar *name, gchar* data, event_t event)
6648 {
6649   if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
6650     {
6651       if (strcmp (name, "secinfo_type"))
6652         return 1;
6653 
6654       if (data == NULL)
6655         return 2;
6656 
6657       if (strcasecmp (data, "nvt")
6658           && strcasecmp (data, "cve")
6659           && strcasecmp (data, "cpe")
6660           && strcasecmp (data, "cert_bund_adv")
6661           && strcasecmp (data, "dfn_cert_adv")
6662           && strcasecmp (data, "ovaldef"))
6663         return 2;
6664     }
6665   return 0;
6666 }
6667 
6668 /**
6669  * @brief Validate method data for the email method.
6670  *
6671  * @param[in]  method          Method that data corresponds to.
6672  * @param[in]  name            Name of data.
6673  * @param[in]  data            The data.
6674  * @param[in]  for_modify      Whether to return error codes for modify_alert.
6675  *
6676  * @return 0 valid, 2 or 6: validation of email address failed,
6677  *         7 or 9 subject too long, 8 or 10 message too long,
6678  *         60 recipient credential not found, 61 invalid recipient credential
6679  *         type, -1 error. When for_modify is 0, the first code is returned,
6680  *         otherwise the second one.
6681  */
6682 int
validate_email_data(alert_method_t method,const gchar * name,gchar ** data,int for_modify)6683 validate_email_data (alert_method_t method, const gchar *name, gchar **data,
6684                      int for_modify)
6685 {
6686   if (method == ALERT_METHOD_EMAIL
6687       && strcmp (name, "to_address") == 0
6688       && validate_email_list (*data))
6689     return for_modify ? 6 : 2;
6690 
6691   if (method == ALERT_METHOD_EMAIL
6692       && strcmp (name, "from_address") == 0
6693       && validate_email (*data))
6694     return for_modify ? 6 : 2;
6695 
6696   if (method == ALERT_METHOD_EMAIL
6697       && strcmp (name, "subject") == 0
6698       && strlen (*data) > 80)
6699     return for_modify ? 9 : 7;
6700 
6701   if (method == ALERT_METHOD_EMAIL
6702       && strcmp (name, "message") == 0
6703       && strlen (*data) > max_email_message_length)
6704     return for_modify ? 10 : 8;
6705 
6706   if (method == ALERT_METHOD_EMAIL
6707       && strcmp (name, "recipient_credential") == 0
6708       && *data && strcmp (*data, ""))
6709     {
6710       credential_t credential;
6711       char *type;
6712 
6713       if (find_credential_with_permission (*data, &credential, NULL))
6714         return -1;
6715       else if (credential == 0)
6716         return 60;
6717 
6718       type = credential_type (credential);
6719       if (strcmp (type, "pgp")
6720           && strcmp (type, "smime"))
6721         {
6722           free (type);
6723           return 61;
6724         }
6725       free (type);
6726     }
6727 
6728   return 0;
6729 }
6730 
6731 /**
6732  * @brief Validate method data for the SCP method.
6733  *
6734  * @param[in]  method          Method that data corresponds to.
6735  * @param[in]  name            Name of data.
6736  * @param[in]  data            The data.
6737  *
6738  * @return 0 valid, 15 error in SCP host, 17 failed to find report format for
6739  *         SCP method, 18 error in SCP credential, 19 error in SCP path,
6740  *         -1 error.
6741  */
6742 static int
validate_scp_data(alert_method_t method,const gchar * name,gchar ** data)6743 validate_scp_data (alert_method_t method, const gchar *name, gchar **data)
6744 {
6745   if (method == ALERT_METHOD_SCP
6746       && strcmp (name, "scp_credential") == 0)
6747     {
6748       credential_t credential;
6749       if (find_credential_with_permission (*data, &credential,
6750                                            "get_credentials"))
6751         return -1;
6752       else if (credential == 0)
6753         return 18;
6754       else
6755         {
6756           gchar *username;
6757           username = credential_value (credential, "username");
6758 
6759           if (username == NULL || strlen (username) == 0)
6760             {
6761               g_free (username);
6762               return 18;
6763             }
6764 
6765           if (strchr (username, ':'))
6766             {
6767               g_free (username);
6768               return 18;
6769             }
6770 
6771           g_free (username);
6772         }
6773     }
6774 
6775   if (method == ALERT_METHOD_SCP
6776       && strcmp (name, "scp_path") == 0)
6777     {
6778       if (strlen (*data) == 0)
6779         return 19;
6780     }
6781 
6782   if (method == ALERT_METHOD_SCP
6783       && strcmp (name, "scp_host") == 0)
6784     {
6785       int type;
6786       gchar *stripped;
6787 
6788       stripped = g_strstrip (g_strdup (*data));
6789       type = gvm_get_host_type (stripped);
6790       g_free (stripped);
6791       if ((type != HOST_TYPE_IPV4)
6792           && (type != HOST_TYPE_IPV6)
6793           && (type != HOST_TYPE_NAME))
6794         return 15;
6795     }
6796 
6797   if (method == ALERT_METHOD_SCP
6798       && strcmp (name, "scp_report_format") == 0)
6799     {
6800       report_format_t report_format;
6801 
6802       report_format = 0;
6803       if (find_report_format_with_permission (*data,
6804                                               &report_format,
6805                                               "get_report_formats"))
6806         return -1;
6807       if (report_format == 0)
6808         return 17;
6809     }
6810 
6811   return 0;
6812 }
6813 
6814 /**
6815  * @brief Validate method data for the Send method.
6816  *
6817  * @param[in]  method          Method that data corresponds to.
6818  * @param[in]  name            Name of data.
6819  * @param[in]  data            The data.
6820  *
6821  * @return 0 valid, 12 error in Send host, 13 error in Send port, 14 failed
6822  *         to find report format for Send method, -1 error.
6823  */
6824 static int
validate_send_data(alert_method_t method,const gchar * name,gchar ** data)6825 validate_send_data (alert_method_t method, const gchar *name, gchar **data)
6826 {
6827   if (method == ALERT_METHOD_SEND
6828       && strcmp (name, "send_host") == 0)
6829     {
6830       int type;
6831       gchar *stripped;
6832 
6833       stripped = g_strstrip (g_strdup (*data));
6834       type = gvm_get_host_type (stripped);
6835       g_free (stripped);
6836       if ((type != HOST_TYPE_IPV4)
6837           && (type != HOST_TYPE_IPV6)
6838           && (type != HOST_TYPE_NAME))
6839         return 12;
6840     }
6841 
6842   if (method == ALERT_METHOD_SEND
6843       && strcmp (name, "send_port") == 0)
6844     {
6845       int port;
6846       gchar *stripped, *end;
6847 
6848       stripped = g_strstrip (g_strdup (*data));
6849       port = strtol (stripped, &end, 10);
6850       if (*end != '\0')
6851         {
6852           g_free (stripped);
6853           return 13;
6854         }
6855 
6856       g_free (stripped);
6857       g_free (*data);
6858       *data = g_strdup_printf ("%i", port);
6859     }
6860 
6861   if (method == ALERT_METHOD_SEND
6862       && strcmp (name, "send_report_format") == 0)
6863     {
6864       report_format_t report_format;
6865 
6866       report_format = 0;
6867       if (find_report_format_with_permission (*data,
6868                                               &report_format,
6869                                               "get_report_formats"))
6870         return -1;
6871       if (report_format == 0)
6872         return 14;
6873     }
6874 
6875   return 0;
6876 }
6877 
6878 /**
6879  * @brief Validate method data for the Send method.
6880  *
6881  * @param[in]  method          Method that data corresponds to.
6882  * @param[in]  name            Name of data.
6883  * @param[in]  data            The data.
6884  *
6885  * @return 0 valid, 40 invalid credential, 41 invalid SMB share path,
6886  *         42 invalid SMB file path, 43 SMB file path contains dot, -1 error.
6887  */
6888 static int
validate_smb_data(alert_method_t method,const gchar * name,gchar ** data)6889 validate_smb_data (alert_method_t method, const gchar *name, gchar **data)
6890 {
6891   if (method == ALERT_METHOD_SMB)
6892     {
6893       if (strcmp (name, "smb_credential") == 0)
6894         {
6895           credential_t credential;
6896           if (find_credential_with_permission (*data, &credential,
6897                                               "get_credentials"))
6898             return -1;
6899           else if (credential == 0)
6900             return 40;
6901           else
6902             {
6903               gchar *username;
6904               username = credential_value (credential, "username");
6905 
6906               if (username == NULL || strlen (username) == 0)
6907                 {
6908                   g_free (username);
6909                   return 40;
6910                 }
6911 
6912               if (strchr (username, '@') || strchr (username, ':'))
6913                 {
6914                   g_free (username);
6915                   return 40;
6916                 }
6917 
6918               g_free (username);
6919             }
6920         }
6921 
6922       if (strcmp (name, "smb_share_path") == 0)
6923         {
6924           /* Check if share path has the correct format
6925            *  "\\<host>\<share>" */
6926           if (g_regex_match_simple ("^(?>\\\\\\\\|\\/\\/)[^:?<>|]+"
6927                                     "(?>\\\\|\\/)[^:?<>|]+$", *data, 0, 0)
6928               == FALSE)
6929             {
6930               return 41;
6931             }
6932         }
6933 
6934       if (strcmp (name, "smb_file_path") == 0)
6935         {
6936           /* Check if file path contains invalid characters:
6937            *  ":", "?", "<", ">", "|" */
6938           if (g_regex_match_simple ("^[^:?<>|]+$", *data, 0, 0)
6939               == FALSE)
6940             {
6941               return 42;
6942             }
6943           /* Check if a file or directory name ends with a dot,
6944            *  e.g. "../a", "abc/../xyz" or "abc/..". */
6945           else if (g_regex_match_simple ("^(?:.*\\.)(?:[\\/\\\\].*)*$",
6946                                          *data, 0, 0))
6947             {
6948               return 43;
6949             }
6950         }
6951 
6952     }
6953 
6954   return 0;
6955 }
6956 
6957 /**
6958  * @brief Validate method data for the TippingPoint method.
6959  *
6960  * @param[in]  method          Method that data corresponds to.
6961  * @param[in]  name            Name of data.
6962  * @param[in]  data            The data.
6963  *
6964  * @return 0 valid, 50 invalid credential, 51 invalid hostname,
6965  *  52 invalid certificate, 53 invalid TLS workaround setting.
6966  */
6967 static int
validate_tippingpoint_data(alert_method_t method,const gchar * name,gchar ** data)6968 validate_tippingpoint_data (alert_method_t method, const gchar *name,
6969                              gchar **data)
6970 {
6971   if (method == ALERT_METHOD_TIPPINGPOINT)
6972     {
6973       if (strcmp (name, "tp_sms_credential") == 0)
6974         {
6975           credential_t credential;
6976           if (find_credential_with_permission (*data, &credential,
6977                                                "get_credentials"))
6978             return -1;
6979           else if (credential == 0)
6980             return 50;
6981           else
6982             {
6983               if (strcmp (credential_type (credential), "up"))
6984                 return 50;
6985 
6986             }
6987         }
6988 
6989       if (strcmp (name, "tp_sms_hostname") == 0)
6990         {
6991           if (g_regex_match_simple ("^[0-9A-Za-z][0-9A-Za-z.-]*$",
6992                                     *data, 0, 0)
6993               == FALSE)
6994             {
6995               return 51;
6996             }
6997         }
6998 
6999       if (strcmp (name, "tp_sms_tls_certificate") == 0)
7000         {
7001           // TODO: Check certificate, return 52 on failure
7002         }
7003 
7004       if (strcmp (name, "tp_sms_tls_workaround") == 0)
7005         {
7006           if (g_regex_match_simple ("^0|1$", *data, 0, 0)
7007               == FALSE)
7008             {
7009               return 53;
7010             }
7011         }
7012     }
7013 
7014   return 0;
7015 }
7016 
7017 /**
7018  * @brief Validate method data for the vFire alert method.
7019  *
7020  * @param[in]  method          Method that data corresponds to.
7021  * @param[in]  name            Name of data.
7022  * @param[in]  data            The data.
7023  *
7024  * @return 0 valid, 70 credential not found, 71 invalid credential type
7025  */
7026 static int
validate_vfire_data(alert_method_t method,const gchar * name,gchar ** data)7027 validate_vfire_data (alert_method_t method, const gchar *name,
7028                      gchar **data)
7029 {
7030   if (method == ALERT_METHOD_VFIRE)
7031     {
7032       if (strcmp (name, "vfire_credential") == 0)
7033         {
7034           credential_t credential;
7035           if (find_credential_with_permission (*data, &credential,
7036                                                "get_credentials"))
7037             return -1;
7038           else if (credential == 0)
7039             return 70;
7040           else
7041             {
7042               char *cred_type = credential_type (credential);
7043               if (strcmp (cred_type, "up"))
7044                 {
7045                   free (cred_type);
7046                   return 71;
7047                 }
7048               free (cred_type);
7049             }
7050         }
7051     }
7052   return 0;
7053 }
7054 
7055 /**
7056  * @brief Validate method data for the Sourcefire method.
7057  *
7058  * @param[in]  method          Method that data corresponds to.
7059  * @param[in]  name            Name of data.
7060  * @param[in]  data            The data.
7061  *
7062  * @return 0 valid, 80 credential not found, 81 invalid credential type
7063  */
7064 static int
validate_sourcefire_data(alert_method_t method,const gchar * name,gchar ** data)7065 validate_sourcefire_data (alert_method_t method, const gchar *name,
7066                           gchar **data)
7067 {
7068   if (method == ALERT_METHOD_SOURCEFIRE)
7069     {
7070       if (strcmp (name, "pkcs12_credential") == 0)
7071         {
7072           credential_t credential;
7073           if (find_credential_with_permission (*data, &credential,
7074                                                "get_credentials"))
7075             return -1;
7076           else if (credential == 0)
7077             return 80;
7078           else
7079             {
7080               char *sourcefire_credential_type;
7081               sourcefire_credential_type = credential_type (credential);
7082               if (strcmp (sourcefire_credential_type, "up")
7083                   && strcmp (sourcefire_credential_type, "pw"))
7084                 {
7085                   free (sourcefire_credential_type);
7086                   return 81;
7087                 }
7088               free (sourcefire_credential_type);
7089             }
7090         }
7091     }
7092 
7093   return 0;
7094 }
7095 
7096 /**
7097  * @brief Check alert params.
7098  *
7099  * @param[in]  event           Type of event.
7100  * @param[in]  condition       Event condition.
7101  * @param[in]  method          Escalation method.
7102  *
7103  * @return 0 success, 20 method does not match event, 21 condition does not
7104  *         match event.
7105  */
7106 static int
check_alert_params(event_t event,alert_condition_t condition,alert_method_t method)7107 check_alert_params (event_t event, alert_condition_t condition,
7108                     alert_method_t method)
7109 {
7110   if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
7111     {
7112       if (method == ALERT_METHOD_HTTP_GET
7113           || method == ALERT_METHOD_SOURCEFIRE
7114           || method == ALERT_METHOD_VERINICE)
7115         return 20;
7116 
7117       if (condition == ALERT_CONDITION_SEVERITY_AT_LEAST
7118           || condition == ALERT_CONDITION_SEVERITY_CHANGED
7119           || condition == ALERT_CONDITION_FILTER_COUNT_CHANGED)
7120         return 21;
7121     }
7122   return 0;
7123 }
7124 
7125 /**
7126  * @brief Create an alert.
7127  *
7128  * @param[in]  name            Name of alert.
7129  * @param[in]  comment         Comment on alert.
7130  * @param[in]  filter_id       Filter.
7131  * @param[in]  active          Whether the alert is active.
7132  * @param[in]  event           Type of event.
7133  * @param[in]  event_data      Type-specific event data.
7134  * @param[in]  condition       Event condition.
7135  * @param[in]  condition_data  Condition-specific data.
7136  * @param[in]  method          Escalation method.
7137  * @param[in]  method_data     Data for escalation method.
7138  * @param[out] alert       Created alert on success.
7139  *
7140  * @return 0 success, 1 escalation exists already, 2 validation of email failed,
7141  *         3 failed to find filter, 4 type must be "result" if specified,
7142  *         5 unexpected condition data name, 6 syntax error in condition data,
7143  *         7 email subject too long, 8 email message too long, 9 failed to find
7144  *         filter for condition, 12 error in Send host, 13 error in Send port,
7145  *         14 failed to find report format for Send method, 15 error in
7146  *         SCP host, 17 failed to find report format for SCP method, 18 error
7147  *         in SCP credential, 19 error in SCP path, 20 method does not match
7148  *         event, 21 condition does not match event, 31 unexpected event data
7149  *         name, 32 syntax error in event data, 40 invalid SMB credential
7150  *       , 41 invalid SMB share path, 42 invalid SMB file path,
7151  *         43 SMB file path contains dot,
7152  *         50 invalid TippingPoint credential, 51 invalid TippingPoint hostname,
7153  *         52 invalid TippingPoint certificate, 53 invalid TippingPoint TLS
7154  *         workaround setting, 60 recipient credential not found, 61 invalid
7155  *         recipient credential type, 70 vFire credential not found,
7156  *         71 invalid vFire credential type,
7157  *         99 permission denied, -1 error.
7158  */
7159 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)7160 create_alert (const char* name, const char* comment, const char* filter_id,
7161               const char* active, event_t event, GPtrArray* event_data,
7162               alert_condition_t condition, GPtrArray* condition_data,
7163               alert_method_t method, GPtrArray* method_data,
7164               alert_t *alert)
7165 {
7166   int index, ret;
7167   gchar *item, *quoted_comment;
7168   gchar *quoted_name;
7169   filter_t filter;
7170 
7171   assert (current_credentials.uuid);
7172 
7173   sql_begin_immediate ();
7174 
7175   if (acl_user_may ("create_alert") == 0)
7176     {
7177       sql_rollback ();
7178       return 99;
7179     }
7180 
7181   ret = check_alert_params (event, condition, method);
7182   if (ret)
7183     {
7184       sql_rollback ();
7185       return ret;
7186     }
7187 
7188   filter = 0;
7189   if (event != EVENT_NEW_SECINFO && event != EVENT_UPDATED_SECINFO && filter_id
7190       && strcmp (filter_id, "0"))
7191     {
7192       char *type;
7193 
7194       if (find_filter_with_permission (filter_id, &filter, "get_filters"))
7195         {
7196           sql_rollback ();
7197           return -1;
7198         }
7199 
7200       if (filter == 0)
7201         {
7202           sql_rollback ();
7203           return 3;
7204         }
7205 
7206       /* Filter type must be result if specified. */
7207 
7208       type = sql_string ("SELECT type FROM filters WHERE id = %llu;",
7209                          filter);
7210       if (type && strcasecmp (type, "result"))
7211         {
7212           free (type);
7213           sql_rollback ();
7214           return 4;
7215         }
7216       free (type);
7217     }
7218 
7219   if (resource_with_name_exists (name, "alert", 0))
7220     {
7221       sql_rollback ();
7222       return 1;
7223     }
7224   quoted_name = sql_quote (name);
7225   quoted_comment = sql_quote (comment ?: "");
7226 
7227   sql ("INSERT INTO alerts (uuid, owner, name, comment, event, condition,"
7228        " method, filter, active, creation_time, modification_time)"
7229        " VALUES (make_uuid (),"
7230        " (SELECT id FROM users WHERE users.uuid = '%s'),"
7231        " '%s', '%s', %i, %i, %i, %llu, %i, m_now (), m_now ());",
7232        current_credentials.uuid,
7233        quoted_name,
7234        quoted_comment,
7235        event,
7236        condition,
7237        method,
7238        filter,
7239        active ? strcmp (active, "0") : 1);
7240 
7241   g_free (quoted_comment);
7242   g_free (quoted_name);
7243 
7244   *alert = sql_last_insert_id ();
7245 
7246   index = 0;
7247   while ((item = (gchar*) g_ptr_array_index (condition_data, index++)))
7248     {
7249       int validation_result;
7250       gchar *data_name = sql_quote (item);
7251       gchar *data = sql_quote (item + strlen (item) + 1);
7252 
7253       validation_result = validate_alert_condition_data (data_name,
7254                                                          data,
7255                                                          condition);
7256 
7257       if (validation_result)
7258         {
7259           g_free (data_name);
7260           g_free (data);
7261           sql_rollback ();
7262 
7263           switch (validation_result)
7264             {
7265               case 1:
7266                 return 5;
7267               case 2:
7268                 return 6;
7269               case 3:
7270                 return 9;
7271               default:
7272                 return -1;
7273             }
7274         }
7275 
7276       sql ("INSERT INTO alert_condition_data (alert, name, data)"
7277            " VALUES (%llu, '%s', '%s');",
7278            *alert,
7279            data_name,
7280            data);
7281       g_free (data_name);
7282       g_free (data);
7283     }
7284 
7285   index = 0;
7286   while ((item = (gchar*) g_ptr_array_index (event_data, index++)))
7287     {
7288       int validation_result;
7289       gchar *data_name = sql_quote (item);
7290       gchar *data = sql_quote (item + strlen (item) + 1);
7291 
7292       validation_result = validate_alert_event_data (data_name, data, event);
7293 
7294       if (validation_result)
7295         {
7296           g_free (data_name);
7297           g_free (data);
7298           sql_rollback ();
7299 
7300           switch (validation_result)
7301             {
7302               case 1:
7303                 return 31;
7304               case 2:
7305                 return 32;
7306               default:
7307                 return -1;
7308             }
7309         }
7310 
7311       sql ("INSERT INTO alert_event_data (alert, name, data)"
7312            " VALUES (%llu, '%s', '%s');",
7313            *alert,
7314            data_name,
7315            data);
7316       g_free (data_name);
7317       g_free (data);
7318     }
7319 
7320   index = 0;
7321   while ((item = (gchar*) g_ptr_array_index (method_data, index++)))
7322     {
7323       gchar *data_name, *data;
7324 
7325       data_name = sql_quote (item);
7326       data = sql_quote (item + strlen (item) + 1);
7327 
7328       ret = validate_email_data (method, data_name, &data, 0);
7329       if (ret)
7330         {
7331           g_free (data_name);
7332           g_free (data);
7333           sql_rollback ();
7334           return ret;
7335         }
7336 
7337       ret = validate_scp_data (method, data_name, &data);
7338       if (ret)
7339         {
7340           g_free (data_name);
7341           g_free (data);
7342           sql_rollback ();
7343           return ret;
7344         }
7345 
7346       ret = validate_send_data (method, data_name, &data);
7347       if (ret)
7348         {
7349           g_free (data_name);
7350           g_free (data);
7351           sql_rollback ();
7352           return ret;
7353         }
7354 
7355       ret = validate_smb_data (method, data_name, &data);
7356       if (ret)
7357         {
7358           g_free (data_name);
7359           g_free (data);
7360           sql_rollback ();
7361           return ret;
7362         }
7363 
7364       ret = validate_sourcefire_data (method, data_name, &data);
7365       if (ret)
7366         {
7367           g_free (data_name);
7368           g_free (data);
7369           sql_rollback ();
7370           return ret;
7371         }
7372 
7373       ret = validate_tippingpoint_data (method, data_name, &data);
7374       if (ret)
7375         {
7376           g_free (data_name);
7377           g_free (data);
7378           sql_rollback ();
7379           return ret;
7380         }
7381 
7382       ret = validate_vfire_data (method, data_name, &data);
7383       if (ret)
7384         {
7385           g_free (data_name);
7386           g_free (data);
7387           sql_rollback ();
7388           return ret;
7389         }
7390 
7391       sql ("INSERT INTO alert_method_data (alert, name, data)"
7392            " VALUES (%llu, '%s', '%s');",
7393            *alert,
7394            data_name,
7395            data);
7396       g_free (data_name);
7397       g_free (data);
7398     }
7399 
7400   sql_commit ();
7401 
7402   return 0;
7403 }
7404 
7405 /**
7406  * @brief Create an alert from an existing alert.
7407  *
7408  * @param[in]  name          Name of new alert. NULL to copy from existing.
7409  * @param[in]  comment       Comment on new alert. NULL to copy from
7410  *                           existing.
7411  * @param[in]  alert_id      UUID of existing alert.
7412  * @param[out] new_alert     New alert.
7413  *
7414  * @return 0 success, 1 alert exists already, 2 failed to find existing
7415  *         alert, 99 permission denied, -1 error.
7416  */
7417 int
copy_alert(const char * name,const char * comment,const char * alert_id,alert_t * new_alert)7418 copy_alert (const char* name, const char* comment, const char* alert_id,
7419             alert_t* new_alert)
7420 {
7421   int ret;
7422   alert_t new, old;
7423 
7424   assert (current_credentials.uuid);
7425 
7426   if (alert_id == NULL)
7427     return -1;
7428 
7429   sql_begin_immediate ();
7430 
7431   ret = copy_resource_lock ("alert", name, comment, alert_id,
7432                             "event, condition, method, filter, active",
7433                             1, &new, &old);
7434   if (ret)
7435     {
7436       sql_rollback ();
7437       return ret;
7438     }
7439 
7440   /* Copy the alert condition data */
7441   sql ("INSERT INTO alert_condition_data (alert, name, data)"
7442        " SELECT %llu, name, data FROM alert_condition_data"
7443        "  WHERE alert = %llu;",
7444        new,
7445        old);
7446 
7447   /* Copy the alert event data */
7448   sql ("INSERT INTO alert_event_data (alert, name, data)"
7449        " SELECT %llu, name, data FROM alert_event_data"
7450        "  WHERE alert = %llu;",
7451        new,
7452        old);
7453 
7454   /* Copy the alert method data */
7455   sql ("INSERT INTO alert_method_data (alert, name, data)"
7456        " SELECT %llu, name, data FROM alert_method_data"
7457        "  WHERE alert = %llu;",
7458        new,
7459        old);
7460 
7461   sql_commit ();
7462   if (new_alert) *new_alert = new;
7463   return 0;
7464 }
7465 
7466 /**
7467  * @brief Modify an alert.
7468  *
7469  * @param[in]   alert_id        UUID of alert.
7470  * @param[in]   name            Name of alert.
7471  * @param[in]   comment         Comment on alert.
7472  * @param[in]   filter_id       Filter.
7473  * @param[in]   active          Whether the alert is active.  NULL to leave it
7474  *                              at the current value.
7475  * @param[in]   event           Type of event.
7476  * @param[in]   event_data      Type-specific event data.
7477  * @param[in]   condition       Event condition.
7478  * @param[in]   condition_data  Condition-specific data.
7479  * @param[in]   method          Escalation method.
7480  * @param[in]   method_data     Data for escalation method.
7481  *
7482  * @return 0 success, 1 failed to find alert, 2 alert with new name exists,
7483  *         3 alert_id required, 4 failed to find filter, 5 filter type must be
7484  *         result if specified, 6 Provided email address not valid,
7485  *         7 unexpected condition data name, 8 syntax error in condition data,
7486  *         9 email subject too long, 10 email message too long, 11 failed to
7487  *         find filter for condition, 12 error in Send host, 13 error in Send
7488  *         port, 14 failed to find report format for Send method, 15 error in
7489  *         SCP host, 17 failed to find report format for SCP method, 18 error
7490  *         in SCP credential, 19 error in SCP path, 20 method does not match
7491  *         event, 21 condition does not match event, 31 unexpected event data
7492  *         name, 32 syntax error in event data, 40 invalid SMB credential
7493  *       , 41 invalid SMB share path, 42 invalid SMB file path,
7494  *         43 SMB file path contains dot,
7495  *         50 invalid TippingPoint credential, 51 invalid TippingPoint hostname,
7496  *         52 invalid TippingPoint certificate, 53 invalid TippingPoint TLS
7497  *         workaround setting, 60 recipient credential not found, 61 invalid
7498  *         recipient credential type, 70 vFire credential not found,
7499  *         71 invalid vFire credential type,
7500  *         99 permission denied, -1 internal error.
7501  */
7502 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)7503 modify_alert (const char *alert_id, const char *name, const char *comment,
7504               const char *filter_id, const char *active, event_t event,
7505               GPtrArray *event_data, alert_condition_t condition,
7506               GPtrArray *condition_data, alert_method_t method,
7507               GPtrArray *method_data)
7508 {
7509   int index, ret;
7510   gchar *quoted_name, *quoted_comment, *item;
7511   alert_t alert;
7512   filter_t filter;
7513 
7514   if (alert_id == NULL)
7515     return 3;
7516 
7517   sql_begin_immediate ();
7518 
7519   assert (current_credentials.uuid);
7520 
7521   if (acl_user_may ("modify_alert") == 0)
7522     {
7523       sql_rollback ();
7524       return 99;
7525     }
7526 
7527   ret = check_alert_params (event, condition, method);
7528   if (ret)
7529     {
7530       sql_rollback ();
7531       return ret;
7532     }
7533 
7534   alert = 0;
7535   if (find_alert_with_permission (alert_id, &alert, "modify_alert"))
7536     {
7537       sql_rollback ();
7538       return -1;
7539     }
7540 
7541   if (alert == 0)
7542     {
7543       sql_rollback ();
7544       return 1;
7545     }
7546 
7547   /* Check whether an alert with the same name exists already. */
7548   if (resource_with_name_exists (name, "alert", alert))
7549     {
7550       sql_rollback ();
7551       return 2;
7552     }
7553 
7554   /* Check filter. */
7555   filter = 0;
7556   if (event != EVENT_NEW_SECINFO && event != EVENT_UPDATED_SECINFO && filter_id
7557       && strcmp (filter_id, "0"))
7558     {
7559       char *type;
7560 
7561       if (find_filter_with_permission (filter_id, &filter, "get_filters"))
7562         {
7563           sql_rollback ();
7564           return -1;
7565         }
7566 
7567       if (filter == 0)
7568         {
7569           sql_rollback ();
7570           return 4;
7571         }
7572 
7573       /* Filter type must be report if specified. */
7574 
7575       type = sql_string ("SELECT type FROM filters WHERE id = %llu;",
7576                          filter);
7577       if (type && strcasecmp (type, "result"))
7578         {
7579           free (type);
7580           sql_rollback ();
7581           return 5;
7582         }
7583       free (type);
7584     }
7585 
7586   quoted_name = sql_quote (name ?: "");
7587   quoted_comment = sql_quote (comment ? comment : "");
7588 
7589   sql ("UPDATE alerts SET"
7590        " name = '%s',"
7591        " comment = '%s',"
7592        " filter = %llu,"
7593        " active = %s,"
7594        " modification_time = m_now ()"
7595        " WHERE id = %llu;",
7596        quoted_name,
7597        quoted_comment,
7598        filter,
7599        active
7600         ? (strcmp (active, "0") ? "1" : "0")
7601         : "active",
7602        alert);
7603 
7604   g_free (quoted_comment);
7605   g_free (quoted_name);
7606 
7607   /* Modify alert event */
7608   if (event != EVENT_ERROR)
7609     {
7610       sql ("UPDATE alerts set event = %i WHERE id = %llu", event, alert);
7611       sql ("DELETE FROM alert_event_data WHERE alert = %llu", alert);
7612       index = 0;
7613       while ((item = (gchar*) g_ptr_array_index (event_data, index++)))
7614         {
7615           int validation_result;
7616           gchar *data_name = sql_quote (item);
7617           gchar *data = sql_quote (item + strlen (item) + 1);
7618 
7619           validation_result = validate_alert_event_data (data_name,
7620                                                          data,
7621                                                          event);
7622 
7623           if (validation_result)
7624             {
7625               g_free (data_name);
7626               g_free (data);
7627               sql_rollback ();
7628 
7629               switch (validation_result)
7630                 {
7631                   case 1:
7632                     return 31;
7633                   case 2:
7634                     return 32;
7635                   default:
7636                     return -1;
7637                 }
7638             }
7639 
7640           sql ("INSERT INTO alert_event_data (alert, name, data)"
7641                " VALUES (%llu, '%s', '%s');",
7642                alert,
7643                data_name,
7644                data);
7645           g_free (data_name);
7646           g_free (data);
7647         }
7648     }
7649 
7650   /* Modify alert condition */
7651   if (condition != ALERT_CONDITION_ERROR)
7652     {
7653       sql ("UPDATE alerts set condition = %i WHERE id = %llu",
7654            condition,
7655            alert);
7656       sql ("DELETE FROM alert_condition_data WHERE alert = %llu", alert);
7657       index = 0;
7658       while ((item = (gchar*) g_ptr_array_index (condition_data, index++)))
7659         {
7660           int validation_result;
7661           gchar *data_name = sql_quote (item);
7662           gchar *data = sql_quote (item + strlen (item) + 1);
7663 
7664           validation_result = validate_alert_condition_data (data_name, data,
7665                                                              condition);
7666 
7667           if (validation_result)
7668             {
7669               g_free (data_name);
7670               g_free (data);
7671               sql_rollback ();
7672 
7673               switch (validation_result)
7674                 {
7675                   case 1:
7676                     return 7;
7677                   case 2:
7678                     return 8;
7679                   case 3:
7680                     return 11;
7681                   default:
7682                     return -1;
7683                 }
7684             }
7685 
7686           sql ("INSERT INTO alert_condition_data (alert, name, data)"
7687                " VALUES (%llu, '%s', '%s');",
7688                alert,
7689                data_name,
7690                data);
7691           g_free (data_name);
7692           g_free (data);
7693         }
7694     }
7695 
7696   /* Modify alert method */
7697   if (method != ALERT_METHOD_ERROR)
7698     {
7699       sql ("UPDATE alerts set method = %i WHERE id = %llu", method, alert);
7700       sql ("DELETE FROM alert_method_data WHERE alert = %llu", alert);
7701       index = 0;
7702       while ((item = (gchar*) g_ptr_array_index (method_data, index++)))
7703         {
7704           gchar *data_name, *data;
7705 
7706           data_name = sql_quote (item);
7707           data = sql_quote (item + strlen (item) + 1);
7708 
7709           ret = validate_email_data (method, data_name, &data, 1);
7710           if (ret)
7711             {
7712               g_free (data_name);
7713               g_free (data);
7714               sql_rollback ();
7715               return ret;
7716             }
7717 
7718           ret = validate_scp_data (method, data_name, &data);
7719           if (ret)
7720             {
7721               g_free (data_name);
7722               g_free (data);
7723               sql_rollback ();
7724               return ret;
7725             }
7726 
7727           ret = validate_send_data (method, data_name, &data);
7728           if (ret)
7729             {
7730               g_free (data_name);
7731               g_free (data);
7732               sql_rollback ();
7733               return ret;
7734             }
7735 
7736           ret = validate_smb_data (method, data_name, &data);
7737           if (ret)
7738             {
7739               g_free (data_name);
7740               g_free (data);
7741               sql_rollback ();
7742               return ret;
7743             }
7744 
7745           ret = validate_sourcefire_data (method, data_name, &data);
7746           if (ret)
7747             {
7748               g_free (data_name);
7749               g_free (data);
7750               sql_rollback ();
7751               return ret;
7752             }
7753 
7754           ret = validate_tippingpoint_data (method, data_name, &data);
7755           if (ret)
7756             {
7757               g_free (data_name);
7758               g_free (data);
7759               sql_rollback ();
7760               return ret;
7761             }
7762 
7763           ret = validate_vfire_data (method, data_name, &data);
7764           if (ret)
7765             {
7766               g_free (data_name);
7767               g_free (data);
7768               sql_rollback ();
7769               return ret;
7770             }
7771 
7772           sql ("INSERT INTO alert_method_data (alert, name, data)"
7773                " VALUES (%llu, '%s', '%s');",
7774                alert,
7775                data_name,
7776                data);
7777           g_free (data_name);
7778           g_free (data);
7779         }
7780     }
7781 
7782   sql_commit ();
7783 
7784   return 0;
7785 }
7786 
7787 /**
7788  * @brief Delete an alert.
7789  *
7790  * @param[in]  alert_id  UUID of alert.
7791  * @param[in]  ultimate      Whether to remove entirely, or to trashcan.
7792  *
7793  * @return 0 success, 1 fail because a task refers to the alert, 2 failed
7794  *         to find target, 99 permission denied, -1 error.
7795  */
7796 int
delete_alert(const char * alert_id,int ultimate)7797 delete_alert (const char *alert_id, int ultimate)
7798 {
7799   alert_t alert = 0;
7800 
7801   sql_begin_immediate ();
7802 
7803   if (acl_user_may ("delete_alert") == 0)
7804     {
7805       sql_rollback ();
7806       return 99;
7807     }
7808 
7809   if (find_alert_with_permission (alert_id, &alert, "delete_alert"))
7810     {
7811       sql_rollback ();
7812       return -1;
7813     }
7814 
7815   if (alert == 0)
7816     {
7817       if (find_trash ("alert", alert_id, &alert))
7818         {
7819           sql_rollback ();
7820           return -1;
7821         }
7822       if (alert == 0)
7823         {
7824           sql_rollback ();
7825           return 2;
7826         }
7827       if (ultimate == 0)
7828         {
7829           /* It's already in the trashcan. */
7830           sql_commit ();
7831           return 0;
7832         }
7833 
7834       /* Check if it's in use by a task in the trashcan. */
7835       if (sql_int ("SELECT count(*) FROM task_alerts"
7836                    " WHERE alert = %llu"
7837                    " AND alert_location = " G_STRINGIFY (LOCATION_TRASH) ";",
7838                    alert))
7839         {
7840           sql_rollback ();
7841           return 1;
7842         }
7843 
7844       permissions_set_orphans ("alert", alert, LOCATION_TRASH);
7845       tags_remove_resource ("alert", alert, LOCATION_TRASH);
7846 
7847       sql ("DELETE FROM alert_condition_data_trash WHERE alert = %llu;",
7848            alert);
7849       sql ("DELETE FROM alert_event_data_trash WHERE alert = %llu;",
7850            alert);
7851       sql ("DELETE FROM alert_method_data_trash WHERE alert = %llu;",
7852            alert);
7853       sql ("DELETE FROM alerts_trash WHERE id = %llu;", alert);
7854       sql_commit ();
7855       return 0;
7856     }
7857 
7858   if (ultimate == 0)
7859     {
7860       alert_t trash_alert;
7861 
7862       if (sql_int ("SELECT count(*) FROM task_alerts"
7863                    " WHERE alert = %llu"
7864                    " AND alert_location = " G_STRINGIFY (LOCATION_TABLE)
7865                    " AND (SELECT hidden < 2 FROM tasks"
7866                    "      WHERE id = task_alerts.task);",
7867                    alert))
7868         {
7869           sql_rollback ();
7870           return 1;
7871         }
7872 
7873       sql ("INSERT INTO alerts_trash"
7874            " (uuid, owner, name, comment, event, condition, method, filter,"
7875            "  filter_location, active, creation_time, modification_time)"
7876            " SELECT uuid, owner, name, comment, event, condition, method,"
7877            "        filter, " G_STRINGIFY (LOCATION_TABLE) ", active,"
7878            "        creation_time, m_now ()"
7879            " FROM alerts WHERE id = %llu;",
7880            alert);
7881 
7882       trash_alert = sql_last_insert_id ();
7883 
7884       sql ("INSERT INTO alert_condition_data_trash"
7885            " (alert, name, data)"
7886            " SELECT %llu, name, data"
7887            " FROM alert_condition_data WHERE alert = %llu;",
7888            trash_alert,
7889            alert);
7890 
7891       sql ("INSERT INTO alert_event_data_trash"
7892            " (alert, name, data)"
7893            " SELECT %llu, name, data"
7894            " FROM alert_event_data WHERE alert = %llu;",
7895            trash_alert,
7896            alert);
7897 
7898       sql ("INSERT INTO alert_method_data_trash"
7899            " (alert, name, data)"
7900            " SELECT %llu, name, data"
7901            " FROM alert_method_data WHERE alert = %llu;",
7902            trash_alert,
7903            alert);
7904 
7905       /* Update the location of the alert in any trashcan tasks. */
7906       sql ("UPDATE task_alerts"
7907            " SET alert = %llu,"
7908            "     alert_location = " G_STRINGIFY (LOCATION_TRASH)
7909            " WHERE alert = %llu"
7910            " AND alert_location = " G_STRINGIFY (LOCATION_TABLE) ";",
7911            trash_alert,
7912            alert);
7913 
7914       permissions_set_locations ("alert", alert, trash_alert,
7915                                  LOCATION_TRASH);
7916       tags_set_locations ("alert", alert, trash_alert,
7917                           LOCATION_TRASH);
7918     }
7919   else if (sql_int ("SELECT count(*) FROM task_alerts"
7920                     " WHERE alert = %llu"
7921                     " AND alert_location = " G_STRINGIFY (LOCATION_TABLE) ";",
7922                     alert))
7923     {
7924       sql_rollback ();
7925       return 1;
7926     }
7927   else
7928     {
7929       permissions_set_orphans ("alert", alert, LOCATION_TABLE);
7930       tags_remove_resource ("alert", alert, LOCATION_TABLE);
7931     }
7932 
7933   sql ("DELETE FROM alert_condition_data WHERE alert = %llu;",
7934        alert);
7935   sql ("DELETE FROM alert_event_data WHERE alert = %llu;", alert);
7936   sql ("DELETE FROM alert_method_data WHERE alert = %llu;", alert);
7937   sql ("DELETE FROM alerts WHERE id = %llu;", alert);
7938   sql_commit ();
7939   return 0;
7940 }
7941 
7942 /**
7943  * @brief Return the UUID of an alert.
7944  *
7945  * @param[in]  alert  Alert.
7946  *
7947  * @return UUID of alert.
7948  */
7949 char *
alert_uuid(alert_t alert)7950 alert_uuid (alert_t alert)
7951 {
7952   return sql_string ("SELECT uuid FROM alerts WHERE id = %llu;",
7953                      alert);
7954 }
7955 
7956 /**
7957  * @brief Return the name of an alert.
7958  *
7959  * @param[in]  alert  Alert.
7960  *
7961  * @return Name of alert.
7962  */
7963 static char *
alert_name(alert_t alert)7964 alert_name (alert_t alert)
7965 {
7966   return sql_string ("SELECT name FROM alerts WHERE id = %llu;", alert);
7967 }
7968 
7969 /**
7970  * @brief Return the owner of an alert.
7971  *
7972  * @param[in]  alert  Alert.
7973  *
7974  * @return Owner.
7975  */
7976 static user_t
alert_owner(alert_t alert)7977 alert_owner (alert_t alert)
7978 {
7979   return sql_int64_0 ("SELECT owner FROM alerts WHERE id = %llu;",
7980                       alert);
7981 }
7982 
7983 /**
7984  * @brief Return the UUID of the owner of an alert.
7985  *
7986  * @param[in]  alert  Alert.
7987  *
7988  * @return UUID of owner.
7989  */
7990 static char *
alert_owner_uuid(alert_t alert)7991 alert_owner_uuid (alert_t alert)
7992 {
7993   return sql_string ("SELECT uuid FROM users"
7994                      " WHERE id = (SELECT owner FROM alerts WHERE id = %llu);",
7995                      alert);
7996 }
7997 
7998 /**
7999  * @brief Return the UUID of the filter of an alert.
8000  *
8001  * @param[in]  alert  Alert.
8002  *
8003  * @return UUID if there's a filter, else NULL.
8004  */
8005 static char *
alert_filter_id(alert_t alert)8006 alert_filter_id (alert_t alert)
8007 {
8008   return sql_string ("SELECT"
8009                      " (CASE WHEN (SELECT filter IS NULL OR filter = 0"
8010                      "             FROM alerts WHERE id = %llu)"
8011                      "  THEN NULL"
8012                      "  ELSE (SELECT uuid FROM filters"
8013                      "        WHERE id = (SELECT filter FROM alerts"
8014                      "                    WHERE id = %llu))"
8015                      "  END);",
8016                      alert,
8017                      alert);
8018 }
8019 
8020 /**
8021  * @brief Return the condition associated with an alert.
8022  *
8023  * @param[in]  alert  Alert.
8024  *
8025  * @return Condition.
8026  */
8027 static alert_condition_t
alert_condition(alert_t alert)8028 alert_condition (alert_t alert)
8029 {
8030   return sql_int ("SELECT condition FROM alerts WHERE id = %llu;",
8031                   alert);
8032 }
8033 
8034 /**
8035  * @brief Return the method associated with an alert.
8036  *
8037  * @param[in]  alert  Alert.
8038  *
8039  * @return Method.
8040  */
8041 static alert_method_t
alert_method(alert_t alert)8042 alert_method (alert_t alert)
8043 {
8044   return sql_int ("SELECT method FROM alerts WHERE id = %llu;",
8045                   alert);
8046 }
8047 
8048 /**
8049  * @brief Return the event associated with an alert.
8050  *
8051  * @param[in]  alert  Alert.
8052  *
8053  * @return Event.
8054  */
8055 static event_t
alert_event(alert_t alert)8056 alert_event (alert_t alert)
8057 {
8058   return sql_int ("SELECT event FROM alerts WHERE id = %llu;",
8059                   alert);
8060 }
8061 
8062 /**
8063  * @brief Filter columns for alert iterator.
8064  */
8065 #define ALERT_ITERATOR_FILTER_COLUMNS                                         \
8066  { GET_ITERATOR_FILTER_COLUMNS, "event", "condition", "method",               \
8067    "filter",  NULL }
8068 
8069 /**
8070  * @brief Alert iterator columns.
8071  */
8072 #define ALERT_ITERATOR_COLUMNS                                                \
8073  {                                                                            \
8074    GET_ITERATOR_COLUMNS (alerts),                                             \
8075    { "event", NULL, KEYWORD_TYPE_INTEGER },                                   \
8076    { "condition", NULL, KEYWORD_TYPE_INTEGER },                               \
8077    { "method", NULL, KEYWORD_TYPE_INTEGER },                                  \
8078    { "filter", NULL, KEYWORD_TYPE_INTEGER },                                  \
8079    { G_STRINGIFY (LOCATION_TABLE), NULL, KEYWORD_TYPE_INTEGER },              \
8080    { "active", NULL, KEYWORD_TYPE_INTEGER },                                  \
8081    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
8082  }
8083 
8084 /**
8085  * @brief Alert iterator columns for trash case.
8086  */
8087 #define ALERT_ITERATOR_TRASH_COLUMNS                                          \
8088  {                                                                            \
8089    GET_ITERATOR_COLUMNS (alerts_trash),                                       \
8090    { "event", NULL, KEYWORD_TYPE_INTEGER },                                   \
8091    { "condition", NULL, KEYWORD_TYPE_INTEGER },                               \
8092    { "method", NULL, KEYWORD_TYPE_INTEGER },                                  \
8093    { "filter", NULL, KEYWORD_TYPE_STRING },                                   \
8094    { "filter_location", NULL, KEYWORD_TYPE_INTEGER},                          \
8095    { "active", NULL, KEYWORD_TYPE_INTEGER },                                  \
8096    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
8097  }
8098 
8099 /**
8100  * @brief Count the number of alerts.
8101  *
8102  * @param[in]  get  GET params.
8103  *
8104  * @return Total number of alerts filtered set.
8105  */
8106 int
alert_count(const get_data_t * get)8107 alert_count (const get_data_t *get)
8108 {
8109   static const char *filter_columns[] = ALERT_ITERATOR_FILTER_COLUMNS;
8110   static column_t columns[] = ALERT_ITERATOR_COLUMNS;
8111   static column_t trash_columns[] = ALERT_ITERATOR_TRASH_COLUMNS;
8112   return count ("alert", get, columns, trash_columns, filter_columns, 0, 0, 0,
8113                   TRUE);
8114 }
8115 
8116 /**
8117  * @brief Return whether a alert is in use by a task.
8118  *
8119  * @param[in]  alert  Alert.
8120  *
8121  * @return 1 if in use, else 0.
8122  */
8123 int
alert_in_use(alert_t alert)8124 alert_in_use (alert_t alert)
8125 {
8126   return !!sql_int ("SELECT count (*) FROM task_alerts WHERE alert = %llu;",
8127                     alert);
8128 }
8129 
8130 /**
8131  * @brief Return whether a trashcan alert is in use by a task.
8132  *
8133  * @param[in]  alert  Alert.
8134  *
8135  * @return 1 if in use, else 0.
8136  */
8137 int
trash_alert_in_use(alert_t alert)8138 trash_alert_in_use (alert_t alert)
8139 {
8140   return !!sql_int ("SELECT count(*) FROM task_alerts"
8141                     " WHERE alert = %llu"
8142                     " AND alert_location = " G_STRINGIFY (LOCATION_TRASH),
8143                     alert);
8144 }
8145 
8146 /**
8147  * @brief Return whether a alert is writable.
8148  *
8149  * @param[in]  alert  Alert.
8150  *
8151  * @return 1 if writable, else 0.
8152  */
8153 int
alert_writable(alert_t alert)8154 alert_writable (alert_t alert)
8155 {
8156     return 1;
8157 }
8158 
8159 /**
8160  * @brief Return whether a trashcan alert is writable.
8161  *
8162  * @param[in]  alert  Alert.
8163  *
8164  * @return 1 if writable, else 0.
8165  */
8166 int
trash_alert_writable(alert_t alert)8167 trash_alert_writable (alert_t alert)
8168 {
8169     return 1;
8170 }
8171 
8172 /**
8173  * @brief Initialise an alert iterator, including observed alerts.
8174  *
8175  * @param[in]  iterator    Iterator.
8176  * @param[in]  get         GET data.
8177  *
8178  * @return 0 success, 1 failed to find alert, 2 failed to find filter (filt_id),
8179  *         -1 error.
8180  */
8181 int
init_alert_iterator(iterator_t * iterator,const get_data_t * get)8182 init_alert_iterator (iterator_t* iterator, const get_data_t *get)
8183 {
8184   static const char *filter_columns[] = ALERT_ITERATOR_FILTER_COLUMNS;
8185   static column_t columns[] = ALERT_ITERATOR_COLUMNS;
8186   static column_t trash_columns[] = ALERT_ITERATOR_TRASH_COLUMNS;
8187 
8188   return init_get_iterator (iterator,
8189                             "alert",
8190                             get,
8191                             columns,
8192                             trash_columns,
8193                             filter_columns,
8194                             0,
8195                             NULL,
8196                             NULL,
8197                             TRUE);
8198 }
8199 
8200 /**
8201  * @brief Return the event from an alert iterator.
8202  *
8203  * @param[in]  iterator  Iterator.
8204  *
8205  * @return Event of the alert or NULL if iteration is complete.
8206  */
8207 int
alert_iterator_event(iterator_t * iterator)8208 alert_iterator_event (iterator_t* iterator)
8209 {
8210   int ret;
8211   if (iterator->done) return -1;
8212   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT);
8213   return ret;
8214 }
8215 
8216 /**
8217  * @brief Return the condition from an alert iterator.
8218  *
8219  * @param[in]  iterator  Iterator.
8220  *
8221  * @return Condition of the alert or NULL if iteration is complete.
8222  */
8223 int
alert_iterator_condition(iterator_t * iterator)8224 alert_iterator_condition (iterator_t* iterator)
8225 {
8226   int ret;
8227   if (iterator->done) return -1;
8228   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
8229   return ret;
8230 }
8231 
8232 /**
8233  * @brief Return the method from an alert iterator.
8234  *
8235  * @param[in]  iterator  Iterator.
8236  *
8237  * @return Method of the alert or NULL if iteration is complete.
8238  */
8239 int
alert_iterator_method(iterator_t * iterator)8240 alert_iterator_method (iterator_t* iterator)
8241 {
8242   int ret;
8243   if (iterator->done) return -1;
8244   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
8245   return ret;
8246 }
8247 
8248 /**
8249  * @brief Return the filter from an alert iterator.
8250  *
8251  * @param[in]  iterator  Iterator.
8252  *
8253  * @return Filter of the alert or NULL if iteration is complete.
8254  */
8255 static filter_t
alert_iterator_filter(iterator_t * iterator)8256 alert_iterator_filter (iterator_t* iterator)
8257 {
8258   if (iterator->done) return -1;
8259   return (filter_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
8260 }
8261 
8262 /**
8263  * @brief Return the filter UUID from an alert iterator.
8264  *
8265  * @param[in]  iterator  Iterator.
8266  *
8267  * @return UUID of filter of the alert or NULL if iteration is complete.
8268  */
8269 char *
alert_iterator_filter_uuid(iterator_t * iterator)8270 alert_iterator_filter_uuid (iterator_t* iterator)
8271 {
8272   filter_t filter;
8273 
8274   if (iterator->done) return NULL;
8275 
8276   filter = alert_iterator_filter (iterator);
8277   if (filter)
8278     {
8279       if (iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
8280           == LOCATION_TABLE)
8281         return filter_uuid (filter);
8282       return trash_filter_uuid (filter);
8283     }
8284   return NULL;
8285 }
8286 
8287 /**
8288  * @brief Return the filter name from an alert iterator.
8289  *
8290  * @param[in]  iterator  Iterator.
8291  *
8292  * @return Name of filter of the alert or NULL if iteration is complete.
8293  */
8294 char *
alert_iterator_filter_name(iterator_t * iterator)8295 alert_iterator_filter_name (iterator_t* iterator)
8296 {
8297   filter_t filter;
8298 
8299   if (iterator->done) return NULL;
8300 
8301   filter = alert_iterator_filter (iterator);
8302   if (filter)
8303     {
8304       if (iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
8305           == LOCATION_TABLE)
8306         return filter_name (filter);
8307       return trash_filter_name (filter);
8308     }
8309   return NULL;
8310 }
8311 
8312 /**
8313  * @brief Return the location of an alert iterator filter.
8314  *
8315  * @param[in]  iterator  Iterator.
8316  *
8317  * @return 0 in table, 1 in trash.
8318  */
8319 int
alert_iterator_filter_trash(iterator_t * iterator)8320 alert_iterator_filter_trash (iterator_t* iterator)
8321 {
8322   if (iterator->done) return 0;
8323   if (alert_iterator_filter (iterator)
8324       && (iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
8325           == LOCATION_TRASH))
8326     return 1;
8327   return 0;
8328 }
8329 
8330 /**
8331  * @brief Return the filter readable state from an alert iterator.
8332  *
8333  * @param[in]  iterator  Iterator.
8334  *
8335  * @return Whether filter is readable.
8336  */
8337 int
alert_iterator_filter_readable(iterator_t * iterator)8338 alert_iterator_filter_readable (iterator_t* iterator)
8339 {
8340   filter_t filter;
8341 
8342   if (iterator->done) return 0;
8343 
8344   filter = alert_iterator_filter (iterator);
8345   if (filter)
8346     {
8347       char *uuid;
8348       uuid = alert_iterator_filter_uuid (iterator);
8349       if (uuid)
8350         {
8351           int readable;
8352           readable = acl_user_has_access_uuid
8353                       ("filter", uuid, "get_filters",
8354                        iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4)
8355                        == LOCATION_TRASH);
8356           free (uuid);
8357           return readable;
8358         }
8359     }
8360   return 0;
8361 }
8362 
8363 /**
8364  * @brief Return the active state from an alert.
8365  *
8366  * @param[in]  iterator  Iterator.
8367  *
8368  * @return Method of the alert or NULL if iteration is complete.
8369  */
8370 int
alert_iterator_active(iterator_t * iterator)8371 alert_iterator_active (iterator_t* iterator)
8372 {
8373   int ret;
8374   if (iterator->done) return -1;
8375   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
8376   return ret;
8377 }
8378 
8379 /**
8380  * @brief Initialise an alert data iterator.
8381  *
8382  * @param[in]  iterator   Iterator.
8383  * @param[in]  alert  Alert.
8384  * @param[in]  trash      Whether to iterate over trashcan alert data.
8385  * @param[in]  table      Type of data: "condition", "event" or "method",
8386  *                        corresponds to substring of the table to select
8387  *                        from.
8388  */
8389 void
init_alert_data_iterator(iterator_t * iterator,alert_t alert,int trash,const char * table)8390 init_alert_data_iterator (iterator_t *iterator, alert_t alert,
8391                           int trash, const char *table)
8392 {
8393   init_iterator (iterator,
8394                  "SELECT name, data FROM alert_%s_data%s"
8395                  " WHERE alert = %llu;",
8396                  table,
8397                  trash ? "_trash" : "",
8398                  alert);
8399 }
8400 
8401 /**
8402  * @brief Return the name from an alert data iterator.
8403  *
8404  * @param[in]  iterator  Iterator.
8405  *
8406  * @return Name of the alert data or NULL if iteration is complete.
8407  */
8408 const char*
alert_data_iterator_name(iterator_t * iterator)8409 alert_data_iterator_name (iterator_t* iterator)
8410 {
8411   const char *ret;
8412   if (iterator->done) return NULL;
8413   ret = iterator_string (iterator, 0);
8414   return ret;
8415 }
8416 
8417 /**
8418  * @brief Return the data from an alert data iterator.
8419  *
8420  * @param[in]  iterator  Iterator.
8421  *
8422  *
8423  * @return Data of the alert data or NULL if iteration is complete.
8424  */
8425 const char*
alert_data_iterator_data(iterator_t * iterator)8426 alert_data_iterator_data (iterator_t* iterator)
8427 {
8428   const char *ret;
8429   if (iterator->done) return NULL;
8430   ret = iterator_string (iterator, 1);
8431   return ret;
8432 }
8433 
8434 /**
8435  * @brief Return data associated with an alert.
8436  *
8437  * @param[in]  alert  Alert.
8438  * @param[in]  type       Type of data: "condition", "event" or "method".
8439  * @param[in]  name       Name of the data.
8440  *
8441  * @return Freshly allocated data if it exists, else NULL.
8442  */
8443 char *
alert_data(alert_t alert,const char * type,const char * name)8444 alert_data (alert_t alert, const char *type, const char *name)
8445 {
8446   gchar *quoted_name;
8447   char *data;
8448 
8449   assert (strcmp (type, "condition") == 0
8450           || strcmp (type, "event") == 0
8451           || strcmp (type, "method") == 0);
8452 
8453   quoted_name = sql_quote (name);
8454   data = sql_string ("SELECT data FROM alert_%s_data"
8455                      " WHERE alert = %llu AND name = '%s';",
8456                      type,
8457                      alert,
8458                      quoted_name);
8459   g_free (quoted_name);
8460   return data;
8461 }
8462 
8463 /**
8464  * @brief Check whether an alert applies to a task.
8465  *
8466  * @param[in]  alert  Alert.
8467  * @param[in]  task   Task.
8468  *
8469  * @return 1 if applies, else 0.
8470  */
8471 static int
alert_applies_to_task(alert_t alert,task_t task)8472 alert_applies_to_task (alert_t alert, task_t task)
8473 {
8474   return sql_int ("SELECT EXISTS (SELECT * FROM task_alerts"
8475                   "               WHERE task = %llu"
8476                   "               AND alert = %llu);",
8477                   task,
8478                   alert);
8479 }
8480 
8481 /**
8482  * @brief Initialise a task alert iterator.
8483  *
8484  * @param[in]  iterator  Iterator.
8485  * @param[in]  task      Task.
8486  */
8487 void
init_task_alert_iterator(iterator_t * iterator,task_t task)8488 init_task_alert_iterator (iterator_t* iterator, task_t task)
8489 {
8490   gchar *owned_clause, *with_clause;
8491   get_data_t get;
8492   array_t *permissions;
8493 
8494   assert (task);
8495 
8496   get.trash = 0;
8497   permissions = make_array ();
8498   array_add (permissions, g_strdup ("get_alerts"));
8499   owned_clause = acl_where_owned ("alert", &get, 0, "any", 0, permissions, 0,
8500                                   &with_clause);
8501   array_free (permissions);
8502 
8503   init_iterator (iterator,
8504                  "%s"
8505                  " SELECT alerts.id, alerts.uuid, alerts.name"
8506                  " FROM alerts, task_alerts"
8507                  " WHERE task_alerts.task = %llu"
8508                  " AND task_alerts.alert = alerts.id"
8509                  " AND %s;",
8510                  with_clause ? with_clause : "",
8511                  task,
8512                  owned_clause);
8513 
8514   g_free (with_clause);
8515   g_free (owned_clause);
8516 }
8517 
8518 /**
8519  * @brief Get the UUID from a task alert iterator.
8520  *
8521  * @param[in]  iterator  Iterator.
8522  *
8523  * @return UUID, or NULL if iteration is complete.  Freed by
8524  *         cleanup_iterator.
8525  */
8526 DEF_ACCESS (task_alert_iterator_uuid, 1);
8527 
8528 /**
8529  * @brief Get the name from a task alert iterator.
8530  *
8531  * @param[in]  iterator  Iterator.
8532  *
8533  * @return Name, or NULL if iteration is complete.  Freed by
8534  *         cleanup_iterator.
8535  */
8536 DEF_ACCESS (task_alert_iterator_name, 2);
8537 
8538 /**
8539  * @brief Initialise an event alert iterator.
8540  *
8541  * @param[in]  iterator  Iterator.
8542  * @param[in]  event     Event.
8543  */
8544 static void
init_event_alert_iterator(iterator_t * iterator,event_t event)8545 init_event_alert_iterator (iterator_t* iterator, event_t event)
8546 {
8547   gchar *owned_clause, *with_clause;
8548   get_data_t get;
8549   array_t *permissions;
8550 
8551   assert (event);
8552 
8553   get.trash = 0;
8554   permissions = make_array ();
8555   array_add (permissions, g_strdup ("get_alerts"));
8556   owned_clause = acl_where_owned ("alert", &get, 0, "any", 0, permissions, 0,
8557                                   &with_clause);
8558   array_free (permissions);
8559 
8560   init_iterator (iterator,
8561                  "%s"
8562                  " SELECT alerts.id, alerts.active"
8563                  " FROM alerts"
8564                  " WHERE event = %i"
8565                  " AND %s;",
8566                  with_clause ? with_clause : "",
8567                  event,
8568                  owned_clause);
8569 
8570   g_free (with_clause);
8571   g_free (owned_clause);
8572 }
8573 
8574 /**
8575  * @brief Get the alert from a event alert iterator.
8576  *
8577  * @param[in]  iterator  Iterator.
8578  *
8579  * @return alert.
8580  */
8581 static alert_t
event_alert_iterator_alert(iterator_t * iterator)8582 event_alert_iterator_alert (iterator_t* iterator)
8583 {
8584   if (iterator->done) return 0;
8585   return (task_t) iterator_int64 (iterator, 0);
8586 }
8587 
8588 /**
8589  * @brief Get the active state from an event alert iterator.
8590  *
8591  * @param[in]  iterator  Iterator.
8592  *
8593  * @return Active state.
8594  */
8595 static int
event_alert_iterator_active(iterator_t * iterator)8596 event_alert_iterator_active (iterator_t* iterator)
8597 {
8598   int ret;
8599   if (iterator->done) return -1;
8600   ret = iterator_int (iterator, 1);
8601   return ret;
8602 }
8603 
8604 /**
8605  * @brief Write the content of a plain text email to a stream.
8606  *
8607  * @param[in]  content_file  Stream to write the email content to.
8608  * @param[in]  to_address    Address to send to.
8609  * @param[in]  from_address  Address to send to.
8610  * @param[in]  subject       Subject of email.
8611  * @param[in]  body          Body of email.
8612  * @param[in]  attachment    Attachment in line broken base64, or NULL.
8613  * @param[in]  attachment_type  Attachment MIME type, or NULL.
8614  * @param[in]  attachment_name  Base file name of the attachment, or NULL.
8615  * @param[in]  attachment_extension  Attachment file extension, or NULL.
8616  *
8617  * @return 0 success, -1 error.
8618  */
8619 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)8620 email_write_content (FILE *content_file,
8621                      const char *to_address, const char *from_address,
8622                      const char *subject, const char *body,
8623                      const gchar *attachment, const char *attachment_type,
8624                      const char *attachment_name,
8625                      const char *attachment_extension)
8626 {
8627   if (fprintf (content_file,
8628                "To: %s\n"
8629                "From: %s\n"
8630                "Subject: %s\n"
8631                "%s%s%s"
8632                "\n"
8633                "%s"
8634                "%s\n",
8635                to_address,
8636                from_address ? from_address
8637                             : "automated@openvas.org",
8638                subject,
8639                (attachment
8640                  ? "MIME-Version: 1.0\n"
8641                    "Content-Type: multipart/mixed;"
8642                    " boundary=\""
8643                  : "Content-Type: text/plain; charset=utf-8\n"
8644                    "Content-Transfer-Encoding: 8bit\n"),
8645                /* @todo Future callers may give email containing this string. */
8646                (attachment ? "=-=-=-=-=" : ""),
8647                (attachment ? "\"\n" : ""),
8648                (attachment ? "--=-=-=-=-=\n"
8649                              "Content-Type: text/plain; charset=utf-8\n"
8650                              "Content-Transfer-Encoding: 8bit\n"
8651                              "Content-Disposition: inline\n"
8652                              "\n"
8653                            : ""),
8654                body)
8655       < 0)
8656     {
8657       g_warning ("%s: output error", __func__);
8658       return -1;
8659     }
8660 
8661   if (attachment)
8662     {
8663       int len;
8664 
8665       if (fprintf (content_file,
8666                    "--=-=-=-=-=\n"
8667                    "Content-Type: %s\n"
8668                    "Content-Disposition: attachment;"
8669                    " filename=\"%s.%s\"\n"
8670                    "Content-Transfer-Encoding: base64\n"
8671                    "Content-Description: Report\n\n",
8672                    attachment_type,
8673                    attachment_name,
8674                    attachment_extension)
8675           < 0)
8676         {
8677           g_warning ("%s: output error", __func__);
8678           return -1;
8679         }
8680 
8681       len = strlen (attachment);
8682       while (len)
8683         if (len > 72)
8684           {
8685             if (fprintf (content_file,
8686                          "%.*s\n",
8687                          72,
8688                          attachment)
8689                 < 0)
8690               {
8691                 g_warning ("%s: output error", __func__);
8692                 return -1;
8693               }
8694             attachment += 72;
8695             len -= 72;
8696           }
8697         else
8698           {
8699             if (fprintf (content_file,
8700                          "%s\n",
8701                          attachment)
8702                 < 0)
8703               {
8704                 g_warning ("%s: output error", __func__);
8705                 return -1;
8706               }
8707             break;
8708           }
8709 
8710       if (fprintf (content_file,
8711                    "--=-=-=-=-=--\n")
8712           < 0)
8713         {
8714           g_warning ("%s: output error", __func__);
8715           return -1;
8716         }
8717     }
8718 
8719   while (fflush (content_file))
8720     if (errno == EINTR)
8721       continue;
8722     else
8723       {
8724         g_warning ("%s", strerror (errno));
8725         return -1;
8726       }
8727 
8728   return 0;
8729 }
8730 
8731 /**
8732  * @brief  Create a PGP encrypted email from a plain text one.
8733  *
8734  * @param[in]  plain_file     Stream to read the plain text email from.
8735  * @param[in]  encrypted_file Stream to write the encrypted email to.
8736  * @param[in]  public_key     Recipient public key to use for encryption.
8737  * @param[in]  to_address     Email address to send to.
8738  * @param[in]  from_address   Email address to use as sender.
8739  * @param[in]  subject        Subject of email.
8740  *
8741  * @return 0 success, -1 error.
8742  */
8743 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)8744 email_encrypt_gpg (FILE *plain_file, FILE *encrypted_file,
8745                    const char *public_key,
8746                    const char *to_address, const char *from_address,
8747                    const char *subject)
8748 {
8749   // Headers and metadata parts
8750   if (fprintf (encrypted_file,
8751                "To: %s\n"
8752                "From: %s\n"
8753                "Subject: %s\n"
8754                "MIME-Version: 1.0\n"
8755                "Content-Type: multipart/encrypted;\n"
8756                " protocol=\"application/pgp-encrypted\";\n"
8757                " boundary=\"=-=-=-=-=\"\n"
8758                "\n"
8759                "--=-=-=-=-=\n"
8760                "Content-Type: application/pgp-encrypted\n"
8761                "Content-Description: PGP/MIME version identification\n"
8762                "\n"
8763                "Version: 1\n"
8764                "\n"
8765                "--=-=-=-=-=\n"
8766                "Content-Type: application/octet-stream\n"
8767                "Content-Description: OpenPGP encrypted message\n"
8768                "Content-Disposition: inline; filename=\"encrypted.asc\"\n"
8769                "\n",
8770                to_address,
8771                from_address ? from_address
8772                             : "automated@openvas.org",
8773                subject) < 0)
8774     {
8775       g_warning ("%s: output error at headers", __func__);
8776       return -1;
8777     }
8778 
8779   // Encrypted message
8780   if (gvm_pgp_pubkey_encrypt_stream (plain_file, encrypted_file, to_address,
8781                                      public_key, -1))
8782     {
8783       return -1;
8784     }
8785 
8786   // End of message
8787   if (fprintf (encrypted_file,
8788                "\n"
8789                "--=-=-=-=-=--\n") < 0)
8790     {
8791       g_warning ("%s: output error at end of message", __func__);
8792       return -1;
8793     }
8794 
8795   while (fflush (encrypted_file))
8796     if (errno == EINTR)
8797       continue;
8798     else
8799       {
8800         g_warning ("%s", strerror (errno));
8801         return -1;
8802       }
8803 
8804   return 0;
8805 }
8806 
8807 /**
8808  * @brief  Create an S/MIME encrypted email from a plain text one.
8809  *
8810  * @param[in]  plain_file     Stream to read the plain text email from.
8811  * @param[in]  encrypted_file Stream to write the encrypted email to.
8812  * @param[in]  certificate    Recipient certificate chain for encryption.
8813  * @param[in]  to_address     Email address to send to.
8814  * @param[in]  from_address   Email address to use as sender.
8815  * @param[in]  subject        Subject of email.
8816  *
8817  * @return 0 success, -1 error.
8818  */
8819 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)8820 email_encrypt_smime (FILE *plain_file, FILE *encrypted_file,
8821                      const char *certificate,
8822                      const char *to_address, const char *from_address,
8823                      const char *subject)
8824 {
8825   // Headers and metadata parts
8826   if (fprintf (encrypted_file,
8827                "To: %s\n"
8828                "From: %s\n"
8829                "Subject: %s\n"
8830                "Content-Type: application/x-pkcs7-mime;"
8831                " smime-type=enveloped-data; name=\"smime.p7m\"\n"
8832                "Content-Disposition: attachment; filename=\"smime.p7m\"\n"
8833                "Content-Transfer-Encoding: base64\n"
8834                "\n",
8835                to_address,
8836                from_address ? from_address
8837                             : "automated@openvas.org",
8838                subject) < 0)
8839     {
8840       g_warning ("%s: output error at headers", __func__);
8841       return -1;
8842     }
8843 
8844   // Encrypted message
8845   if (gvm_smime_encrypt_stream (plain_file, encrypted_file, to_address,
8846                                 certificate, -1))
8847     {
8848       g_warning ("%s: encryption failed", __func__);
8849       return -1;
8850     }
8851 
8852   // End of message
8853   if (fprintf (encrypted_file,
8854                "\n") < 0)
8855     {
8856       g_warning ("%s: output error at end of message", __func__);
8857       return -1;
8858     }
8859 
8860   while (fflush (encrypted_file))
8861     if (errno == EINTR)
8862       continue;
8863     else
8864       {
8865         g_warning ("%s", strerror (errno));
8866         return -1;
8867       }
8868 
8869   return 0;
8870 }
8871 
8872 /**
8873  * @brief Send an email.
8874  *
8875  * @param[in]  to_address    Address to send to.
8876  * @param[in]  from_address  Address to send to.
8877  * @param[in]  subject       Subject of email.
8878  * @param[in]  body          Body of email.
8879  * @param[in]  attachment    Attachment in line broken base64, or NULL.
8880  * @param[in]  attachment_type  Attachment MIME type, or NULL.
8881  * @param[in]  attachment_name  Base file name of the attachment, or NULL.
8882  * @param[in]  attachment_extension  Attachment file extension, or NULL.
8883  * @param[in]  recipient_credential  Optional credential to use for encryption.
8884  *
8885  * @return 0 success, -1 error.
8886  */
8887 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)8888 email (const char *to_address, const char *from_address, const char *subject,
8889        const char *body, const gchar *attachment, const char *attachment_type,
8890        const char *attachment_name, const char *attachment_extension,
8891        credential_t recipient_credential)
8892 {
8893   int ret, content_fd, args_fd;
8894   gchar *command;
8895   GError *error = NULL;
8896   char content_file_name[] = "/tmp/gvmd-content-XXXXXX";
8897   char args_file_name[] = "/tmp/gvmd-args-XXXXXX";
8898   gchar *sendmail_args;
8899   FILE *content_file;
8900 
8901   content_fd = mkstemp (content_file_name);
8902   if (content_fd == -1)
8903     {
8904       g_warning ("%s: mkstemp: %s", __func__, strerror (errno));
8905       return -1;
8906     }
8907 
8908   g_debug ("   EMAIL to %s from %s subject: %s, body: %s",
8909           to_address, from_address, subject, body);
8910 
8911   content_file = fdopen (content_fd, "w");
8912   if (content_file == NULL)
8913     {
8914       g_warning ("%s: Could not open content file: %s",
8915                  __func__, strerror (errno));
8916       close (content_fd);
8917       return -1;
8918     }
8919 
8920   if (recipient_credential)
8921     {
8922       iterator_t iterator;
8923       init_credential_iterator_one (&iterator, recipient_credential);
8924 
8925       if (next (&iterator))
8926         {
8927           const char *type = credential_iterator_type (&iterator);
8928           const char *public_key = credential_iterator_public_key (&iterator);
8929           const char *certificate
8930             = credential_iterator_certificate (&iterator);
8931           char plain_file_name[] = "/tmp/gvmd-plain-XXXXXX";
8932           int plain_fd;
8933           FILE *plain_file;
8934 
8935           // Create plain text message
8936           plain_fd = mkstemp (plain_file_name);
8937           if (plain_fd == -1)
8938             {
8939               g_warning ("%s: mkstemp for plain text file: %s",
8940                          __func__, strerror (errno));
8941               fclose (content_file);
8942               unlink (content_file_name);
8943               cleanup_iterator (&iterator);
8944               return -1;
8945             }
8946 
8947           plain_file = fdopen (plain_fd, "w+");
8948           if (plain_file == NULL)
8949             {
8950               g_warning ("%s: Could not open plain text file: %s",
8951                          __func__, strerror (errno));
8952               fclose (content_file);
8953               unlink (content_file_name);
8954               close (plain_fd);
8955               unlink (plain_file_name);
8956               cleanup_iterator (&iterator);
8957               return -1;
8958             }
8959 
8960           if (email_write_content (plain_file,
8961                                    to_address, from_address,
8962                                    subject, body, attachment,
8963                                    attachment_type, attachment_name,
8964                                    attachment_extension))
8965             {
8966               fclose (content_file);
8967               unlink (content_file_name);
8968               fclose (plain_file);
8969               unlink (plain_file_name);
8970               cleanup_iterator (&iterator);
8971               return -1;
8972             }
8973 
8974           rewind (plain_file);
8975 
8976           // Create encrypted email
8977           if (strcmp (type, "pgp") == 0)
8978             {
8979               ret = email_encrypt_gpg (plain_file, content_file,
8980                                        public_key,
8981                                        to_address, from_address, subject);
8982 
8983               fclose (plain_file);
8984               unlink (plain_file_name);
8985 
8986               if (ret)
8987                 {
8988                   g_warning ("%s: PGP encryption failed", __func__);
8989                   fclose (content_file);
8990                   unlink (content_file_name);
8991                   cleanup_iterator (&iterator);
8992                   return -1;
8993                 }
8994             }
8995           else if (strcmp (type, "smime") == 0)
8996             {
8997               ret = email_encrypt_smime (plain_file, content_file,
8998                                          certificate,
8999                                          to_address, from_address, subject);
9000 
9001               fclose (plain_file);
9002               unlink (plain_file_name);
9003 
9004               if (ret)
9005                 {
9006                   g_warning ("%s: S/MIME encryption failed", __func__);
9007                   fclose (content_file);
9008                   unlink (content_file_name);
9009                   cleanup_iterator (&iterator);
9010                   return -1;
9011                 }
9012             }
9013           else
9014             {
9015               g_warning ("%s: Invalid recipient credential type",
9016                         __func__);
9017               fclose (content_file);
9018               unlink (content_file_name);
9019               fclose (plain_file);
9020               unlink (plain_file_name);
9021               cleanup_iterator (&iterator);
9022               return -1;
9023             }
9024         }
9025 
9026       cleanup_iterator (&iterator);
9027     }
9028   else
9029     {
9030       if (email_write_content (content_file,
9031                                to_address, from_address,
9032                                subject, body, attachment, attachment_type,
9033                                attachment_name, attachment_extension))
9034         {
9035           fclose (content_file);
9036           return -1;
9037         }
9038     }
9039 
9040   args_fd = mkstemp (args_file_name);
9041   if (args_fd == -1)
9042     {
9043       g_warning ("%s: mkstemp: %s", __func__, strerror (errno));
9044       fclose (content_file);
9045       return -1;
9046     }
9047 
9048   sendmail_args = g_strdup_printf ("%s %s",
9049                                    from_address,
9050                                    to_address);
9051   g_file_set_contents (args_file_name,
9052                        sendmail_args,
9053                        strlen (sendmail_args),
9054                        &error);
9055   g_free (sendmail_args);
9056 
9057   if (error)
9058     {
9059       g_warning ("%s", error->message);
9060       g_error_free (error);
9061       fclose (content_file);
9062       close (args_fd);
9063       return -1;
9064     }
9065 
9066   command = g_strdup_printf ("read FROM TO < %s;"
9067                              " /usr/sbin/sendmail -f \"$FROM\" \"$TO\" < %s"
9068                              " > /dev/null 2>&1",
9069                              args_file_name,
9070                              content_file_name);
9071 
9072   g_debug ("   command: %s", command);
9073 
9074   ret = system (command);
9075   if ((ret == -1) || WEXITSTATUS (ret))
9076     {
9077       g_warning ("%s: system failed with ret %i, %i, %s",
9078                  __func__,
9079                  ret,
9080                  WEXITSTATUS (ret),
9081                  command);
9082       g_free (command);
9083       fclose (content_file);
9084       close (args_fd);
9085       unlink (content_file_name);
9086       unlink (args_file_name);
9087       return -1;
9088     }
9089   g_free (command);
9090   fclose (content_file);
9091   close (args_fd);
9092   unlink (content_file_name);
9093   unlink (args_file_name);
9094   return 0;
9095 }
9096 
9097 /**
9098  * @brief GET an HTTP resource.
9099  *
9100  * @param[in]  url  URL.
9101  *
9102  * @return 0 success, -1 error.
9103  */
9104 static int
http_get(const char * url)9105 http_get (const char *url)
9106 {
9107   int ret;
9108   gchar *standard_out = NULL;
9109   gchar *standard_err = NULL;
9110   gint exit_status;
9111   gchar **cmd;
9112 
9113   g_debug ("   HTTP_GET %s", url);
9114 
9115   cmd = (gchar **) g_malloc (5 * sizeof (gchar *));
9116   cmd[0] = g_strdup ("/usr/local/bin/wget");
9117   cmd[1] = g_strdup ("-O");
9118   cmd[2] = g_strdup ("-");
9119   cmd[3] = g_strdup (url);
9120   cmd[4] = NULL;
9121   g_debug ("%s: Spawning in /tmp/: %s %s %s %s",
9122            __func__, cmd[0], cmd[1], cmd[2], cmd[3]);
9123   if ((g_spawn_sync ("/tmp/",
9124                      cmd,
9125                      NULL,                  /* Environment. */
9126                      G_SPAWN_SEARCH_PATH,
9127                      NULL,                  /* Setup function. */
9128                      NULL,
9129                      &standard_out,
9130                      &standard_err,
9131                      &exit_status,
9132                      NULL)
9133        == FALSE)
9134       || (WIFEXITED (exit_status) == 0)
9135       || WEXITSTATUS (exit_status))
9136     {
9137       g_debug ("%s: wget failed: %d (WIF %i, WEX %i)",
9138                __func__,
9139                exit_status,
9140                WIFEXITED (exit_status),
9141                WEXITSTATUS (exit_status));
9142       g_debug ("%s: stdout: %s", __func__, standard_out);
9143       g_debug ("%s: stderr: %s", __func__, standard_err);
9144       ret = -1;
9145     }
9146   else
9147     {
9148       if (strlen (standard_out) > 80)
9149         standard_out[80] = '\0';
9150       g_debug ("   HTTP_GET %s: %s", url, standard_out);
9151       ret = 0;
9152     }
9153 
9154   g_free (cmd[0]);
9155   g_free (cmd[1]);
9156   g_free (cmd[2]);
9157   g_free (cmd[3]);
9158   g_free (cmd[4]);
9159   g_free (cmd);
9160   g_free (standard_out);
9161   g_free (standard_err);
9162   return ret;
9163 }
9164 
9165 /**
9166  * @brief Initialize common files and variables for an alert script.
9167  *
9168  * The temporary file / dir parameters will be modified by mkdtemp / mkstemp
9169  *  to contain the actual path.
9170  * The extra data is meant for data that should not be logged like passwords.
9171  *
9172  * @param[in]     report_filename Filename for the report or NULL for default.
9173  * @param[in]     report          Report that should be sent.
9174  * @param[in]     report_size     Size of the report.
9175  * @param[in]     extra_content   Optional extra data, e.g. credentials
9176  * @param[in]     extra_size      Optional extra data length
9177  * @param[in,out] report_dir      Template for temporary report directory
9178  * @param[out]    report_path Pointer to store path to report file at
9179  * @param[out]    error_path  Pointer to temporary file path for error messages
9180  * @param[out]    extra_path  Pointer to temporary extra data file path
9181  *
9182  * @return 0 success, -1 error.
9183  */
9184 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)9185 alert_script_init (const char *report_filename, const char* report,
9186                    size_t report_size,
9187                    const char *extra_content, size_t extra_size,
9188                    char *report_dir,
9189                    gchar **report_path, gchar **error_path, gchar **extra_path)
9190 {
9191   GError *error;
9192 
9193   /* Create temp directory */
9194 
9195   if (mkdtemp (report_dir) == NULL)
9196     {
9197       g_warning ("%s: mkdtemp failed", __func__);
9198       return -1;
9199     }
9200 
9201   /* Create report file */
9202 
9203   *report_path = g_strdup_printf ("%s/%s",
9204                                   report_dir,
9205                                   report_filename ? report_filename
9206                                                   : "report");
9207 
9208   error = NULL;
9209   g_file_set_contents (*report_path, report, report_size, &error);
9210   if (error)
9211     {
9212       g_warning ("%s: could not write report: %s",
9213                  __func__, error->message);
9214       g_error_free (error);
9215       g_free (*report_path);
9216       gvm_file_remove_recurse (report_dir);
9217       return -1;
9218     }
9219 
9220   /* Create error file */
9221 
9222   *error_path = g_strdup_printf ("%s/error_XXXXXX", report_dir);
9223 
9224   if (mkstemp (*error_path) == -1)
9225     {
9226       g_warning ("%s: mkstemp for error output failed", __func__);
9227       gvm_file_remove_recurse (report_dir);
9228       g_free (*report_path);
9229       g_free (*error_path);
9230       return -1;
9231     }
9232 
9233   /* Create extra data file */
9234 
9235   if (extra_content)
9236     {
9237       *extra_path = g_strdup_printf ("%s/extra_XXXXXX", report_dir);
9238       if (mkstemp (*extra_path) == -1)
9239         {
9240           g_warning ("%s: mkstemp for extra data failed", __func__);
9241           gvm_file_remove_recurse (report_dir);
9242           g_free (*report_path);
9243           g_free (*error_path);
9244           g_free (*extra_path);
9245           return -1;
9246         }
9247 
9248       error = NULL;
9249       g_file_set_contents (*extra_path, extra_content, extra_size, &error);
9250       if (error)
9251         {
9252           g_warning ("%s: could not write extra data: %s",
9253                     __func__, error->message);
9254           g_error_free (error);
9255           gvm_file_remove_recurse (report_dir);
9256           g_free (*report_path);
9257           g_free (*error_path);
9258           g_free (*extra_path);
9259           return -1;
9260         }
9261     }
9262   else
9263     *extra_path = NULL;
9264 
9265   return 0;
9266 }
9267 
9268 /**
9269  * @brief Execute the alert script.
9270  *
9271  * @param[in]  alert_id      UUID of the alert.
9272  * @param[in]  command_args  Args for the "alert" script.
9273  * @param[in]  report_path   Path to temporary file containing the report
9274  * @param[in]  report_dir    Temporary directory for the report
9275  * @param[in]  error_path    Path to the script error message file
9276  * @param[in]  extra_path    Path to the extra data file
9277  * @param[out] message       Custom error message generated by the script
9278  *
9279  * @return 0 success, -1 error, -5 alert script failed.
9280  */
9281 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)9282 alert_script_exec (const char *alert_id, const char *command_args,
9283                    const char *report_path, const char *report_dir,
9284                    const char *error_path, const char *extra_path,
9285                    gchar **message)
9286 {
9287   gchar *script, *script_dir;
9288 
9289   /* Setup script file name. */
9290   script_dir = g_build_filename (GVMD_DATA_DIR,
9291                                  "global_alert_methods",
9292                                  alert_id,
9293                                  NULL);
9294 
9295   script = g_build_filename (script_dir, "alert", NULL);
9296 
9297   if (!gvm_file_is_readable (script))
9298     {
9299       g_warning ("%s: Failed to find alert script: %s",
9300            __func__,
9301            script);
9302       g_free (script);
9303       g_free (script_dir);
9304       return -1;
9305     }
9306 
9307   /* Run the script */
9308   {
9309     gchar *command;
9310     char *previous_dir;
9311     int ret;
9312 
9313     /* Change into the script directory. */
9314 
9315     previous_dir = getcwd (NULL, 0);
9316     if (previous_dir == NULL)
9317       {
9318         g_warning ("%s: Failed to getcwd: %s",
9319                    __func__,
9320                    strerror (errno));
9321         g_free (previous_dir);
9322         g_free (script);
9323         g_free (script_dir);
9324         return -1;
9325       }
9326 
9327     if (chdir (script_dir))
9328       {
9329         g_warning ("%s: Failed to chdir: %s",
9330                    __func__,
9331                    strerror (errno));
9332         g_free (previous_dir);
9333         g_free (script);
9334         g_free (script_dir);
9335         return -1;
9336       }
9337     g_free (script_dir);
9338 
9339     /* Call the script. */
9340 
9341     if (extra_path)
9342       command = g_strdup_printf ("%s %s %s %s"
9343                                  " > /dev/null 2> %s",
9344                                  script,
9345                                  command_args,
9346                                  extra_path,
9347                                  report_path,
9348                                  error_path);
9349     else
9350       command = g_strdup_printf ("%s %s %s"
9351                                  " > /dev/null 2> %s",
9352                                  script,
9353                                  command_args,
9354                                  report_path,
9355                                  error_path);
9356     g_free (script);
9357 
9358     g_debug ("   command: %s", command);
9359 
9360     if (geteuid () == 0)
9361       {
9362         pid_t pid;
9363         struct passwd *nobody;
9364 
9365         /* Run the command with lower privileges in a fork. */
9366 
9367         nobody = getpwnam ("nobody");
9368         if ((nobody == NULL)
9369             || chown (report_dir, nobody->pw_uid, nobody->pw_gid)
9370             || chown (report_path, nobody->pw_uid, nobody->pw_gid)
9371             || chown (error_path, nobody->pw_uid, nobody->pw_gid)
9372             || (extra_path && chown (extra_path, nobody->pw_uid,
9373                                      nobody->pw_gid)))
9374           {
9375             g_warning ("%s: Failed to set permissions for user nobody: %s",
9376                        __func__,
9377                        strerror (errno));
9378             g_free (previous_dir);
9379             g_free (command);
9380             return -1;
9381           }
9382 
9383         pid = fork ();
9384         switch (pid)
9385           {
9386             case 0:
9387               {
9388                 /* Child.  Drop privileges, run command, exit. */
9389 
9390                 cleanup_manage_process (FALSE);
9391 
9392                 proctitle_set ("gvmd: Running alert script");
9393 
9394                 if (setgroups (0,NULL))
9395                   {
9396                     g_warning ("%s (child): setgroups: %s",
9397                                __func__, strerror (errno));
9398                     exit (EXIT_FAILURE);
9399                   }
9400                 if (setgid (nobody->pw_gid))
9401                   {
9402                     g_warning ("%s (child): setgid: %s",
9403                                __func__,
9404                                strerror (errno));
9405                     exit (EXIT_FAILURE);
9406                   }
9407                 if (setuid (nobody->pw_uid))
9408                   {
9409                     g_warning ("%s (child): setuid: %s",
9410                                __func__,
9411                                strerror (errno));
9412                     exit (EXIT_FAILURE);
9413                   }
9414 
9415                 ret = system (command);
9416                 /*
9417                  * Check shell command exit status, assuming 0 means success.
9418                  */
9419                 if (ret == -1)
9420                   {
9421                     g_warning ("%s (child):"
9422                                " system failed with ret %i, %i, %s",
9423                                __func__,
9424                                ret,
9425                                WEXITSTATUS (ret),
9426                                command);
9427                     exit (EXIT_FAILURE);
9428                   }
9429                 else if (ret != 0)
9430                   {
9431                     GError *error;
9432 
9433                     if (g_file_get_contents (error_path, message,
9434                                              NULL, &error) == FALSE)
9435                       {
9436                         g_warning ("%s: failed to test error message: %s",
9437                                     __func__, error->message);
9438                         g_error_free (error);
9439                         if (message)
9440                           g_free (*message);
9441                         exit (EXIT_FAILURE);
9442                       }
9443 
9444                     if (message == NULL)
9445                       exit (EXIT_FAILURE);
9446                     else if (*message == NULL || strcmp (*message, "") == 0)
9447                       {
9448                         g_free (*message);
9449                         *message
9450                           = g_strdup_printf ("Exited with code %d.",
9451                                               WEXITSTATUS (ret));
9452 
9453                         if (g_file_set_contents (error_path, *message,
9454                                                  strlen (*message),
9455                                                  &error) == FALSE)
9456                           {
9457                             g_warning ("%s: failed to write error message:"
9458                                         " %s",
9459                                         __func__, error->message);
9460                             g_error_free (error);
9461                             g_free (*message);
9462                             exit (EXIT_FAILURE);
9463                           }
9464                       }
9465 
9466                     g_free (*message);
9467                     exit (2);
9468                   }
9469 
9470                 exit (EXIT_SUCCESS);
9471               }
9472 
9473             case -1:
9474               /* Parent when error. */
9475 
9476               g_warning ("%s: Failed to fork: %s",
9477                          __func__,
9478                          strerror (errno));
9479               if (chdir (previous_dir))
9480                 g_warning ("%s: and chdir failed",
9481                            __func__);
9482               g_free (previous_dir);
9483               g_free (command);
9484               return -1;
9485               break;
9486 
9487             default:
9488               {
9489                 int status;
9490 
9491                 /* Parent on success.  Wait for child, and check result. */
9492 
9493                 while (waitpid (pid, &status, 0) < 0)
9494                   {
9495                     if (errno == ECHILD)
9496                       {
9497                         g_warning ("%s: Failed to get child exit status",
9498                                    __func__);
9499                         if (chdir (previous_dir))
9500                           g_warning ("%s: and chdir failed",
9501                                      __func__);
9502                         g_free (previous_dir);
9503                         return -1;
9504                       }
9505                     if (errno == EINTR)
9506                       continue;
9507                     g_warning ("%s: wait: %s",
9508                                __func__,
9509                                strerror (errno));
9510                     if (chdir (previous_dir))
9511                       g_warning ("%s: and chdir failed",
9512                                  __func__);
9513                     g_free (previous_dir);
9514                     return -1;
9515                   }
9516                 if (WIFEXITED (status))
9517                   switch (WEXITSTATUS (status))
9518                     {
9519                     case EXIT_SUCCESS:
9520                       break;
9521                     case 2: // script failed
9522                       if (message)
9523                         {
9524                           GError *error = NULL;
9525                           if (g_file_get_contents (error_path, message,
9526                                                    NULL, &error) == FALSE)
9527                             {
9528                               g_warning ("%s: failed to get error message: %s",
9529                                          __func__, error->message);
9530                               g_error_free (error);
9531                             }
9532 
9533                           if (strcmp (*message, "") == 0)
9534                             {
9535                               g_free (*message);
9536                               *message = NULL;
9537                             }
9538                         }
9539                       if (chdir (previous_dir))
9540                         g_warning ("%s: chdir failed",
9541                                    __func__);
9542                       return -5;
9543                     case EXIT_FAILURE:
9544                     default:
9545                       g_warning ("%s: child failed, %s",
9546                                  __func__,
9547                                  command);
9548                       if (chdir (previous_dir))
9549                         g_warning ("%s: and chdir failed",
9550                                    __func__);
9551                       g_free (previous_dir);
9552                       return -1;
9553                     }
9554                 else
9555                   {
9556                     g_warning ("%s: child failed, %s",
9557                                __func__,
9558                                command);
9559                     if (chdir (previous_dir))
9560                       g_warning ("%s: and chdir failed",
9561                                  __func__);
9562                     g_free (previous_dir);
9563                     return -1;
9564                   }
9565 
9566                 /* Child succeeded, continue to process result. */
9567 
9568                 break;
9569               }
9570           }
9571       }
9572     else
9573       {
9574         /* Just run the command as the current user. */
9575 
9576         ret = system (command);
9577         /* Ignore the shell command exit status, because we've not
9578          * specified what it must be in the past. */
9579         if (ret == -1)
9580           {
9581             g_warning ("%s: system failed with ret %i, %i, %s",
9582                        __func__,
9583                        ret,
9584                        WEXITSTATUS (ret),
9585                        command);
9586             if (chdir (previous_dir))
9587               g_warning ("%s: and chdir failed",
9588                          __func__);
9589             g_free (previous_dir);
9590             g_free (command);
9591             return -1;
9592           }
9593         else if (ret)
9594           {
9595             if (message)
9596               {
9597                 GError *error = NULL;
9598                 if (g_file_get_contents (error_path, message, NULL, &error)
9599                       == FALSE)
9600                   {
9601                     g_warning ("%s: failed to get error message: %s",
9602                                __func__, error->message);
9603                     g_error_free (error);
9604                   }
9605 
9606                 if (strcmp (*message, "") == 0)
9607                   {
9608                     g_free (*message);
9609                     *message = NULL;
9610                   }
9611 
9612                 if (*message == NULL)
9613                   {
9614                     *message
9615                       = g_strdup_printf ("Exited with code %d.",
9616                                          WEXITSTATUS (ret));
9617                   }
9618               }
9619             g_free (previous_dir);
9620             g_free (command);
9621             return -5;
9622           }
9623       }
9624 
9625     g_free (command);
9626 
9627     /* Change back to the previous directory. */
9628 
9629     if (chdir (previous_dir))
9630       {
9631         g_warning ("%s: Failed to chdir back: %s",
9632                    __func__,
9633                    strerror (errno));
9634         g_free (previous_dir);
9635         return -1;
9636       }
9637     g_free (previous_dir);
9638   }
9639   return 0;
9640 }
9641 
9642 /**
9643  * @brief Write data to a file for use by an alert script.
9644  *
9645  * @param[in]  directory      Base directory to create the file in
9646  * @param[in]  filename       Filename without directory
9647  * @param[in]  content        The file content
9648  * @param[in]  content_size   Size of the file content
9649  * @param[in]  description    Short file description for error messages
9650  * @param[out] file_path      Return location of combined file path
9651  *
9652  * @return 0 success, -1 error
9653  */
9654 static int
alert_write_data_file(const char * directory,const char * filename,const char * content,gsize content_size,const char * description,gchar ** file_path)9655 alert_write_data_file (const char *directory, const char *filename,
9656                        const char *content, gsize content_size,
9657                        const char *description, gchar **file_path)
9658 {
9659   gchar *path;
9660   GError *error;
9661 
9662   if (file_path)
9663     *file_path = NULL;
9664 
9665   /* Setup extra data file */
9666   path = g_build_filename (directory, filename, NULL);
9667   error = NULL;
9668   if (g_file_set_contents (path, content, content_size, &error) == FALSE)
9669     {
9670       g_warning ("%s: Failed to write %s to file: %s",
9671                  __func__,
9672                  description ? description : "extra data",
9673                  error->message);
9674       g_free (path);
9675       return -1;
9676     }
9677 
9678   if (geteuid () == 0)
9679     {
9680       struct passwd *nobody;
9681 
9682       /* Set the owner for the extra data file like the other
9683        * files handled by alert_script_exec, to be able to
9684        * run the command with lower privileges in a fork. */
9685 
9686       nobody = getpwnam ("nobody");
9687       if ((nobody == NULL)
9688           || chown (path, nobody->pw_uid, nobody->pw_gid))
9689         {
9690           g_warning ("%s: Failed to set permissions for user nobody: %s",
9691                       __func__,
9692                       strerror (errno));
9693           g_free (path);
9694           return -1;
9695         }
9696     }
9697 
9698   if (file_path)
9699     *file_path = path;
9700 
9701   return 0;
9702 }
9703 
9704 /**
9705  * @brief Clean up common files and variables for running alert script.
9706  *
9707  * @param[in]  report_dir   The temporary directory.
9708  * @param[in]  report_path  The temporary report file path to free.
9709  * @param[in]  error_path   The temporary error file path to free.
9710  * @param[in]  extra_path   The temporary extra data file path to free.
9711  *
9712  * @return 0 success, -1 error.
9713  */
9714 static int
alert_script_cleanup(const char * report_dir,gchar * report_path,gchar * error_path,gchar * extra_path)9715 alert_script_cleanup (const char *report_dir,
9716                       gchar *report_path, gchar *error_path, gchar *extra_path)
9717 {
9718   gvm_file_remove_recurse (report_dir);
9719   g_free (report_path);
9720   g_free (error_path);
9721   g_free (extra_path);
9722   return 0;
9723 }
9724 
9725 /**
9726  * @brief Run an alert's "alert" script with one file of extra data.
9727  *
9728  * @param[in]  alert_id         ID of alert.
9729  * @param[in]  command_args     Args for the "alert" script.
9730  * @param[in]  report_filename  Optional report file name, default: "report"
9731  * @param[in]  report           Report that should be sent.
9732  * @param[in]  report_size      Size of the report.
9733  * @param[in]  extra_content    Optional extra data like passwords
9734  * @param[in]  extra_size       Size of the report.
9735  * @param[out] message          Custom error message of the script.
9736  *
9737  * @return 0 success, -1 error, -5 alert script failed.
9738  */
9739 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)9740 run_alert_script (const char *alert_id, const char *command_args,
9741                   const char *report_filename, const char *report,
9742                   size_t report_size,
9743                   const char *extra_content, size_t extra_size,
9744                   gchar **message)
9745 {
9746   char report_dir[] = "/tmp/gvmd_alert_XXXXXX";
9747   gchar *report_path, *error_path, *extra_path;
9748   int ret;
9749 
9750   if (message)
9751     *message = NULL;
9752 
9753   if (report == NULL)
9754     return -1;
9755 
9756   g_debug ("report: %s", report);
9757 
9758   /* Setup files. */
9759   ret = alert_script_init (report_filename, report, report_size,
9760                            extra_content, extra_size,
9761                            report_dir,
9762                            &report_path, &error_path, &extra_path);
9763   if (ret)
9764     return ret;
9765 
9766   /* Run the script */
9767   ret = alert_script_exec (alert_id, command_args, report_path, report_dir,
9768                            error_path, extra_path, message);
9769   if (ret)
9770     {
9771       alert_script_cleanup (report_dir, report_path, error_path, extra_path);
9772       return ret;
9773     }
9774 
9775   /* Remove the directory. */
9776   ret = alert_script_cleanup (report_dir, report_path, error_path, extra_path);
9777 
9778   return ret;
9779 }
9780 
9781 /**
9782  * @brief Send an SNMP TRAP to a host.
9783  *
9784  * @param[in]  community  Community.
9785  * @param[in]  agent      Agent.
9786  * @param[in]  message    Message.
9787  * @param[out] script_message  Custom error message of the script.
9788  *
9789  * @return 0 success, -1 error, -5 alert script failed.
9790  */
9791 static int
snmp_to_host(const char * community,const char * agent,const char * message,gchar ** script_message)9792 snmp_to_host (const char *community, const char *agent, const char *message,
9793               gchar **script_message)
9794 {
9795   gchar *clean_community, *clean_agent, *clean_message, *command_args;
9796   int ret;
9797 
9798   g_debug ("SNMP to host: %s", agent);
9799 
9800   if (community == NULL || agent == NULL || message == NULL)
9801     {
9802       g_warning ("%s: parameter was NULL", __func__);
9803       return -1;
9804     }
9805 
9806   clean_community = g_shell_quote (community);
9807   clean_agent = g_shell_quote (agent);
9808   clean_message = g_shell_quote (message);
9809   command_args = g_strdup_printf ("%s %s %s", clean_community, clean_agent,
9810                                   clean_message);
9811   g_free (clean_community);
9812   g_free (clean_agent);
9813   g_free (clean_message);
9814 
9815   ret = run_alert_script ("9d435134-15d3-11e6-bf5c-28d24461215b", command_args,
9816                           "report", "", 0, NULL, 0, script_message);
9817 
9818   g_free (command_args);
9819   return ret;
9820 }
9821 
9822 /**
9823  * @brief Send a report to a host via TCP.
9824  *
9825  * @param[in]  host         Address of host.
9826  * @param[in]  port         Port of host.
9827  * @param[in]  report      Report that should be sent.
9828  * @param[in]  report_size Size of the report.
9829  * @param[out] script_message  Custom error message of the script.
9830  *
9831  * @return 0 success, -1 error, -5 alert script failed.
9832  */
9833 static int
send_to_host(const char * host,const char * port,const char * report,int report_size,gchar ** script_message)9834 send_to_host (const char *host, const char *port,
9835               const char *report, int report_size,
9836               gchar **script_message)
9837 {
9838   gchar *clean_host, *clean_port, *command_args;
9839   int ret;
9840 
9841   g_debug ("send to host: %s:%s", host, port);
9842 
9843   if (host == NULL)
9844     return -1;
9845 
9846   clean_host = g_shell_quote (host);
9847   clean_port = g_shell_quote (port);
9848   command_args = g_strdup_printf ("%s %s", clean_host, clean_port);
9849   g_free (clean_host);
9850   g_free (clean_port);
9851 
9852   ret = run_alert_script ("4a398d42-87c0-11e5-a1c0-28d24461215b", command_args,
9853                           "report", report, report_size, NULL, 0,
9854                           script_message);
9855 
9856   g_free (command_args);
9857   return ret;
9858 }
9859 
9860 /**
9861  * @brief Send a report to a host via TCP.
9862  *
9863  * @param[in]  username     Username.
9864  * @param[in]  password     Password or passphrase of private key.
9865  * @param[in]  private_key  Private key or NULL for password-only auth.
9866  * @param[in]  host         Address of host.
9867  * @param[in]  path         Destination filename with path.
9868  * @param[in]  known_hosts  Content for known_hosts file.
9869  * @param[in]  report       Report that should be sent.
9870  * @param[in]  report_size  Size of the report.
9871  * @param[out] script_message  Custom error message of the alert script.
9872  *
9873  * @return 0 success, -1 error, -5 alert script failed.
9874  */
9875 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)9876 scp_to_host (const char *username, const char *password,
9877              const char *private_key,
9878              const char *host, const char *path, const char *known_hosts,
9879              const char *report, int report_size, gchar **script_message)
9880 {
9881   const char *alert_id = "2db07698-ec49-11e5-bcff-28d24461215b";
9882   char report_dir[] = "/tmp/gvmd_alert_XXXXXX";
9883   gchar *report_path, *error_path, *password_path, *private_key_path;
9884   gchar *clean_username, *clean_host, *clean_path, *clean_private_key_path;
9885   gchar *clean_known_hosts, *command_args;
9886   int ret;
9887 
9888   g_debug ("scp to host: %s@%s:%s", username, host, path);
9889 
9890   if (password == NULL || username == NULL || host == NULL || path == NULL)
9891     return -1;
9892 
9893   if (known_hosts == NULL)
9894     known_hosts = "";
9895 
9896   /* Setup files, including password but not private key */
9897   ret = alert_script_init ("report", report, report_size,
9898                            password, strlen (password),
9899                            report_dir,
9900                            &report_path, &error_path, &password_path);
9901   if (ret)
9902     return -1;
9903 
9904   if (private_key)
9905     {
9906       /* Setup private key here because alert_script_init and alert_script_exec
9907        *  only handle one extra file. */
9908       if (alert_write_data_file (report_dir, "private_key",
9909                                  private_key, strlen (private_key),
9910                                  "private key", &private_key_path))
9911         {
9912           alert_script_cleanup (report_dir, report_path, error_path,
9913                                 password_path);
9914           g_free (private_key_path);
9915           return -1;
9916         }
9917     }
9918   else
9919     private_key_path = g_strdup ("");
9920 
9921   /* Create arguments */
9922   clean_username = g_shell_quote (username);
9923   clean_host = g_shell_quote (host);
9924   clean_path = g_shell_quote (path);
9925   clean_known_hosts = g_shell_quote (known_hosts);
9926   clean_private_key_path = g_shell_quote (private_key_path);
9927   command_args = g_strdup_printf ("%s %s %s %s %s",
9928                                   clean_username,
9929                                   clean_host,
9930                                   clean_path,
9931                                   clean_known_hosts,
9932                                   clean_private_key_path);
9933   g_free (clean_username);
9934   g_free (clean_host);
9935   g_free (clean_path);
9936   g_free (clean_known_hosts);
9937   g_free (clean_private_key_path);
9938 
9939   /* Run script */
9940   ret = alert_script_exec (alert_id, command_args, report_path, report_dir,
9941                            error_path, password_path, script_message);
9942   g_free (command_args);
9943   if (ret)
9944     {
9945       alert_script_cleanup (report_dir, report_path, error_path,
9946                             password_path);
9947       g_free (private_key_path);
9948       return ret;
9949     }
9950 
9951   /* Remove the directory and free path strings. */
9952   ret = alert_script_cleanup (report_dir, report_path, error_path,
9953                               password_path);
9954   g_free (private_key_path);
9955   return ret;
9956 }
9957 
9958 /**
9959  * @brief Send a report to a host via SMB.
9960  *
9961  * @param[in]  password       Password.
9962  * @param[in]  username       Username.
9963  * @param[in]  share_path     Name/address of host and name of the share.
9964  * @param[in]  file_path      Destination filename with path inside the share.
9965  * @param[in]  report         Report that should be sent.
9966  * @param[in]  report_size    Size of the report.
9967  * @param[out] script_message Custom error message of the alert script.
9968  *
9969  * @return 0 success, -1 error, -5 alert script failed.
9970  */
9971 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)9972 smb_send_to_host (const char *password, const char *username,
9973                   const char *share_path, const char *file_path,
9974                   const char *report, gsize report_size,
9975                   gchar **script_message)
9976 {
9977   gchar *clean_share_path, *clean_file_path;
9978   gchar *authfile_content;
9979   gchar *command_args;
9980   int ret;
9981 
9982   g_debug ("smb as %s to share: %s, path: %s", username, share_path, file_path);
9983 
9984   if (password == NULL || username == NULL
9985       || share_path == NULL || file_path == NULL)
9986     return -1;
9987 
9988   clean_share_path = g_shell_quote (share_path);
9989   clean_file_path = g_shell_quote (file_path);
9990   authfile_content = g_strdup_printf ("username = %s\n"
9991                                       "password = %s\n",
9992                                       username, password);
9993   command_args = g_strdup_printf ("%s %s",
9994                                   clean_share_path, clean_file_path);
9995   g_free (clean_share_path);
9996   g_free (clean_file_path);
9997 
9998   ret = run_alert_script ("c427a688-b653-40ab-a9d0-d6ba842a9d63", command_args,
9999                           "report", report, report_size,
10000                           authfile_content, strlen (authfile_content),
10001                           script_message);
10002 
10003   g_free (authfile_content);
10004   g_free (command_args);
10005   return ret;
10006 }
10007 
10008 /**
10009  * @brief Send a report to a Sourcefire Defense Center.
10010  *
10011  * @param[in]  ip               IP of center.
10012  * @param[in]  port             Port of center.
10013  * @param[in]  pkcs12_64        PKCS12 content in base64.
10014  * @param[in]  pkcs12_password  Password for encrypted PKCS12.
10015  * @param[in]  report           Report in "Sourcefire" format.
10016  *
10017  * @return 0 success, -1 error.
10018  */
10019 static int
send_to_sourcefire(const char * ip,const char * port,const char * pkcs12_64,const char * pkcs12_password,const char * report)10020 send_to_sourcefire (const char *ip, const char *port, const char *pkcs12_64,
10021                     const char *pkcs12_password, const char *report)
10022 {
10023   gchar *script, *script_dir;
10024   gchar *report_file, *pkcs12_file, *pkcs12, *clean_password;
10025   gchar *clean_ip, *clean_port;
10026   char report_dir[] = "/tmp/gvmd_escalate_XXXXXX";
10027   GError *error;
10028   gsize pkcs12_len;
10029 
10030   if ((report == NULL) || (ip == NULL) || (port == NULL))
10031     return -1;
10032 
10033   g_debug ("send to sourcefire: %s:%s", ip, port);
10034   g_debug ("report: %s", report);
10035 
10036   /* Setup files. */
10037 
10038   if (mkdtemp (report_dir) == NULL)
10039     {
10040       g_warning ("%s: mkdtemp failed", __func__);
10041       return -1;
10042     }
10043 
10044   report_file = g_strdup_printf ("%s/report.csv", report_dir);
10045 
10046   error = NULL;
10047   g_file_set_contents (report_file, report, strlen (report), &error);
10048   if (error)
10049     {
10050       g_warning ("%s", error->message);
10051       g_error_free (error);
10052       g_free (report_file);
10053       return -1;
10054     }
10055 
10056   pkcs12_file = g_strdup_printf ("%s/pkcs12", report_dir);
10057 
10058   if (strlen (pkcs12_64))
10059     pkcs12 = (gchar*) g_base64_decode (pkcs12_64, &pkcs12_len);
10060   else
10061     {
10062       pkcs12 = g_strdup ("");
10063       pkcs12_len = 0;
10064     }
10065 
10066   error = NULL;
10067   g_file_set_contents (pkcs12_file, pkcs12, pkcs12_len, &error);
10068   if (error)
10069     {
10070       g_warning ("%s", error->message);
10071       g_error_free (error);
10072       g_free (report_file);
10073       g_free (pkcs12_file);
10074       return -1;
10075     }
10076 
10077   clean_password = g_shell_quote (pkcs12_password ? pkcs12_password : "");
10078 
10079   /* Setup file names. */
10080 
10081   script_dir = g_build_filename (GVMD_DATA_DIR,
10082                                  "global_alert_methods",
10083                                  "cd1f5a34-6bdc-11e0-9827-002264764cea",
10084                                  NULL);
10085 
10086   script = g_build_filename (script_dir, "alert", NULL);
10087 
10088   if (!gvm_file_is_readable (script))
10089     {
10090       g_free (report_file);
10091       g_free (pkcs12_file);
10092       g_free (clean_password);
10093       g_free (script);
10094       g_free (script_dir);
10095       return -1;
10096     }
10097 
10098   {
10099     gchar *command;
10100     char *previous_dir;
10101     int ret;
10102 
10103     /* Change into the script directory. */
10104 
10105     previous_dir = getcwd (NULL, 0);
10106     if (previous_dir == NULL)
10107       {
10108         g_warning ("%s: Failed to getcwd: %s",
10109                    __func__,
10110                    strerror (errno));
10111         g_free (report_file);
10112         g_free (pkcs12_file);
10113         g_free (clean_password);
10114         g_free (previous_dir);
10115         g_free (script);
10116         g_free (script_dir);
10117         return -1;
10118       }
10119 
10120     if (chdir (script_dir))
10121       {
10122         g_warning ("%s: Failed to chdir: %s",
10123                    __func__,
10124                    strerror (errno));
10125         g_free (report_file);
10126         g_free (pkcs12_file);
10127         g_free (clean_password);
10128         g_free (previous_dir);
10129         g_free (script);
10130         g_free (script_dir);
10131         return -1;
10132       }
10133     g_free (script_dir);
10134 
10135     /* Call the script. */
10136 
10137     clean_ip = g_shell_quote (ip);
10138     clean_port = g_shell_quote (port);
10139 
10140     command = g_strdup_printf ("%s %s %s %s %s %s > /dev/null"
10141                                " 2> /dev/null",
10142                                script,
10143                                clean_ip,
10144                                clean_port,
10145                                pkcs12_file,
10146                                report_file,
10147                                clean_password);
10148     g_free (script);
10149     g_free (clean_ip);
10150     g_free (clean_port);
10151     g_free (clean_password);
10152 
10153     g_debug ("   command: %s", command);
10154 
10155     if (geteuid () == 0)
10156       {
10157         pid_t pid;
10158         struct passwd *nobody;
10159 
10160         /* Run the command with lower privileges in a fork. */
10161 
10162         nobody = getpwnam ("nobody");
10163         if ((nobody == NULL)
10164             || chown (report_dir, nobody->pw_uid, nobody->pw_gid)
10165             || chown (report_file, nobody->pw_uid, nobody->pw_gid)
10166             || chown (pkcs12_file, nobody->pw_uid, nobody->pw_gid))
10167           {
10168             g_warning ("%s: Failed to set permissions for user nobody: %s",
10169                        __func__,
10170                        strerror (errno));
10171             g_free (report_file);
10172             g_free (pkcs12_file);
10173             g_free (previous_dir);
10174             return -1;
10175           }
10176         g_free (report_file);
10177         g_free (pkcs12_file);
10178 
10179         pid = fork ();
10180         switch (pid)
10181           {
10182           case 0:
10183               {
10184                 /* Child.  Drop privileges, run command, exit. */
10185                 cleanup_manage_process (FALSE);
10186 
10187                 proctitle_set ("gvmd: Sending to Sourcefire");
10188 
10189                 if (setgroups (0,NULL))
10190                   {
10191                     g_warning ("%s (child): setgroups: %s",
10192                                __func__, strerror (errno));
10193                     exit (EXIT_FAILURE);
10194                   }
10195                 if (setgid (nobody->pw_gid))
10196                   {
10197                     g_warning ("%s (child): setgid: %s",
10198                                __func__,
10199                                strerror (errno));
10200                     exit (EXIT_FAILURE);
10201                   }
10202                 if (setuid (nobody->pw_uid))
10203                   {
10204                     g_warning ("%s (child): setuid: %s",
10205                                __func__,
10206                                strerror (errno));
10207                     exit (EXIT_FAILURE);
10208                   }
10209 
10210                 ret = system (command);
10211                 /* Ignore the shell command exit status, because we've not
10212                  * specified what it must be in the past. */
10213                 if (ret == -1)
10214                   {
10215                     g_warning ("%s (child):"
10216                                " system failed with ret %i, %i, %s",
10217                                __func__,
10218                                ret,
10219                                WEXITSTATUS (ret),
10220                                command);
10221                     exit (EXIT_FAILURE);
10222                   }
10223 
10224                 exit (EXIT_SUCCESS);
10225               }
10226 
10227           case -1:
10228             /* Parent when error. */
10229 
10230             g_warning ("%s: Failed to fork: %s",
10231                        __func__,
10232                        strerror (errno));
10233             if (chdir (previous_dir))
10234               g_warning ("%s: and chdir failed",
10235                          __func__);
10236             g_free (previous_dir);
10237             g_free (command);
10238             return -1;
10239             break;
10240 
10241           default:
10242               {
10243                 int status;
10244 
10245                 /* Parent on success.  Wait for child, and check result. */
10246 
10247 
10248                 while (waitpid (pid, &status, 0) < 0)
10249                   {
10250                     if (errno == ECHILD)
10251                       {
10252                         g_warning ("%s: Failed to get child exit status",
10253                                    __func__);
10254                         if (chdir (previous_dir))
10255                           g_warning ("%s: and chdir failed",
10256                                      __func__);
10257                         g_free (previous_dir);
10258                         return -1;
10259                       }
10260                     if (errno == EINTR)
10261                       continue;
10262                     g_warning ("%s: wait: %s",
10263                                __func__,
10264                                strerror (errno));
10265                     if (chdir (previous_dir))
10266                       g_warning ("%s: and chdir failed",
10267                                  __func__);
10268                     g_free (previous_dir);
10269                     g_free (command);
10270                     return -1;
10271                   }
10272                 if (WIFEXITED (status))
10273                   switch (WEXITSTATUS (status))
10274                     {
10275                     case EXIT_SUCCESS:
10276                       break;
10277                     case EXIT_FAILURE:
10278                     default:
10279                       g_warning ("%s: child failed, %s",
10280                                  __func__,
10281                                  command);
10282                       if (chdir (previous_dir))
10283                         g_warning ("%s: and chdir failed",
10284                                    __func__);
10285                       g_free (previous_dir);
10286                       g_free (command);
10287                       return -1;
10288                     }
10289                 else
10290                   {
10291                     g_warning ("%s: child failed, %s",
10292                                __func__,
10293                                command);
10294                     if (chdir (previous_dir))
10295                       g_warning ("%s: and chdir failed",
10296                                  __func__);
10297                     g_free (previous_dir);
10298                     g_free (command);
10299                     return -1;
10300                   }
10301 
10302                 /* Child succeeded, continue to process result. */
10303                 g_free (command);
10304                 break;
10305               }
10306           }
10307       }
10308     else
10309       {
10310         /* Just run the command as the current user. */
10311         g_free (report_file);
10312         g_free (pkcs12_file);
10313 
10314         ret = system (command);
10315         /* Ignore the shell command exit status, because we've not
10316          * specified what it must be in the past. */
10317         if (ret == -1)
10318           {
10319             g_warning ("%s: system failed with ret %i, %i, %s",
10320                        __func__,
10321                        ret,
10322                        WEXITSTATUS (ret),
10323                        command);
10324             if (chdir (previous_dir))
10325               g_warning ("%s: and chdir failed",
10326                          __func__);
10327             g_free (previous_dir);
10328             g_free (command);
10329             return -1;
10330           }
10331 
10332         g_free (command);
10333       }
10334 
10335     /* Change back to the previous directory. */
10336 
10337     if (chdir (previous_dir))
10338       {
10339         g_warning ("%s: Failed to chdir back: %s",
10340                    __func__,
10341                    strerror (errno));
10342         g_free (previous_dir);
10343         return -1;
10344       }
10345     g_free (previous_dir);
10346 
10347     /* Remove the directory. */
10348 
10349     gvm_file_remove_recurse (report_dir);
10350 
10351     return 0;
10352   }
10353 }
10354 
10355 /**
10356  * @brief Send a report to a verinice.PRO server.
10357  *
10358  * @param[in]  url          URL of the server.
10359  * @param[in]  username     Username for server access.
10360  * @param[in]  password     Password for server access.
10361  * @param[in]  archive      Verinice archive that should be sent.
10362  * @param[in]  archive_size Size of the verinice archive
10363  *
10364  * @return 0 success, -1 error.
10365  */
10366 static int
send_to_verinice(const char * url,const char * username,const char * password,const char * archive,int archive_size)10367 send_to_verinice (const char *url, const char *username, const char *password,
10368                   const char *archive, int archive_size)
10369 {
10370   gchar *script, *script_dir;
10371   gchar *archive_file;
10372   gchar *clean_url, *clean_username, *clean_password;
10373   char archive_dir[] = "/tmp/gvmd_alert_XXXXXX";
10374   GError *error;
10375 
10376   if ((archive == NULL) || (url == NULL))
10377     return -1;
10378 
10379   g_debug ("send to verinice: %s", url);
10380   g_debug ("archive: %s", archive);
10381 
10382   /* Setup files. */
10383 
10384   if (mkdtemp (archive_dir) == NULL)
10385     {
10386       g_warning ("%s: mkdtemp failed", __func__);
10387       return -1;
10388     }
10389 
10390   archive_file = g_strdup_printf ("%s/archive.vna", archive_dir);
10391 
10392   error = NULL;
10393   g_file_set_contents (archive_file, archive, archive_size, &error);
10394   if (error)
10395     {
10396       g_warning ("%s", error->message);
10397       g_error_free (error);
10398       g_free (archive_file);
10399       return -1;
10400     }
10401 
10402   /* Setup file names. */
10403   script_dir = g_build_filename (GVMD_DATA_DIR,
10404                                  "global_alert_methods",
10405                                  "f9d97653-f89b-41af-9ba1-0f6ee00e9c1a",
10406                                  NULL);
10407 
10408   script = g_build_filename (script_dir, "alert", NULL);
10409 
10410   if (!gvm_file_is_readable (script))
10411     {
10412       g_warning ("%s: Failed to find alert script: %s",
10413            __func__,
10414            script);
10415       g_free (archive_file);
10416       g_free (script);
10417       g_free (script_dir);
10418       return -1;
10419     }
10420 
10421   {
10422     gchar *command;
10423     gchar *log_command; /* Command with password removed. */
10424     char *previous_dir;
10425     int ret;
10426 
10427     /* Change into the script directory. */
10428 
10429     previous_dir = getcwd (NULL, 0);
10430     if (previous_dir == NULL)
10431       {
10432         g_warning ("%s: Failed to getcwd: %s",
10433                    __func__,
10434                    strerror (errno));
10435         g_free (archive_file);
10436         g_free (previous_dir);
10437         g_free (script);
10438         g_free (script_dir);
10439         return -1;
10440       }
10441 
10442     if (chdir (script_dir))
10443       {
10444         g_warning ("%s: Failed to chdir: %s",
10445                    __func__,
10446                    strerror (errno));
10447         g_free (archive_file);
10448         g_free (previous_dir);
10449         g_free (script);
10450         g_free (script_dir);
10451         return -1;
10452       }
10453     g_free (script_dir);
10454 
10455     /* Call the script. */
10456 
10457     clean_url = g_shell_quote (url);
10458     clean_username = g_shell_quote (username);
10459     clean_password = g_shell_quote (password);
10460 
10461     command = g_strdup_printf ("%s %s %s %s %s > /dev/null"
10462                                " 2> /dev/null",
10463                                script,
10464                                clean_url,
10465                                clean_username,
10466                                clean_password,
10467                                archive_file);
10468     log_command = g_strdup_printf ("%s %s %s ****** %s > /dev/null"
10469                                    " 2> /dev/null",
10470                                    script,
10471                                    clean_url,
10472                                    clean_username,
10473                                    archive_file);
10474     g_free (script);
10475     g_free (clean_url);
10476     g_free (clean_username);
10477     g_free (clean_password);
10478 
10479     g_debug ("   command: %s", log_command);
10480 
10481     if (geteuid () == 0)
10482       {
10483         pid_t pid;
10484         struct passwd *nobody;
10485 
10486         /* Run the command with lower privileges in a fork. */
10487 
10488         nobody = getpwnam ("nobody");
10489         if ((nobody == NULL)
10490             || chown (archive_dir, nobody->pw_uid, nobody->pw_gid)
10491             || chown (archive_file, nobody->pw_uid, nobody->pw_gid))
10492           {
10493             g_warning ("%s: Failed to set permissions for user nobody: %s",
10494                        __func__,
10495                        strerror (errno));
10496             g_free (previous_dir);
10497             g_free (archive_file);
10498             g_free (command);
10499             g_free (log_command);
10500             return -1;
10501           }
10502         g_free (archive_file);
10503 
10504         pid = fork ();
10505         switch (pid)
10506           {
10507           case 0:
10508               {
10509                 /* Child.  Drop privileges, run command, exit. */
10510 
10511                 proctitle_set ("gvmd: Sending to Verinice");
10512 
10513                 cleanup_manage_process (FALSE);
10514 
10515                 if (setgroups (0,NULL))
10516                   {
10517                     g_warning ("%s (child): setgroups: %s",
10518                                __func__, strerror (errno));
10519                     exit (EXIT_FAILURE);
10520                   }
10521                 if (setgid (nobody->pw_gid))
10522                   {
10523                     g_warning ("%s (child): setgid: %s",
10524                                __func__,
10525                                strerror (errno));
10526                     exit (EXIT_FAILURE);
10527                   }
10528                 if (setuid (nobody->pw_uid))
10529                   {
10530                     g_warning ("%s (child): setuid: %s",
10531                                __func__,
10532                                strerror (errno));
10533                     exit (EXIT_FAILURE);
10534                   }
10535 
10536                 ret = system (command);
10537                 /* Ignore the shell command exit status, because we've not
10538                  * specified what it must be in the past. */
10539                 if (ret == -1)
10540                   {
10541                     g_warning ("%s (child):"
10542                                " system failed with ret %i, %i, %s",
10543                                __func__,
10544                                ret,
10545                                WEXITSTATUS (ret),
10546                                log_command);
10547                     exit (EXIT_FAILURE);
10548                   }
10549 
10550                 exit (EXIT_SUCCESS);
10551               }
10552 
10553           case -1:
10554             /* Parent when error. */
10555 
10556             g_warning ("%s: Failed to fork: %s",
10557                        __func__,
10558                        strerror (errno));
10559             if (chdir (previous_dir))
10560               g_warning ("%s: and chdir failed",
10561                          __func__);
10562             g_free (previous_dir);
10563             g_free (command);
10564             g_free (log_command);
10565             return -1;
10566             break;
10567 
10568           default:
10569               {
10570                 int status;
10571 
10572                 /* Parent on success.  Wait for child, and check result. */
10573 
10574                 while (waitpid (pid, &status, 0) < 0)
10575                   {
10576                     if (errno == ECHILD)
10577                       {
10578                         g_warning ("%s: Failed to get child exit status",
10579                                    __func__);
10580                         if (chdir (previous_dir))
10581                           g_warning ("%s: and chdir failed",
10582                                      __func__);
10583                         g_free (previous_dir);
10584                         return -1;
10585                       }
10586                     if (errno == EINTR)
10587                       continue;
10588                     g_warning ("%s: wait: %s",
10589                                __func__,
10590                                strerror (errno));
10591                     if (chdir (previous_dir))
10592                       g_warning ("%s: and chdir failed",
10593                                  __func__);
10594                     g_free (previous_dir);
10595                     return -1;
10596                   }
10597                 if (WIFEXITED (status))
10598                   switch (WEXITSTATUS (status))
10599                     {
10600                     case EXIT_SUCCESS:
10601                       break;
10602                     case EXIT_FAILURE:
10603                     default:
10604                       g_warning ("%s: child failed, %s",
10605                                  __func__,
10606                                  log_command);
10607                       if (chdir (previous_dir))
10608                         g_warning ("%s: and chdir failed",
10609                                    __func__);
10610                       g_free (previous_dir);
10611                       return -1;
10612                     }
10613                 else
10614                   {
10615                     g_warning ("%s: child failed, %s",
10616                                __func__,
10617                                log_command);
10618                     if (chdir (previous_dir))
10619                       g_warning ("%s: and chdir failed",
10620                                  __func__);
10621                     g_free (previous_dir);
10622                     return -1;
10623                   }
10624 
10625                 /* Child succeeded, continue to process result. */
10626 
10627                 break;
10628               }
10629           }
10630       }
10631     else
10632       {
10633         /* Just run the command as the current user. */
10634         g_free (archive_file);
10635 
10636         ret = system (command);
10637         /* Ignore the shell command exit status, because we've not
10638          * specified what it must be in the past. */
10639         if (ret == -1)
10640           {
10641             g_warning ("%s: system failed with ret %i, %i, %s",
10642                        __func__,
10643                        ret,
10644                        WEXITSTATUS (ret),
10645                        log_command);
10646             if (chdir (previous_dir))
10647               g_warning ("%s: and chdir failed",
10648                          __func__);
10649             g_free (previous_dir);
10650             g_free (command);
10651             return -1;
10652           }
10653 
10654       }
10655 
10656     g_free (command);
10657     g_free (log_command);
10658 
10659     /* Change back to the previous directory. */
10660 
10661     if (chdir (previous_dir))
10662       {
10663         g_warning ("%s: Failed to chdir back: %s",
10664                    __func__,
10665                    strerror (errno));
10666         g_free (previous_dir);
10667         return -1;
10668       }
10669     g_free (previous_dir);
10670 
10671     /* Remove the directory. */
10672 
10673     gvm_file_remove_recurse (archive_dir);
10674 
10675     return 0;
10676   }
10677 }
10678 
10679 /**
10680  * @brief  Appends an XML fragment for vFire call input to a string buffer.
10681  *
10682  * @param[in]  key      The name of the key.
10683  * @param[in]  value    The value to add.
10684  * @param[in]  buffer   The string buffer to append to.
10685  *
10686  * @return  Always FALSE.
10687  */
10688 gboolean
buffer_vfire_call_input(gchar * key,gchar * value,GString * buffer)10689 buffer_vfire_call_input (gchar *key, gchar *value, GString *buffer)
10690 {
10691   xml_string_append (buffer,
10692                      "<%s>%s</%s>",
10693                      key, value, key);
10694   return FALSE;
10695 }
10696 
10697 /**
10698  * @brief  Checks a mandatory vFire parameter and adds it to the config XML.
10699  *
10700  * @param[in]  param  The parameter to check.
10701  */
10702 #define APPEND_VFIRE_PARAM(param)                                             \
10703   if (param)                                                                  \
10704     xml_string_append (config_xml,                                            \
10705                        "<" G_STRINGIFY(param) ">%s</" G_STRINGIFY(param) ">", \
10706                        param);                                                \
10707   else                                                                        \
10708     {                                                                         \
10709       if (message)                                                            \
10710         *message = g_strdup ("Mandatory " G_STRINGIFY(param) " missing.");    \
10711       g_warning ("%s: Missing " G_STRINGIFY(param) ".", __func__);        \
10712       g_string_free (config_xml, TRUE);                                       \
10713       return -1;                                                              \
10714     }
10715 
10716 /**
10717  * @brief Create a new call on an Alemba vFire server.
10718  *
10719  * @param[in]  base_url       Base url of the vFire server.
10720  * @param[in]  client_id      The Alemba API Client ID to authenticate with.
10721  * @param[in]  session_type   Alemba session type to use, e.g. "Analyst".
10722  * @param[in]  username       Username.
10723  * @param[in]  password       Password.
10724  * @param[in]  report_data    Data for vFire call report attachments.
10725  * @param[in]  call_data      Data for creating the vFire call.
10726  * @param[in]  description_template  Template for the description text.
10727  * @param[out] message        Error message.
10728  *
10729  * @return 0 success, -1 error, -5 alert script failed.
10730  */
10731 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)10732 send_to_vfire (const char *base_url, const char *client_id,
10733                const char *session_type, const char *username,
10734                const char *password, GPtrArray *report_data,
10735                GTree *call_data, const char *description_template,
10736                gchar **message)
10737 {
10738   const char *alert_id = "159f79a5-fce8-4ec5-aa49-7d17a77739a3";
10739   GString *config_xml;
10740   int index;
10741   char config_xml_filename[] = "/tmp/gvmd_vfire_data_XXXXXX.xml";
10742   int config_xml_fd;
10743   FILE *config_xml_file;
10744   gchar **cmd;
10745   gchar *alert_script;
10746   int ret, exit_status;
10747   GError *err;
10748 
10749   config_xml = g_string_new ("<alert_data>");
10750 
10751   // Mandatory parameters
10752   APPEND_VFIRE_PARAM (base_url)
10753   APPEND_VFIRE_PARAM (client_id)
10754   APPEND_VFIRE_PARAM (username)
10755   APPEND_VFIRE_PARAM (password)
10756 
10757   // Optional parameters
10758   xml_string_append (config_xml,
10759                      "<session_type>%s</session_type>",
10760                      session_type ? session_type : "Analyst");
10761 
10762   // Call input
10763   g_string_append (config_xml, "<call_input>");
10764   g_tree_foreach (call_data, ((GTraverseFunc) buffer_vfire_call_input),
10765                   config_xml);
10766   g_string_append (config_xml, "</call_input>");
10767 
10768   // Report data
10769   g_string_append (config_xml, "<attach_reports>");
10770   for (index = 0; index < report_data->len; index++)
10771     {
10772       alert_report_data_t *report_item;
10773       report_item = g_ptr_array_index (report_data, index);
10774 
10775       xml_string_append (config_xml,
10776                          "<report>"
10777                          "<src_path>%s</src_path>"
10778                          "<dest_filename>%s</dest_filename>"
10779                          "<content_type>%s</content_type>"
10780                          "<report_format>%s</report_format>"
10781                          "</report>",
10782                          report_item->local_filename,
10783                          report_item->remote_filename,
10784                          report_item->content_type,
10785                          report_item->report_format_name);
10786 
10787     }
10788   g_string_append (config_xml, "</attach_reports>");
10789 
10790   // End data XML and output to file
10791   g_string_append (config_xml, "</alert_data>");
10792 
10793   config_xml_fd = g_mkstemp (config_xml_filename);
10794   if (config_xml_fd == -1)
10795     {
10796       g_warning ("%s: Could not create alert script config file: %s",
10797                  __func__, strerror (errno));
10798       g_string_free (config_xml, TRUE);
10799       return -1;
10800     }
10801 
10802   config_xml_file = fdopen (config_xml_fd, "w");
10803   if (config_xml_file == NULL)
10804     {
10805       g_warning ("%s: Could not open alert script config file: %s",
10806                  __func__, strerror (errno));
10807       g_string_free (config_xml, TRUE);
10808       close (config_xml_fd);
10809       return -1;
10810     }
10811 
10812   if (fprintf (config_xml_file, "%s", config_xml->str) <= 0)
10813     {
10814       g_warning ("%s: Could not write alert script config file: %s",
10815                  __func__, strerror (errno));
10816       g_string_free (config_xml, TRUE);
10817       fclose (config_xml_file);
10818       return -1;
10819     }
10820 
10821   fflush (config_xml_file);
10822   fclose (config_xml_file);
10823   g_string_free (config_xml, TRUE);
10824 
10825   // Run the script
10826   alert_script = g_build_filename (GVMD_DATA_DIR,
10827                                    "global_alert_methods",
10828                                    alert_id,
10829                                    "alert",
10830                                    NULL);
10831 
10832   // TODO: Drop privileges when running as root
10833 
10834   cmd = (gchar **) g_malloc (3 * sizeof (gchar *));
10835   cmd[0] = alert_script;
10836   cmd[1] = config_xml_filename;
10837   cmd[2] = NULL;
10838 
10839   ret = 0;
10840   if (g_spawn_sync (NULL,
10841                     cmd,
10842                     NULL,
10843                     G_SPAWN_STDOUT_TO_DEV_NULL,
10844                     NULL,
10845                     NULL,
10846                     NULL,
10847                     message,
10848                     &exit_status,
10849                     &err) == FALSE)
10850     {
10851       g_warning ("%s: Failed to run alert script: %s",
10852                  __func__, err->message);
10853       ret = -1;
10854     }
10855 
10856   if (exit_status)
10857     {
10858       g_warning ("%s: Alert script exited with status %d",
10859                  __func__, exit_status);
10860       g_message ("%s: stderr: %s",
10861                  __func__, message ? *message: "");
10862       ret = -5;
10863     }
10864 
10865   // Cleanup
10866   g_free (cmd);
10867   g_free (alert_script);
10868   g_unlink (config_xml_filename);
10869 
10870   return ret;
10871 }
10872 
10873 /**
10874  * @brief Convert an XML report and send it to a TippingPoint SMS.
10875  *
10876  * @param[in]  report           Report to send.
10877  * @param[in]  report_size      Size of report.
10878  * @param[in]  username         Username.
10879  * @param[in]  password         Password.
10880  * @param[in]  hostname         Hostname.
10881  * @param[in]  certificate      Certificate.
10882  * @param[in]  cert_workaround  Whether to use cert workaround.
10883  * @param[out] message          Custom error message of the script.
10884  *
10885  * @return 0 success, -1 error.
10886  */
10887 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)10888 send_to_tippingpoint (const char *report, size_t report_size,
10889                       const char *username, const char *password,
10890                       const char *hostname, const char *certificate,
10891                       int cert_workaround, gchar **message)
10892 {
10893   const char *alert_id = "5b39c481-9137-4876-b734-263849dd96ce";
10894   char report_dir[] = "/tmp/gvmd_alert_XXXXXX";
10895   gchar *auth_config, *report_path, *error_path, *extra_path, *cert_path;
10896   gchar *command_args, *hostname_clean, *convert_script;
10897   GError *error = NULL;
10898   int ret;
10899 
10900   /* Setup auth file contents */
10901   auth_config = g_strdup_printf ("machine %s\n"
10902                                  "login %s\n"
10903                                  "password %s\n",
10904                                  cert_workaround ? "Tippingpoint" : hostname,
10905                                  username, password);
10906 
10907   /* Setup common files. */
10908   ret = alert_script_init ("report", report, report_size,
10909                            auth_config, strlen (auth_config),
10910                            report_dir,
10911                            &report_path, &error_path, &extra_path);
10912   g_free (auth_config);
10913 
10914   if (ret)
10915     return ret;
10916 
10917   /* Setup certificate file */
10918   cert_path = g_build_filename (report_dir, "cacert.pem", NULL);
10919 
10920   if (g_file_set_contents (cert_path,
10921                            certificate, strlen (certificate),
10922                            &error) == FALSE)
10923     {
10924       g_warning ("%s: Failed to write TLS certificate to file: %s",
10925                  __func__, error->message);
10926       alert_script_cleanup (report_dir, report_path, error_path, extra_path);
10927       g_free (cert_path);
10928       return -1;
10929     }
10930 
10931   if (geteuid () == 0)
10932     {
10933       struct passwd *nobody;
10934 
10935       /* Run the command with lower privileges in a fork. */
10936 
10937       nobody = getpwnam ("nobody");
10938       if ((nobody == NULL)
10939           || chown (cert_path, nobody->pw_uid, nobody->pw_gid))
10940         {
10941           g_warning ("%s: Failed to set permissions for user nobody: %s",
10942                       __func__,
10943                       strerror (errno));
10944           g_free (cert_path);
10945           alert_script_cleanup (report_dir, report_path, error_path,
10946                                 extra_path);
10947           return -1;
10948         }
10949     }
10950 
10951   /* Build args and run the script */
10952   hostname_clean = g_shell_quote (hostname);
10953   convert_script = g_build_filename (GVMD_DATA_DIR,
10954                                      "global_alert_methods",
10955                                      alert_id,
10956                                      "report-convert.py",
10957                                      NULL);
10958 
10959   command_args = g_strdup_printf ("%s %s %d %s",
10960                                   hostname_clean, cert_path, cert_workaround,
10961                                   convert_script);
10962 
10963   g_free (hostname_clean);
10964   g_free (cert_path);
10965   g_free (convert_script);
10966 
10967   ret = alert_script_exec (alert_id, command_args, report_path, report_dir,
10968                            error_path, extra_path, message);
10969   if (ret)
10970     {
10971       alert_script_cleanup (report_dir, report_path, error_path, extra_path);
10972       return ret;
10973     }
10974 
10975   /* Remove the directory. */
10976   ret = alert_script_cleanup (report_dir, report_path, error_path, extra_path);
10977   return ret;
10978 }
10979 
10980 /**
10981  * @brief Format string for simple notice alert email.
10982  */
10983 #define SIMPLE_NOTICE_FORMAT                                                  \
10984  "%s.\n"                                                                      \
10985  "\n"                                                                         \
10986  "After the event %s,\n"                                                      \
10987  "the following condition was met: %s\n"                                      \
10988  "\n"                                                                         \
10989  "This email escalation is not configured to provide more details.\n"         \
10990  "Full details are stored on the scan engine.\n"                              \
10991  "\n"                                                                         \
10992  "\n"                                                                         \
10993  "Note:\n"                                                                    \
10994  "This email was sent to you as a configured security scan escalation.\n"     \
10995  "Please contact your local system administrator if you think you\n"          \
10996  "should not have received it.\n"
10997 
10998 /**
10999  * @brief Format string for simple notice alert email.
11000  */
11001 #define SECINFO_SIMPLE_NOTICE_FORMAT                                          \
11002  "%s.\n"                                                                      \
11003  "\n"                                                                         \
11004  "After the event %s,\n"                                                      \
11005  "the following condition was met: %s\n"                                      \
11006  "\n"                                                                         \
11007  "This email escalation is not configured to provide more details.\n"         \
11008  "Full details are stored on the scan engine.\n"                              \
11009  "\n"                                                                         \
11010  "\n"                                                                         \
11011  "Note:\n"                                                                    \
11012  "This email was sent to you as a configured security scan escalation.\n"     \
11013  "Please contact your local system administrator if you think you\n"          \
11014  "should not have received it.\n"
11015 
11016 /**
11017  * @brief Print an alert subject.
11018  *
11019  * @param[in]  subject     Format string for subject.
11020  * @param[in]  event       Event.
11021  * @param[in]  event_data  Event data.
11022  * @param[in]  alert       Alert.
11023  * @param[in]  task        Task.
11024  * @param[in]  total       Total number of resources (for SecInfo alerts).
11025  *
11026  * @return Freshly allocated subject.
11027  */
11028 static gchar *
alert_subject_print(const gchar * subject,event_t event,const void * event_data,alert_t alert,task_t task,int total)11029 alert_subject_print (const gchar *subject, event_t event,
11030                      const void *event_data,
11031                      alert_t alert, task_t task, int total)
11032 {
11033   int formatting;
11034   const gchar *point, *end;
11035   GString *new_subject;
11036 
11037   assert (subject);
11038 
11039   new_subject = g_string_new ("");
11040   for (formatting = 0, point = subject, end = (subject + strlen (subject));
11041        point < end;
11042        point++)
11043     if (formatting)
11044       {
11045         switch (*point)
11046           {
11047             case '$':
11048               g_string_append_c (new_subject, '$');
11049               break;
11050             case 'd':
11051               /* Date that the check was last performed. */
11052               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
11053                 {
11054                   char time_string[100];
11055                   time_t date;
11056                   struct tm tm;
11057 
11058                   if (event_data && (strcasecmp (event_data, "nvt") == 0))
11059                     date = nvts_check_time ();
11060                   else if (type_is_scap (event_data))
11061                     date = scap_check_time ();
11062                   else
11063                     date = cert_check_time ();
11064 
11065                   if (localtime_r (&date, &tm) == NULL)
11066                     {
11067                       g_warning ("%s: localtime failed, aborting",
11068                                  __func__);
11069                       abort ();
11070                     }
11071                   if (strftime (time_string, 98, "%F", &tm) == 0)
11072                     break;
11073                   g_string_append (new_subject, time_string);
11074                 }
11075               break;
11076             case 'e':
11077               {
11078                 gchar *event_desc;
11079                 event_desc = event_description (event, event_data,
11080                                                 NULL);
11081                 g_string_append (new_subject, event_desc);
11082                 g_free (event_desc);
11083                 break;
11084               }
11085             case 'n':
11086               {
11087                 if (task)
11088                   {
11089                     char *name = task_name (task);
11090                     g_string_append (new_subject, name);
11091                     free (name);
11092                   }
11093                 break;
11094               }
11095             case 'N':
11096               {
11097                 /* Alert Name */
11098                 char *name = alert_name (alert);
11099                 g_string_append (new_subject, name);
11100                 free (name);
11101                 break;
11102               }
11103             case 'q':
11104               if (event == EVENT_NEW_SECINFO)
11105                 g_string_append (new_subject, "New");
11106               else if (event == EVENT_UPDATED_SECINFO)
11107                 g_string_append (new_subject, "Updated");
11108               break;
11109             case 's':
11110               /* Type. */
11111               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
11112                 g_string_append (new_subject, type_name (event_data));
11113               break;
11114             case 'S':
11115               /* Type, plural. */
11116               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
11117                 g_string_append (new_subject, type_name_plural (event_data));
11118               break;
11119             case 'T':
11120               g_string_append_printf (new_subject, "%i", total);
11121               break;
11122             case 'u':
11123               {
11124                 /* Current user or owner of the Alert */
11125                 if (current_credentials.username
11126                     && strcmp (current_credentials.username, ""))
11127                   {
11128                     g_string_append (new_subject, current_credentials.username);
11129                   }
11130                 else
11131                   {
11132                     char *owner = alert_owner_uuid (alert);
11133                     gchar *name = user_name (owner);
11134                     g_string_append (new_subject, name);
11135                     free (owner);
11136                     g_free (name);
11137                   }
11138                 break;
11139               }
11140             case 'U':
11141               {
11142                 /* Alert UUID */
11143                 char *uuid = alert_uuid (alert);
11144                 g_string_append (new_subject, uuid);
11145                 free (uuid);
11146                 break;
11147               }
11148             default:
11149               g_string_append_c (new_subject, '$');
11150               g_string_append_c (new_subject, *point);
11151               break;
11152           }
11153         formatting = 0;
11154       }
11155     else if (*point == '$')
11156       formatting = 1;
11157     else
11158       g_string_append_c (new_subject, *point);
11159 
11160   return g_string_free (new_subject, FALSE);
11161 }
11162 
11163 /**
11164  * @brief Print an alert message.
11165  *
11166  * @param[in]  message      Format string for message.
11167  * @param[in]  event        Event.
11168  * @param[in]  event_data   Event data.
11169  * @param[in]  task         Task.
11170  * @param[in]  alert        Alert.
11171  * @param[in]  condition    Alert condition.
11172  * @param[in]  format_name  Report format name.
11173  * @param[in]  filter       Filter.
11174  * @param[in]  term         Filter term.
11175  * @param[in]  zone         Timezone.
11176  * @param[in]  host_summary    Host summary.
11177  * @param[in]  content         The report, for inlining.
11178  * @param[in]  content_length  Length of content.
11179  * @param[in]  truncated       Whether the report was truncated.
11180  * @param[in]  total        Total number of resources (for SecInfo alerts).
11181  * @param[in]  max_length   Max allowed length of content.
11182  *
11183  * @return Freshly allocated message.
11184  */
11185 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)11186 alert_message_print (const gchar *message, event_t event,
11187                      const void *event_data, task_t task,
11188                      alert_t alert, alert_condition_t condition,
11189                      gchar *format_name, filter_t filter,
11190                      const gchar *term, const gchar *zone,
11191                      const gchar *host_summary, const gchar *content,
11192                      gsize content_length, int truncated, int total,
11193                      int max_length)
11194 {
11195   int formatting;
11196   const gchar *point, *end;
11197   GString *new_message;
11198 
11199   assert (message);
11200 
11201   new_message = g_string_new ("");
11202   for (formatting = 0, point = message, end = (message + strlen (message));
11203        point < end;
11204        point++)
11205     if (formatting)
11206       {
11207         switch (*point)
11208           {
11209             case '$':
11210               g_string_append_c (new_message, '$');
11211               break;
11212             case 'c':
11213               {
11214                 gchar *condition_desc;
11215                 condition_desc = alert_condition_description
11216                                   (condition, alert);
11217                 g_string_append (new_message, condition_desc);
11218                 g_free (condition_desc);
11219                 break;
11220               }
11221             case 'd':
11222               /* Date that the check was last performed. */
11223               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
11224                 {
11225                   char time_string[100];
11226                   time_t date;
11227                   struct tm tm;
11228 
11229                   if (event_data && (strcasecmp (event_data, "nvt") == 0))
11230                     date = nvts_check_time ();
11231                   else if (type_is_scap (event_data))
11232                     date = scap_check_time ();
11233                   else
11234                     date = cert_check_time ();
11235 
11236                   if (localtime_r (&date, &tm) == NULL)
11237                     {
11238                       g_warning ("%s: localtime failed, aborting",
11239                                  __func__);
11240                       abort ();
11241                     }
11242                   if (strftime (time_string, 98, "%F", &tm) == 0)
11243                     break;
11244                   g_string_append (new_message, time_string);
11245                 }
11246               break;
11247             case 'e':
11248               {
11249                 gchar *event_desc;
11250                 event_desc = event_description (event, event_data,
11251                                                 NULL);
11252                 g_string_append (new_message, event_desc);
11253                 g_free (event_desc);
11254                 break;
11255               }
11256             case 'H':
11257               {
11258                 /* Host summary. */
11259 
11260                 g_string_append (new_message,
11261                                  host_summary ? host_summary : "N/A");
11262                 break;
11263               }
11264             case 'i':
11265               {
11266                 if (content)
11267                   {
11268                     g_string_append_printf (new_message,
11269                                             "%.*s",
11270                                             /* Cast for 64 bit. */
11271                                             (int) MIN (content_length,
11272                                                        max_content_length),
11273                                             content);
11274                     if (content_length > max_content_length)
11275                       g_string_append_printf (new_message,
11276                                               "\n... (report truncated after"
11277                                               " %i characters)\n",
11278                                               max_content_length);
11279                   }
11280 
11281                 break;
11282               }
11283             case 'n':
11284               if (task)
11285                 {
11286                   char *name = task_name (task);
11287                   g_string_append (new_message, name);
11288                   free (name);
11289                 }
11290               break;
11291             case 'N':
11292               {
11293                 /* Alert Name */
11294                 char *name = alert_name (alert);
11295                 g_string_append (new_message, name);
11296                 free (name);
11297                 break;
11298               }
11299             case 'r':
11300               {
11301                 /* Report format name. */
11302 
11303                 g_string_append (new_message,
11304                                  format_name ? format_name : "N/A");
11305                 break;
11306               }
11307             case 'F':
11308               {
11309                 /* Name of filter. */
11310 
11311                 if (filter)
11312                   {
11313                     char *name = filter_name (filter);
11314                     g_string_append (new_message, name);
11315                     free (name);
11316                   }
11317                 else
11318                   g_string_append (new_message, "N/A");
11319                 break;
11320               }
11321             case 'f':
11322               {
11323                 /* Filter term. */
11324 
11325                 g_string_append (new_message, term ? term : "N/A");
11326                 break;
11327               }
11328             case 'q':
11329               {
11330                 if (event == EVENT_NEW_SECINFO)
11331                   g_string_append (new_message, "New");
11332                 else if (event == EVENT_UPDATED_SECINFO)
11333                   g_string_append (new_message, "Updated");
11334                 break;
11335               }
11336             case 's':
11337               /* Type. */
11338               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
11339                 g_string_append (new_message, type_name (event_data));
11340               break;
11341             case 'S':
11342               /* Type, plural. */
11343               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
11344                 g_string_append (new_message, type_name_plural (event_data));
11345               break;
11346             case 't':
11347               {
11348                 if (truncated)
11349                   g_string_append_printf (new_message,
11350                                           "Note: This report exceeds the"
11351                                           " maximum length of %i characters"
11352                                           " and thus\n"
11353                                           "was truncated.\n",
11354                                           max_length);
11355                 break;
11356               }
11357             case 'T':
11358               {
11359                 g_string_append_printf (new_message, "%i", total);
11360                 break;
11361               }
11362             case 'u':
11363               {
11364                 /* Current user or owner of the Alert */
11365                 if (current_credentials.username
11366                     && strcmp (current_credentials.username, ""))
11367                   {
11368                     g_string_append (new_message, current_credentials.username);
11369                   }
11370                 else
11371                   {
11372                     char *owner = alert_owner_uuid (alert);
11373                     gchar *name = user_name (owner);
11374                     g_string_append (new_message, name);
11375                     free (owner);
11376                     g_free (name);
11377                   }
11378                 break;
11379               }
11380             case 'U':
11381               {
11382                 /* Alert UUID */
11383                 char *uuid = alert_uuid (alert);
11384                 g_string_append (new_message, uuid);
11385                 free (uuid);
11386                 break;
11387               }
11388             case 'z':
11389               {
11390                 /* Timezone. */
11391 
11392                 g_string_append (new_message, zone ? zone : "N/A");
11393                 break;
11394               }
11395 
11396             case 'R':
11397             default:
11398               g_string_append_c (new_message, '$');
11399               g_string_append_c (new_message, *point);
11400               break;
11401           }
11402         formatting = 0;
11403       }
11404     else if (*point == '$')
11405       formatting = 1;
11406     else
11407       g_string_append_c (new_message, *point);
11408 
11409   return g_string_free (new_message, FALSE);
11410 }
11411 
11412 /**
11413  * @brief Print an SCP alert file path.
11414  *
11415  * @param[in]  message      Format string for message.
11416  * @param[in]  task         Task.
11417  *
11418  * @return Freshly allocated message.
11419  */
11420 static gchar *
scp_alert_path_print(const gchar * message,task_t task)11421 scp_alert_path_print (const gchar *message, task_t task)
11422 {
11423   int formatting;
11424   const gchar *point, *end;
11425   GString *new_message;
11426 
11427   assert (message);
11428 
11429   new_message = g_string_new ("");
11430   for (formatting = 0, point = message, end = (message + strlen (message));
11431        point < end;
11432        point++)
11433     if (formatting)
11434       {
11435         switch (*point)
11436           {
11437             case '$':
11438               g_string_append_c (new_message, '$');
11439               break;
11440             case 'D':
11441             case 'T':
11442               {
11443                 char time_string[9];
11444                 time_t current_time;
11445                 struct tm tm;
11446                 const gchar *format_str;
11447 
11448                 if (*point == 'T')
11449                   format_str = "%H%M%S";
11450                 else
11451                   format_str = "%Y%m%d";
11452 
11453                 memset(&time_string, 0, 9);
11454                 current_time = time (NULL);
11455 
11456                 if (localtime_r (&current_time, &tm) == NULL)
11457                   {
11458                     g_warning ("%s: localtime failed, aborting",
11459                                 __func__);
11460                     abort ();
11461                   }
11462                 if (strftime (time_string, 9, format_str, &tm))
11463                   g_string_append (new_message, time_string);
11464                 break;
11465               }
11466             case 'n':
11467               if (task)
11468                 {
11469                   char *name = task_name (task);
11470                   g_string_append (new_message, name);
11471                   free (name);
11472                 }
11473               break;
11474           }
11475         formatting = 0;
11476       }
11477     else if (*point == '$')
11478       formatting = 1;
11479     else
11480       g_string_append_c (new_message, *point);
11481 
11482   return g_string_free (new_message, FALSE);
11483 }
11484 
11485 /**
11486  * @brief Build and send email for a ticket alert.
11487  *
11488  * @param[in]  alert       Alert.
11489  * @param[in]  ticket      Ticket.
11490  * @param[in]  event       Event.
11491  * @param[in]  event_data  Event data.
11492  * @param[in]  method      Method from alert.
11493  * @param[in]  condition   Condition from alert, which was met by event.
11494  * @param[in]  to_address    To address.
11495  * @param[in]  from_address  From address.
11496  * @param[in]  subject       Subject.
11497  *
11498  * @return 0 success, -1 error.
11499  */
11500 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)11501 email_ticket (alert_t alert, ticket_t ticket, event_t event,
11502               const void* event_data, alert_method_t method,
11503               alert_condition_t condition, const gchar *to_address,
11504               const gchar *from_address, const gchar *subject)
11505 {
11506   gchar *full_subject, *body;
11507   char *recipient_credential_id;
11508   credential_t recipient_credential;
11509   int ret;
11510 
11511   /* Setup subject. */
11512 
11513   full_subject = g_strdup_printf ("%s: %s (UUID: %s)",
11514                                   subject,
11515                                   ticket_nvt_name (ticket)
11516                                    ? ticket_nvt_name (ticket)
11517                                    : "[Orphan]",
11518                                   ticket_uuid (ticket));
11519 
11520   /* Setup body. */
11521 
11522   {
11523     gchar *event_desc, *condition_desc;
11524 
11525     event_desc = event_description (event, event_data, NULL);
11526     condition_desc = alert_condition_description
11527                       (condition, alert);
11528     body = g_strdup_printf (SIMPLE_NOTICE_FORMAT,
11529                             event_desc,
11530                             event_desc,
11531                             condition_desc);
11532     free (event_desc);
11533     free (condition_desc);
11534   }
11535 
11536   /* Get credential */
11537   recipient_credential_id = alert_data (alert, "method",
11538                                         "recipient_credential");
11539   recipient_credential = 0;
11540   if (recipient_credential_id)
11541     {
11542       find_credential_with_permission (recipient_credential_id,
11543                                        &recipient_credential, NULL);
11544     }
11545 
11546   /* Send email. */
11547 
11548   ret = email (to_address, from_address, full_subject,
11549                body, NULL, NULL, NULL, NULL,
11550                recipient_credential);
11551   g_free (body);
11552   g_free (full_subject);
11553   free (recipient_credential_id);
11554   return ret;
11555 }
11556 
11557 /**
11558  * @brief Build and send email for SecInfo alert.
11559  *
11560  * @param[in]  alert       Alert.
11561  * @param[in]  task        Task.
11562  * @param[in]  event       Event.
11563  * @param[in]  event_data  Event data.
11564  * @param[in]  method      Method from alert.
11565  * @param[in]  condition   Condition from alert, which was met by event.
11566  * @param[in]  to_address    To address.
11567  * @param[in]  from_address  From address.
11568  *
11569  * @return 0 success, -1 error, -2 failed to find report format, -3 failed to
11570  *         find filter.
11571  */
11572 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)11573 email_secinfo (alert_t alert, task_t task, event_t event,
11574                const void* event_data, alert_method_t method,
11575                alert_condition_t condition, const gchar *to_address,
11576                const gchar *from_address)
11577 {
11578   gchar *alert_subject, *message, *subject, *example, *list, *type, *base64;
11579   gchar *term, *body;
11580   char *notice, *recipient_credential_id, *condition_filter_id;
11581   filter_t condition_filter;
11582   credential_t recipient_credential;
11583   int ret, count;
11584 
11585   list = new_secinfo_list (event, event_data, alert, &count);
11586 
11587   type = g_strdup (event_data);
11588   if (type && (example = strstr (type, "_example")))
11589     example[0] = '\0';
11590 
11591   /* Setup subject. */
11592 
11593   subject = g_strdup_printf
11594              ("[GVM] %s %s arrived",
11595               event == EVENT_NEW_SECINFO ? "New" : "Updated",
11596               type_name_plural (type ? type : "nvt"));
11597   alert_subject = alert_data (alert, "method", "subject");
11598   if (alert_subject && strlen (alert_subject))
11599     {
11600       g_free (subject);
11601       subject = alert_subject_print (alert_subject, event,
11602                                      type, alert, task, count);
11603     }
11604   g_free (alert_subject);
11605 
11606   /* Setup body. */
11607 
11608   notice = alert_data (alert, "method", "notice");
11609 
11610   message = alert_data (alert, "method", "message");
11611   if (message == NULL || strlen (message) == 0)
11612     {
11613       g_free (message);
11614       if (notice && strcmp (notice, "0") == 0)
11615         /* Message with inlined report. */
11616         message = g_strdup (SECINFO_ALERT_MESSAGE_INCLUDE);
11617       else if (notice && strcmp (notice, "2") == 0)
11618         /* Message with attached report. */
11619         message = g_strdup (SECINFO_ALERT_MESSAGE_ATTACH);
11620       else
11621         /* Simple notice message. */
11622         message = NULL;
11623     }
11624 
11625   base64 = NULL;
11626   if (list && notice && strcmp (notice, "2") == 0)
11627     {
11628       /* Add list as text attachment. */
11629       if (max_attach_length <= 0
11630           || strlen (list) <= max_attach_length)
11631         base64 = g_base64_encode ((guchar*) list,
11632                                   strlen (list));
11633     }
11634 
11635   condition_filter = 0;
11636   term = NULL;
11637   condition_filter_id = alert_data (alert, "condition", "filter_id");
11638   if (condition_filter_id)
11639     {
11640       gchar *quoted_filter_id;
11641       quoted_filter_id = sql_quote (condition_filter_id);
11642       sql_int64 (&condition_filter,
11643                  "SELECT id FROM filters WHERE uuid = '%s'",
11644                  quoted_filter_id);
11645       term = filter_term (condition_filter_id);
11646       g_free (quoted_filter_id);
11647     }
11648   free (condition_filter_id);
11649 
11650   if (message && strlen (message))
11651     body = alert_message_print (message, event, type,
11652                                 task, alert, condition,
11653                                 NULL, condition_filter, term, NULL, NULL,
11654                                 list,
11655                                 list ? strlen (list) : 0,
11656                                 0, count, 0);
11657   else
11658     {
11659       gchar *event_desc, *condition_desc;
11660       event_desc = event_description (event, event_data, NULL);
11661       condition_desc = alert_condition_description
11662                         (condition, alert);
11663       body = g_strdup_printf (SECINFO_SIMPLE_NOTICE_FORMAT,
11664                               event_desc,
11665                               event_desc,
11666                               condition_desc);
11667       free (event_desc);
11668       free (condition_desc);
11669     }
11670 
11671   g_free (term);
11672   g_free (message);
11673   g_free (list);
11674 
11675   /* Get credential */
11676   recipient_credential_id = alert_data (alert, "method",
11677                                         "recipient_credential");
11678   recipient_credential = 0;
11679   if (recipient_credential_id)
11680     {
11681       find_credential_with_permission (recipient_credential_id,
11682                                        &recipient_credential, NULL);
11683     }
11684 
11685   /* Send email. */
11686 
11687   ret = email (to_address, from_address, subject,
11688                body, base64,
11689                base64 ? "text/plain" : NULL,
11690                base64 ? "secinfo-alert" : NULL,
11691                base64 ? "txt" : NULL,
11692                recipient_credential);
11693   g_free (body);
11694   g_free (type);
11695   g_free (subject);
11696   free (recipient_credential_id);
11697   return ret;
11698 }
11699 
11700 /**
11701  * @brief Get the delta report to be used for an alert.
11702  *
11703  * @param[in]  alert         Alert.
11704  * @param[in]  task          Task.
11705  * @param[in]  report        Report.
11706  *
11707  * @return Report to compare with if required, else 0.
11708  */
11709 static report_t
get_delta_report(alert_t alert,task_t task,report_t report)11710 get_delta_report (alert_t alert, task_t task, report_t report)
11711 {
11712   char *delta_type;
11713   report_t delta_report;
11714 
11715   delta_type = alert_data (alert,
11716                            "method",
11717                            "delta_type");
11718 
11719   if (delta_type == NULL)
11720     return 0;
11721 
11722   delta_report = 0;
11723   if (strcmp (delta_type, "Previous") == 0)
11724     {
11725       if (task_report_previous (task, report, &delta_report))
11726         g_warning ("%s: failed to get previous report", __func__);
11727     }
11728   else if (strcmp (delta_type, "Report") == 0)
11729     {
11730       char *delta_report_id;
11731 
11732       delta_report_id = alert_data (alert,
11733                                     "method",
11734                                     "delta_report_id");
11735 
11736       if (delta_report_id
11737           && find_report_with_permission (delta_report_id,
11738                                           &delta_report,
11739                                           "get_reports"))
11740         g_warning ("%s: error while finding report", __func__);
11741     }
11742   free (delta_type);
11743 
11744   return delta_report;
11745 }
11746 
11747 /**
11748  * @brief  Generates report results get data for an alert.
11749  *
11750  * @param[in]  alert              The alert to try to get the filter data from.
11751  * @param[in]  base_get_data      The get data for fallback and other data.
11752  * @param[out] alert_filter_get   Pointer to the newly allocated get_data.
11753  * @param[out] filter_return      Pointer to the filter.
11754  *
11755  * @return  0 success, -1 error, -3 filter not found.
11756  */
11757 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)11758 generate_alert_filter_get (alert_t alert, const get_data_t *base_get_data,
11759                            get_data_t **alert_filter_get,
11760                            filter_t *filter_return)
11761 {
11762   char *filt_id;
11763   filter_t filter;
11764 
11765   if (alert_filter_get == NULL)
11766     return -1;
11767 
11768   filt_id = alert_filter_id (alert);
11769   filter = 0;
11770   if (filt_id)
11771     {
11772       if (find_filter_with_permission (filt_id, &filter,
11773                                        "get_filters"))
11774         {
11775           free (filt_id);
11776           return -1;
11777         }
11778       if (filter == 0)
11779         {
11780           free (filt_id);
11781           return -3;
11782         }
11783     }
11784 
11785   if (filter_return)
11786     *filter_return = filter;
11787 
11788   if (filter)
11789     {
11790       (*alert_filter_get) = g_malloc0 (sizeof (get_data_t));
11791       (*alert_filter_get)->details = base_get_data->details;
11792       (*alert_filter_get)->ignore_pagination = base_get_data->ignore_pagination;
11793       (*alert_filter_get)->ignore_max_rows_per_page
11794         = base_get_data->ignore_max_rows_per_page;
11795       (*alert_filter_get)->filt_id = g_strdup (filt_id);
11796       (*alert_filter_get)->filter = filter_term (filt_id);
11797     }
11798   else
11799     (*alert_filter_get) = NULL;
11800 
11801   /* Adjust filter for report composer.
11802    *
11803    * As a first step towards a full composer we have two fields stored
11804    * on the alert for controlling visibility of notes and overrides.
11805    *
11806    * We simply use these fields to adjust the filter.  In the future we'll
11807    * remove the filter terms and extend the way we get the report. */
11808 
11809   if (filter)
11810     {
11811       gchar *include_notes, *include_overrides;
11812 
11813       include_notes = alert_data (alert, "method",
11814                                   "composer_include_notes");
11815       if (include_notes)
11816         {
11817           gchar *new_filter;
11818 
11819           new_filter = g_strdup_printf ("notes=%i %s",
11820                                         atoi (include_notes),
11821                                         (*alert_filter_get)->filter);
11822           g_free ((*alert_filter_get)->filter);
11823           (*alert_filter_get)->filter = new_filter;
11824           (*alert_filter_get)->filt_id = NULL;
11825           g_free (include_notes);
11826         }
11827 
11828       include_overrides = alert_data (alert, "method",
11829                                       "composer_include_overrides");
11830       if (include_overrides)
11831         {
11832           gchar *new_filter;
11833 
11834           new_filter = g_strdup_printf ("overrides=%i %s",
11835                                         atoi (include_overrides),
11836                                         (*alert_filter_get)->filter);
11837           g_free ((*alert_filter_get)->filter);
11838           (*alert_filter_get)->filter = new_filter;
11839           (*alert_filter_get)->filt_id = NULL;
11840           g_free (include_overrides);
11841         }
11842     }
11843 
11844   return 0;
11845 }
11846 
11847 /**
11848  * @brief Generate report content for alert
11849  *
11850  * @param[in]  alert  The alert the report is generated for.
11851  * @param[in]  report Report or NULL to get last report of task.
11852  * @param[in]  task   Task the report belongs to.
11853  * @param[in]  get    GET data for the report.
11854  * @param[in]  report_format_data_name  Name of alert data with report format,
11855  *                                      or NULL if not configurable.
11856  * @param[in]  report_format_lookup     Name of report format to lookup if
11857  *                                      lookup by name, or NULL if not required.
11858  *                                      Used if report_format_data_name is
11859  *                                      NULL or fails.
11860  * @param[in]  fallback_format_id       UUID of fallback report format.  Used
11861  *                                      if both report_format_data_name and
11862  *                                      report_format_lookup are NULL or fail.
11863  * @param[in]  notes_details     Whether to include details of notes in report.
11864  * @param[in]  overrides_details Whether to include override details in report.
11865  * @param[out] content              Report content location.
11866  * @param[out] content_length       Length of report content.
11867  * @param[out] extension            File extension of report format.
11868  * @param[out] content_type         Content type of report format.
11869  * @param[out] term                 Filter term.
11870  * @param[out] report_zone          Actual timezone used in report.
11871  * @param[out] host_summary         Summary of results per host.
11872  * @param[out] used_report_format   Report format used.
11873  * @param[out] filter_return        Filter used.
11874  *
11875  * @return 0 success, -1 error, -2 failed to find report format, -3 failed to
11876  *         find filter.
11877  */
11878 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)11879 report_content_for_alert (alert_t alert, report_t report, task_t task,
11880                           const get_data_t *get,
11881                           const char *report_format_data_name,
11882                           const char *report_format_lookup,
11883                           const char *fallback_format_id,
11884                           int notes_details, int overrides_details,
11885                           gchar **content, gsize *content_length,
11886                           gchar **extension, gchar **content_type,
11887                           gchar **term, gchar **report_zone,
11888                           gchar **host_summary,
11889                           report_format_t *used_report_format,
11890                           filter_t *filter_return)
11891 {
11892   int ret;
11893   report_format_t report_format;
11894   get_data_t *alert_filter_get;
11895   gchar *report_content;
11896   filter_t filter;
11897 
11898   assert (content);
11899 
11900   // Get filter
11901 
11902   ret = generate_alert_filter_get (alert, get, &alert_filter_get, &filter);
11903   if (ret)
11904     return ret;
11905 
11906   // Get last report from task if no report is given
11907 
11908   if (report == 0)
11909     switch (sql_int64 (&report,
11910                         "SELECT max (id) FROM reports"
11911                         " WHERE task = %llu",
11912                         task))
11913       {
11914         case 0:
11915           if (report)
11916             break;
11917         case 1:        /* Too few rows in result of query. */
11918         case -1:
11919           if (alert_filter_get)
11920             {
11921               get_data_reset (alert_filter_get);
11922               g_free (alert_filter_get);
11923             }
11924           return -1;
11925           break;
11926         default:       /* Programming error. */
11927           assert (0);
11928           if (alert_filter_get)
11929             {
11930               get_data_reset (alert_filter_get);
11931               g_free (alert_filter_get);
11932             }
11933           return -1;
11934       }
11935 
11936   // Get report format or use fallback.
11937 
11938   report_format = 0;
11939 
11940   if (report_format_data_name)
11941     {
11942       gchar *format_uuid;
11943 
11944       format_uuid = alert_data (alert,
11945                                 "method",
11946                                 report_format_data_name);
11947 
11948       if (format_uuid && strlen (format_uuid))
11949         {
11950           if (find_report_format_with_permission (format_uuid,
11951                                                   &report_format,
11952                                                   "get_report_formats")
11953               || (report_format == 0))
11954             {
11955               g_warning ("%s: Could not find report format '%s' for %s",
11956                          __func__, format_uuid,
11957                          alert_method_name (alert_method (alert)));
11958               g_free (format_uuid);
11959               if (alert_filter_get)
11960                 {
11961                   get_data_reset (alert_filter_get);
11962                   g_free (alert_filter_get);
11963                 }
11964               return -2;
11965             }
11966           g_free (format_uuid);
11967         }
11968     }
11969 
11970   if (report_format_lookup && (report_format == 0))
11971     {
11972       if (lookup_report_format (report_format_lookup, &report_format)
11973           || (report_format == 0))
11974         {
11975           g_warning ("%s: Could not find report format '%s' for %s",
11976                      __func__, report_format_lookup,
11977                      alert_method_name (alert_method (alert)));
11978           if (alert_filter_get)
11979             {
11980               get_data_reset (alert_filter_get);
11981               g_free (alert_filter_get);
11982             }
11983           return -2;
11984         }
11985     }
11986 
11987   if (report_format == 0)
11988     {
11989       if (fallback_format_id == NULL)
11990         {
11991           g_warning ("%s: No fallback report format for %s",
11992                      __func__,
11993                      alert_method_name (alert_method (alert)));
11994           if (alert_filter_get)
11995             {
11996               get_data_reset (alert_filter_get);
11997               g_free (alert_filter_get);
11998             }
11999           return -1;
12000         }
12001 
12002       if (find_report_format_with_permission
12003             (fallback_format_id,
12004              &report_format,
12005              "get_report_formats")
12006           || (report_format == 0))
12007         {
12008           g_warning ("%s: Could not find fallback RFP '%s' for %s",
12009                       __func__, fallback_format_id,
12010                      alert_method_name (alert_method (alert)));
12011           if (alert_filter_get)
12012             {
12013               get_data_reset (alert_filter_get);
12014               g_free (alert_filter_get);
12015             }
12016           return -2;
12017         }
12018     }
12019 
12020   // Generate report content
12021 
12022   report_content = manage_report (report,
12023                                   get_delta_report (alert, task, report),
12024                                   alert_filter_get ? alert_filter_get : get,
12025                                   report_format,
12026                                   notes_details,
12027                                   overrides_details,
12028                                   content_length,
12029                                   extension,
12030                                   content_type,
12031                                   term,
12032                                   report_zone,
12033                                   host_summary);
12034 
12035   if (alert_filter_get)
12036     {
12037       get_data_reset (alert_filter_get);
12038       g_free (alert_filter_get);
12039     }
12040 
12041   if (report_content == NULL)
12042     return -1;
12043 
12044   *content = report_content;
12045   *used_report_format = report_format;
12046 
12047   return 0;
12048 }
12049 
12050 /**
12051  * @brief  Generates a filename or path for a report.
12052  *
12053  * If no custom_format is given, the setting "Report Export File Name"
12054  *  is used instead.
12055  *
12056  * @param[in]  report         The report to generate the filename for.
12057  * @param[in]  report_format  The report format to use.
12058  * @param[in]  custom_format  A custom format string to use for the filename.
12059  * @param[in]  add_extension  Whether to add the filename extension or not.
12060  *
12061  * @return  Newly allocated filename.
12062  */
12063 static gchar *
generate_report_filename(report_t report,report_format_t report_format,const char * custom_format,gboolean add_extension)12064 generate_report_filename (report_t report, report_format_t report_format,
12065                           const char *custom_format, gboolean add_extension)
12066 {
12067   task_t task;
12068   char *fname_format, *report_id, *creation_time, *modification_time;
12069   char *report_task_name, *rf_name;
12070   gchar *filename_base, *filename;
12071 
12072   if (custom_format && strcmp (custom_format, ""))
12073     fname_format = g_strdup (custom_format);
12074   else
12075     fname_format
12076       = sql_string ("SELECT value FROM settings"
12077                     " WHERE name"
12078                     "       = 'Report Export File Name'"
12079                     " AND " ACL_GLOBAL_OR_USER_OWNS ()
12080                     " ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
12081                     current_credentials.uuid);
12082 
12083   report_id = report_uuid (report);
12084 
12085   creation_time
12086     = sql_string ("SELECT iso_time (start_time)"
12087                   " FROM reports"
12088                   " WHERE id = %llu",
12089                   report);
12090 
12091   modification_time
12092     = sql_string ("SELECT iso_time (end_time)"
12093                   " FROM reports"
12094                   " WHERE id = %llu",
12095                   report);
12096 
12097   report_task (report, &task);
12098   report_task_name = task_name (task);
12099 
12100   rf_name = report_format ? report_format_name (report_format)
12101                           : g_strdup ("unknown");
12102 
12103   filename_base
12104     = gvm_export_file_name (fname_format,
12105                             current_credentials.username,
12106                             "report", report_id,
12107                             creation_time, modification_time,
12108                             report_task_name, rf_name);
12109 
12110   if (add_extension && report_format)
12111     {
12112       gchar *extension;
12113       extension = report_format_extension (report_format);
12114       filename = g_strdup_printf ("%s.%s", filename_base, extension);
12115       free (extension);
12116     }
12117   else
12118     filename = g_strdup (filename_base);
12119 
12120   free (fname_format);
12121   free (report_id);
12122   free (creation_time);
12123   free (modification_time);
12124   free (report_task_name);
12125   free (rf_name);
12126   g_free (filename_base);
12127 
12128   return filename;
12129 }
12130 
12131 /**
12132  * @brief Escalate an event.
12133  *
12134  * @param[in]  alert       Alert.
12135  * @param[in]  task        Task.
12136  * @param[in]  report      Report.  0 for most recent report.
12137  * @param[in]  event       Event.
12138  * @param[in]  event_data  Event data.
12139  * @param[in]  method      Method from alert.
12140  * @param[in]  condition   Condition from alert, which was met by event.
12141  * @param[in]  get         GET data for report.
12142  * @param[in]  notes_details      If notes, Whether to include details.
12143  * @param[in]  overrides_details  If overrides, Whether to include details.
12144  * @param[out] script_message  Custom error message from the script.
12145  *
12146  * @return 0 success, -1 error, -2 failed to find report format, -3 failed to
12147  *         find filter, -4 failed to find credential, -5 alert script failed.
12148  */
12149 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)12150 escalate_to_vfire (alert_t alert, task_t task, report_t report, event_t event,
12151                    const void* event_data, alert_method_t method,
12152                    alert_condition_t condition, const get_data_t *get,
12153                    int notes_details, int overrides_details,
12154                    gchar **script_message)
12155 {
12156   int ret;
12157   char *credential_id;
12158   get_data_t *alert_filter_get;
12159   filter_t filter;
12160   credential_t credential;
12161   char *base_url, *session_type, *client_id, *username, *password;
12162   char *report_formats_str;
12163   gchar **report_formats, **point;
12164   char reports_dir[] = "/tmp/gvmd_XXXXXX";
12165   gboolean is_first_report = TRUE;
12166   GString *format_names;
12167   GPtrArray *reports;
12168   char *report_zone;
12169   gchar *host_summary;
12170   iterator_t data_iterator;
12171   GTree *call_input;
12172   char *description_template;
12173   int name_offset;
12174 
12175   if ((event == EVENT_TICKET_RECEIVED)
12176       || (event == EVENT_ASSIGNED_TICKET_CHANGED)
12177       || (event == EVENT_OWNED_TICKET_CHANGED))
12178     {
12179       g_warning ("%s: Ticket events with method"
12180                  " \"Alemba vFire\" not support",
12181                  __func__);
12182       return -1;
12183     }
12184 
12185   // Get report
12186   if (report == 0)
12187     switch (sql_int64 (&report,
12188                        "SELECT max (id) FROM reports"
12189                        " WHERE task = %llu",
12190                        task))
12191       {
12192         case 0:
12193           if (report)
12194             break;
12195         case 1:        /* Too few rows in result of query. */
12196         case -1:
12197           return -1;
12198           break;
12199         default:       /* Programming error. */
12200           assert (0);
12201           return -1;
12202       }
12203 
12204   // Get report results filter and corresponding get data
12205   alert_filter_get = NULL;
12206   ret = generate_alert_filter_get (alert, get, &alert_filter_get, &filter);
12207   if (ret)
12208     return ret;
12209 
12210   // Generate reports
12211   if (mkdtemp (reports_dir) == NULL)
12212     {
12213       g_warning ("%s: mkdtemp failed", __func__);
12214       get_data_reset (alert_filter_get);
12215       g_free (alert_filter_get);
12216       return -1;
12217     }
12218 
12219   reports = g_ptr_array_new_full (0, (GDestroyNotify) alert_report_data_free);
12220   report_formats_str = alert_data (alert, "method", "report_formats");
12221   report_formats = g_strsplit_set (report_formats_str, ",", 0);
12222   point = report_formats;
12223   free (report_formats_str);
12224 
12225   report_zone = NULL;
12226   host_summary = NULL;
12227   format_names = g_string_new ("");
12228   while (*point)
12229     {
12230       gchar *report_format_id;
12231       report_format_t report_format;
12232 
12233       report_format_id = g_strstrip (*point);
12234       find_report_format_with_permission (report_format_id,
12235                                           &report_format,
12236                                           "get_report_formats");
12237 
12238       if (report_format)
12239         {
12240           alert_report_data_t *alert_report_item;
12241           size_t content_length;
12242           gchar *report_content;
12243           GError *error = NULL;
12244 
12245           alert_report_item = g_malloc0 (sizeof (alert_report_data_t));
12246 
12247           report_content = manage_report (report,
12248                                           get_delta_report
12249                                             (alert, task, report),
12250                                           alert_filter_get
12251                                             ? alert_filter_get
12252                                             : get,
12253                                           report_format,
12254                                           notes_details,
12255                                           overrides_details,
12256                                           &content_length,
12257                                           NULL /* extension */,
12258                                           &(alert_report_item->content_type),
12259                                           NULL /* term */,
12260                                           is_first_report
12261                                             ? &report_zone
12262                                             : NULL,
12263                                           is_first_report
12264                                             ? &host_summary
12265                                             : NULL);
12266           if (report_content == NULL)
12267             {
12268               g_warning ("%s: Failed to generate report", __func__);
12269 
12270               get_data_reset (alert_filter_get);
12271               g_free (alert_filter_get);
12272               alert_report_data_free (alert_report_item);
12273               g_strfreev (report_formats);
12274               return -1;
12275             }
12276 
12277           alert_report_item->report_format_name
12278             = report_format_name (report_format);
12279 
12280           if (is_first_report == FALSE)
12281             g_string_append (format_names, ", ");
12282           g_string_append (format_names,
12283                            alert_report_item->report_format_name);
12284 
12285           alert_report_item->remote_filename
12286             = generate_report_filename (report, report_format, NULL, TRUE);
12287 
12288           alert_report_item->local_filename
12289             = g_build_filename (reports_dir,
12290                                 alert_report_item->remote_filename,
12291                                 NULL);
12292 
12293           g_file_set_contents (alert_report_item->local_filename,
12294                                report_content, content_length,
12295                                &error);
12296           g_free (report_content);
12297           if (error)
12298             {
12299               g_warning ("%s: Failed to write report to %s: %s",
12300                          __func__,
12301                          alert_report_item->local_filename,
12302                          error->message);
12303 
12304               get_data_reset (alert_filter_get);
12305               g_free (alert_filter_get);
12306               alert_report_data_free (alert_report_item);
12307               g_strfreev (report_formats);
12308               return -1;
12309             }
12310           g_ptr_array_add (reports, alert_report_item);
12311           is_first_report = FALSE;
12312         }
12313 
12314       point ++;
12315     }
12316   g_strfreev (report_formats);
12317 
12318   // Find vFire credential
12319   credential_id = alert_data (alert, "method",
12320                               "vfire_credential");
12321   if (find_credential_with_permission (credential_id, &credential,
12322                                        "get_credentials"))
12323     {
12324       get_data_reset (alert_filter_get);
12325       g_free (alert_filter_get);
12326       free (credential_id);
12327       return -1;
12328     }
12329   else if (credential == 0)
12330     {
12331       get_data_reset (alert_filter_get);
12332       g_free (alert_filter_get);
12333       free (credential_id);
12334       return -4;
12335     }
12336 
12337   // vFire General data
12338   base_url = alert_data (alert, "method",
12339                           "vfire_base_url");
12340 
12341   // vFire Login data
12342   session_type = alert_data (alert, "method", "vfire_session_type");
12343   client_id = alert_data (alert, "method", "vfire_client_id");
12344 
12345   username = credential_value (credential, "username");
12346   password = credential_encrypted_value (credential, "password");
12347 
12348   // Call input data
12349   call_input = g_tree_new_full ((GCompareDataFunc) g_strcmp0,
12350                                 NULL, g_free, g_free);
12351   name_offset = strlen ("vfire_call_");
12352   init_iterator (&data_iterator,
12353                  "SELECT name, data"
12354                  " FROM alert_method_data"
12355                  " WHERE alert = %llu"
12356                  " AND name %s 'vfire_call_%%';",
12357                  alert, sql_ilike_op ());
12358 
12359   while (next (&data_iterator))
12360     {
12361       gchar *name, *value;
12362       name = g_strdup (iterator_string (&data_iterator, 0)
12363                         + name_offset);
12364       value = g_strdup (iterator_string (&data_iterator, 1));
12365 
12366       g_tree_replace (call_input, name, value);
12367     }
12368   cleanup_iterator (&data_iterator);
12369 
12370   // Special case for descriptionterm
12371   description_template = alert_data (alert, "method",
12372                                      "vfire_call_description");
12373   if (description_template == NULL)
12374     description_template = g_strdup (ALERT_VFIRE_CALL_DESCRIPTION);
12375 
12376   gchar *description;
12377   description = alert_message_print (description_template,
12378                                      event,
12379                                      event_data,
12380                                      task,
12381                                      alert,
12382                                      condition,
12383                                      format_names->str,
12384                                      filter,
12385                                      alert_filter_get
12386                                        ? alert_filter_get->filter
12387                                        : (get->filter ? get->filter : ""),
12388                                      report_zone,
12389                                      host_summary,
12390                                      NULL,
12391                                      0,
12392                                      0,
12393                                      0,
12394                                      max_attach_length);
12395 
12396   g_tree_replace (call_input,
12397                   g_strdup ("description"),
12398                   description);
12399 
12400   // Create vFire ticket
12401   ret = send_to_vfire (base_url, client_id, session_type, username,
12402                        password, reports, call_input, description_template,
12403                        script_message);
12404 
12405   // Cleanup
12406   gvm_file_remove_recurse (reports_dir);
12407 
12408   if (alert_filter_get)
12409     {
12410       get_data_reset (alert_filter_get);
12411       g_free (alert_filter_get);
12412     }
12413   free (base_url);
12414   free (session_type);
12415   free (client_id);
12416   free (username);
12417   free (password);
12418   g_ptr_array_free (reports, TRUE);
12419   g_tree_destroy (call_input);
12420   free (description_template);
12421 
12422   return ret;
12423 }
12424 
12425 /**
12426  * @brief Escalate an event.
12427  *
12428  * @param[in]  alert   Alert.
12429  * @param[in]  task        Task.
12430  * @param[in]  report      Report.  0 for most recent report.
12431  * @param[in]  event       Event.
12432  * @param[in]  event_data  Event data.
12433  * @param[in]  method      Method from alert.
12434  * @param[in]  condition   Condition from alert, which was met by event.
12435  * @param[in]  get         GET data for report.
12436  * @param[in]  notes_details      If notes, Whether to include details.
12437  * @param[in]  overrides_details  If overrides, Whether to include details.
12438  * @param[out] script_message  Custom error message from the script.
12439  *
12440  * @return 0 success, -1 error, -2 failed to find report format, -3 failed to
12441  *         find filter, -4 failed to find credential, -5 alert script failed.
12442  */
12443 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)12444 escalate_2 (alert_t alert, task_t task, report_t report, event_t event,
12445             const void* event_data, alert_method_t method,
12446             alert_condition_t condition,
12447             const get_data_t *get, int notes_details, int overrides_details,
12448             gchar **script_message)
12449 {
12450   if (script_message)
12451     *script_message = NULL;
12452 
12453   {
12454     char *name_alert;
12455     gchar *event_desc, *alert_desc;
12456 
12457     name_alert = alert_name (alert);
12458     event_desc = event_description (event, event_data, NULL);
12459     alert_desc = alert_condition_description (condition, alert);
12460     g_log ("event alert", G_LOG_LEVEL_MESSAGE,
12461            "The alert %s was triggered "
12462            "(Event: %s, Condition: %s)",
12463            name_alert,
12464            event_desc,
12465            alert_desc);
12466     free (name_alert);
12467     free (event_desc);
12468     free (alert_desc);
12469   }
12470 
12471   switch (method)
12472     {
12473       case ALERT_METHOD_EMAIL:
12474         {
12475           char *to_address;
12476           char *format_name;
12477           format_name = NULL;
12478 
12479           to_address = alert_data (alert, "method", "to_address");
12480 
12481           if (to_address)
12482             {
12483               int ret;
12484               gchar *body, *subject;
12485               char *name, *notice, *from_address;
12486               gchar *base64, *type, *extension;
12487 
12488               base64 = NULL;
12489               type = NULL;
12490               extension = NULL;
12491 
12492               from_address = alert_data (alert,
12493                                          "method",
12494                                          "from_address");
12495 
12496               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
12497                 {
12498                   ret = email_secinfo (alert, task, event, event_data, method,
12499                                        condition, to_address, from_address);
12500                   free (to_address);
12501                   free (from_address);
12502                   return ret;
12503                 }
12504 
12505               if (event == EVENT_TICKET_RECEIVED)
12506                 {
12507                   ret = email_ticket (alert, task, event, event_data, method,
12508                                       condition, to_address, from_address,
12509                                       "Ticket received");
12510                   free (to_address);
12511                   free (from_address);
12512                   return ret;
12513                 }
12514 
12515               if (event == EVENT_ASSIGNED_TICKET_CHANGED)
12516                 {
12517                   ret = email_ticket (alert, task, event, event_data, method,
12518                                       condition, to_address, from_address,
12519                                       "Assigned ticket changed");
12520                   free (to_address);
12521                   free (from_address);
12522                   return ret;
12523                 }
12524 
12525               if (event == EVENT_OWNED_TICKET_CHANGED)
12526                 {
12527                   ret = email_ticket (alert, task, event, event_data, method,
12528                                       condition, to_address, from_address,
12529                                       "Owned ticket changed");
12530                   free (to_address);
12531                   free (from_address);
12532                   return ret;
12533                 }
12534 
12535               notice = alert_data (alert, "method", "notice");
12536               name = task_name (task);
12537 
12538               if (notice && strcmp (notice, "0") == 0)
12539                 {
12540                   gchar *event_desc, *condition_desc, *report_content;
12541                   gchar *alert_subject, *message;
12542                   gchar *term, *report_zone, *host_summary;
12543                   report_format_t report_format = 0;
12544                   gsize content_length;
12545                   filter_t filter;
12546 
12547                   /* Message with inlined report. */
12548 
12549                   term = NULL;
12550                   report_zone = NULL;
12551                   host_summary = NULL;
12552                   /* report_content_for_alert always sets this, but init it
12553                    * anyway, to make it easier for the compiler to see. */
12554                   filter = 0;
12555                   ret = report_content_for_alert
12556                           (alert, 0, task, get,
12557                            "notice_report_format",
12558                            NULL,
12559                            /* TXT fallback */
12560                            "a3810a62-1f62-11e1-9219-406186ea4fc5",
12561                            notes_details, overrides_details,
12562                            &report_content, &content_length, &extension,
12563                            NULL, &term, &report_zone, &host_summary,
12564                            &report_format, &filter);
12565                   if (ret || report_content == NULL)
12566                     {
12567                       free (notice);
12568                       free (name);
12569                       free (to_address);
12570                       free (from_address);
12571                       g_free (term);
12572                       g_free (report_zone);
12573                       g_free (host_summary);
12574                       return -1;
12575                     }
12576                   format_name = report_format_name (report_format);
12577                   condition_desc = alert_condition_description (condition,
12578                                                                 alert);
12579                   event_desc = event_description (event, event_data, NULL);
12580                   subject = g_strdup_printf ("[GVM] Task '%s': %s",
12581                                              name ? name : "Internal Error",
12582                                              event_desc);
12583                   g_free (event_desc);
12584 
12585                   alert_subject = alert_data (alert, "method", "subject");
12586                   if (alert_subject && strlen (alert_subject))
12587                     {
12588                       g_free (subject);
12589                       subject = alert_subject_print (alert_subject, event,
12590                                                      event_data,
12591                                                      alert, task, 0);
12592                     }
12593                   g_free (alert_subject);
12594 
12595                   message = alert_data (alert, "method", "message");
12596                   if (message == NULL || strlen (message) == 0)
12597                     {
12598                       g_free (message);
12599                       message = g_strdup (ALERT_MESSAGE_INCLUDE);
12600                     }
12601                   body = alert_message_print (message, event, event_data,
12602                                               task, alert, condition,
12603                                               format_name, filter,
12604                                               term, report_zone,
12605                                               host_summary, report_content,
12606                                               content_length,
12607                                               content_length
12608                                               > max_content_length,
12609                                               0,
12610                                               max_content_length);
12611                   g_free (message);
12612                   g_free (report_content);
12613                   g_free (condition_desc);
12614                   g_free (term);
12615                   g_free (report_zone);
12616                   g_free (host_summary);
12617                 }
12618               else if (notice && strcmp (notice, "2") == 0)
12619                 {
12620                   gchar *event_desc, *condition_desc, *report_content;
12621                   report_format_t report_format = 0;
12622                   gsize content_length;
12623                   gchar *alert_subject, *message;
12624                   gchar *term, *report_zone, *host_summary;
12625                   filter_t filter;
12626 
12627                   /* Message with attached report. */
12628 
12629                   term = NULL;
12630                   report_zone = NULL;
12631                   host_summary = NULL;
12632                   /* report_content_for_alert always sets this, but init it
12633                    * anyway, to make it easier for the compiler to see. */
12634                   filter = 0;
12635                   ret = report_content_for_alert
12636                           (alert, 0, task, get,
12637                            "notice_attach_format",
12638                            NULL,
12639                            /* TXT fallback */
12640                            "a3810a62-1f62-11e1-9219-406186ea4fc5",
12641                            notes_details, overrides_details,
12642                            &report_content, &content_length, &extension,
12643                            &type, &term, &report_zone, &host_summary,
12644                            &report_format, &filter);
12645                   if (ret || report_content == NULL)
12646                     {
12647                       free (notice);
12648                       free (name);
12649                       free (to_address);
12650                       free (from_address);
12651                       g_free (term);
12652                       g_free (report_zone);
12653                       g_free (host_summary);
12654                       return -1;
12655                     }
12656                   format_name = report_format_name (report_format);
12657                   condition_desc = alert_condition_description (condition,
12658                                                                     alert);
12659                   event_desc = event_description (event, event_data, NULL);
12660                   subject = g_strdup_printf ("[GVM] Task '%s': %s",
12661                                              name ? name : "Internal Error",
12662                                              event_desc);
12663                   g_free (event_desc);
12664 
12665                   alert_subject = alert_data (alert, "method", "subject");
12666                   if (alert_subject && strlen (alert_subject))
12667                     {
12668                       g_free (subject);
12669                       subject = alert_subject_print (alert_subject, event,
12670                                                      event_data,
12671                                                      alert, task, 0);
12672                     }
12673                   g_free (alert_subject);
12674                   if (max_attach_length <= 0
12675                       || content_length <= max_attach_length)
12676                     base64 = g_base64_encode ((guchar*) report_content,
12677                                               content_length);
12678                   g_free (report_content);
12679                   message = alert_data (alert, "method", "message");
12680                   if (message == NULL || strlen (message) == 0)
12681                     {
12682                       g_free (message);
12683                       message = g_strdup (ALERT_MESSAGE_ATTACH);
12684                     }
12685                   body = alert_message_print (message, event, event_data,
12686                                               task, alert, condition,
12687                                               format_name, filter,
12688                                               term, report_zone,
12689                                               host_summary, NULL, 0,
12690                                               base64 == NULL,
12691                                               0,
12692                                               max_attach_length);
12693                   g_free (message);
12694                   g_free (condition_desc);
12695                   g_free (term);
12696                   g_free (report_zone);
12697                   g_free (host_summary);
12698                 }
12699               else
12700                 {
12701                   gchar *event_desc, *generic_desc, *condition_desc;
12702                   gchar *alert_subject, *message;
12703 
12704                   /* Simple notice message. */
12705 
12706                   format_name = NULL;
12707                   event_desc = event_description (event, event_data, name);
12708                   generic_desc = event_description (event, event_data, NULL);
12709                   condition_desc = alert_condition_description (condition,
12710                                                                     alert);
12711 
12712                   subject = g_strdup_printf ("[GVM] Task '%s':"
12713                                              " An event occurred",
12714                                              name);
12715 
12716                   alert_subject = alert_data (alert, "method", "subject");
12717                   if (alert_subject && strlen (alert_subject))
12718                     {
12719                       g_free (subject);
12720                       subject = alert_subject_print (alert_subject, event,
12721                                                      event_data,
12722                                                      alert, task, 0);
12723                     }
12724                   g_free (alert_subject);
12725 
12726                   message = alert_data (alert, "method", "message");
12727                   if (message && strlen (message))
12728                     body = alert_message_print (message, event, event_data,
12729                                                 task, alert, condition,
12730                                                 NULL, 0, NULL, NULL, NULL,
12731                                                 NULL, 0, 0, 0, 0);
12732                   else
12733                     body = g_strdup_printf (SIMPLE_NOTICE_FORMAT,
12734                                             event_desc,
12735                                             generic_desc,
12736                                             condition_desc);
12737                   g_free (message);
12738                   g_free (event_desc);
12739                   g_free (generic_desc);
12740                   g_free (condition_desc);
12741                 }
12742               free (notice);
12743 
12744               gchar *fname_format, *file_name;
12745               gchar *report_id, *creation_time, *modification_time;
12746               char *recipient_credential_id;
12747               credential_t recipient_credential;
12748 
12749               fname_format
12750                 = sql_string ("SELECT value FROM settings"
12751                               " WHERE name"
12752                               "       = 'Report Export File Name'"
12753                               " AND " ACL_GLOBAL_OR_USER_OWNS ()
12754                               " ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
12755                               current_credentials.uuid);
12756 
12757               report_id = report_uuid (report);
12758 
12759               creation_time
12760                 = sql_string ("SELECT iso_time (start_time)"
12761                               " FROM reports"
12762                               " WHERE id = %llu",
12763                               report);
12764 
12765               modification_time
12766                 = sql_string ("SELECT iso_time (end_time)"
12767                               " FROM reports"
12768                               " WHERE id = %llu",
12769                               report);
12770 
12771               file_name
12772                 = gvm_export_file_name (fname_format,
12773                                         current_credentials.username,
12774                                         "report", report_id,
12775                                         creation_time, modification_time,
12776                                         name, format_name);
12777 
12778               /* Get credential */
12779               recipient_credential_id = alert_data (alert, "method",
12780                                                     "recipient_credential");
12781               recipient_credential = 0;
12782               if (recipient_credential_id)
12783                 {
12784                   find_credential_with_permission (recipient_credential_id,
12785                                                   &recipient_credential, NULL);
12786                 }
12787 
12788               ret = email (to_address, from_address, subject, body, base64,
12789                            type, file_name ? file_name : "openvas-report",
12790                            extension, recipient_credential);
12791 
12792               free (extension);
12793               free (type);
12794               free (name);
12795               free (format_name);
12796               g_free (base64);
12797               free (to_address);
12798               free (from_address);
12799               g_free (subject);
12800               g_free (body);
12801               g_free (fname_format);
12802               g_free (file_name);
12803               g_free (report_id);
12804               g_free (creation_time);
12805               g_free (modification_time);
12806               free (recipient_credential_id);
12807               return ret;
12808             }
12809           return -1;
12810         }
12811       case ALERT_METHOD_HTTP_GET:
12812         {
12813           char *url;
12814 
12815           if ((event == EVENT_TICKET_RECEIVED)
12816               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
12817               || (event == EVENT_OWNED_TICKET_CHANGED))
12818             {
12819               g_warning ("%s: Ticket events with method"
12820                          " \"HTTP Get\" not support",
12821                          __func__);
12822               return -1;
12823             }
12824 
12825           if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
12826             {
12827               g_warning ("%s: Event \"%s NVTs arrived\" with method"
12828                          " \"HTTP Get\" not support",
12829                          __func__,
12830                          event == EVENT_NEW_SECINFO ? "New" : "Updated");
12831               return -1;
12832             }
12833 
12834           url = alert_data (alert, "method", "URL");
12835 
12836           if (url)
12837             {
12838               int ret, formatting;
12839               gchar *point, *end;
12840               GString *new_url;
12841 
12842               new_url = g_string_new ("");
12843               for (formatting = 0, point = url, end = (url + strlen (url));
12844                    point < end;
12845                    point++)
12846                 if (formatting)
12847                   {
12848                     switch (*point)
12849                       {
12850                         case '$':
12851                           g_string_append_c (new_url, '$');
12852                           break;
12853                         case 'c':
12854                           {
12855                             gchar *condition_desc;
12856                             condition_desc = alert_condition_description
12857                                               (condition, alert);
12858                             g_string_append (new_url, condition_desc);
12859                             g_free (condition_desc);
12860                             break;
12861                           }
12862                         case 'e':
12863                           {
12864                             gchar *event_desc;
12865                             event_desc = event_description (event, event_data,
12866                                                             NULL);
12867                             g_string_append (new_url, event_desc);
12868                             g_free (event_desc);
12869                             break;
12870                           }
12871                         case 'n':
12872                           {
12873                             char *name = task_name (task);
12874                             g_string_append (new_url, name);
12875                             free (name);
12876                             break;
12877                           }
12878                         default:
12879                           g_string_append_c (new_url, '$');
12880                           g_string_append_c (new_url, *point);
12881                           break;
12882                       }
12883                     formatting = 0;
12884                   }
12885                 else if (*point == '$')
12886                   formatting = 1;
12887                 else
12888                   g_string_append_c (new_url, *point);
12889 
12890               ret = http_get (new_url->str);
12891               g_string_free (new_url, TRUE);
12892               g_free (url);
12893               return ret;
12894             }
12895           return -1;
12896         }
12897       case ALERT_METHOD_SCP:
12898         {
12899           credential_t credential;
12900           char *credential_id;
12901           char *private_key, *password, *username, *host, *path, *known_hosts;
12902           gchar *report_content, *alert_path;
12903           gsize content_length;
12904           report_format_t report_format;
12905           int ret;
12906 
12907           if ((event == EVENT_TICKET_RECEIVED)
12908               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
12909               || (event == EVENT_OWNED_TICKET_CHANGED))
12910             {
12911               g_warning ("%s: Ticket events with method"
12912                          " \"SCP\" not support",
12913                          __func__);
12914               return -1;
12915             }
12916 
12917           if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
12918             {
12919               gchar *message;
12920 
12921               credential_id = alert_data (alert, "method", "scp_credential");
12922               if (find_credential_with_permission (credential_id,
12923                                                    &credential,
12924                                                    "get_credentials"))
12925                 {
12926                   return -1;
12927                 }
12928               else if (credential == 0)
12929                 {
12930                   return -4;
12931                 }
12932               else
12933                 {
12934                   message = new_secinfo_message (event, event_data, alert);
12935 
12936                   username = credential_value (credential, "username");
12937                   password = credential_encrypted_value (credential,
12938                                                          "password");
12939                   private_key = credential_encrypted_value (credential,
12940                                                             "private_key");
12941 
12942                   host = alert_data (alert, "method", "scp_host");
12943                   path = alert_data (alert, "method", "scp_path");
12944                   known_hosts = alert_data (alert, "method", "scp_known_hosts");
12945 
12946                   alert_path = scp_alert_path_print (path, task);
12947                   free (path);
12948 
12949                   ret = scp_to_host (username, password, private_key,
12950                                      host, alert_path, known_hosts,
12951                                      message, strlen (message),
12952                                      script_message);
12953 
12954                   g_free (message);
12955                   free (private_key);
12956                   free (password);
12957                   free (username);
12958                   free (host);
12959                   g_free (alert_path);
12960                   free (known_hosts);
12961 
12962                   return ret;
12963                 }
12964             }
12965 
12966           ret = report_content_for_alert
12967                   (alert, 0, task, get,
12968                    "scp_report_format",
12969                    NULL,
12970                    /* XML fallback. */
12971                    "a994b278-1f62-11e1-96ac-406186ea4fc5",
12972                    notes_details, overrides_details,
12973                    &report_content, &content_length, NULL,
12974                    NULL, NULL, NULL, NULL,
12975                    &report_format, NULL);
12976           if (ret || report_content == NULL)
12977             {
12978               g_warning ("%s: Empty Report", __func__);
12979               return -1;
12980             }
12981 
12982           credential_id = alert_data (alert, "method", "scp_credential");
12983           if (find_credential_with_permission (credential_id, &credential,
12984                                                "get_credentials"))
12985             {
12986               g_free (report_content);
12987               return -1;
12988             }
12989           else if (credential == 0)
12990             {
12991               g_free (report_content);
12992               return -4;
12993             }
12994           else
12995             {
12996               username = credential_value (credential, "username");
12997               password = credential_encrypted_value (credential, "password");
12998               private_key = credential_encrypted_value (credential,
12999                                                         "private_key");
13000 
13001 
13002               host = alert_data (alert, "method", "scp_host");
13003               path = alert_data (alert, "method", "scp_path");
13004               known_hosts = alert_data (alert, "method", "scp_known_hosts");
13005 
13006               alert_path = scp_alert_path_print (path, task);
13007               free (path);
13008 
13009               ret = scp_to_host (username, password, private_key,
13010                                  host, alert_path, known_hosts,
13011                                  report_content, content_length,
13012                                  script_message);
13013 
13014               free (private_key);
13015               free (password);
13016               free (username);
13017               free (host);
13018               g_free (alert_path);
13019               free (known_hosts);
13020             }
13021           g_free (report_content);
13022 
13023           return ret;
13024         }
13025       case ALERT_METHOD_SEND:
13026         {
13027           char *host, *port;
13028           gchar *report_content;
13029           gsize content_length;
13030           report_format_t report_format;
13031           int ret;
13032 
13033           if ((event == EVENT_TICKET_RECEIVED)
13034               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
13035               || (event == EVENT_OWNED_TICKET_CHANGED))
13036             {
13037               g_warning ("%s: Ticket events with method"
13038                          " \"Send\" not support",
13039                          __func__);
13040               return -1;
13041             }
13042 
13043           if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
13044             {
13045               gchar *message;
13046 
13047               message = new_secinfo_message (event, event_data, alert);
13048               host = alert_data (alert, "method", "send_host");
13049               port = alert_data (alert, "method", "send_port");
13050 
13051               g_debug ("send host: %s", host);
13052               g_debug ("send port: %s", port);
13053 
13054               ret = send_to_host (host, port, message, strlen (message),
13055                                   script_message);
13056 
13057               g_free (message);
13058               free (host);
13059               free (port);
13060 
13061               return ret;
13062             }
13063 
13064           ret = report_content_for_alert
13065                   (alert, 0, task, get,
13066                    "send_report_format",
13067                    NULL,
13068                    /* XML fallback. */
13069                    "a994b278-1f62-11e1-96ac-406186ea4fc5",
13070                    notes_details, overrides_details,
13071                    &report_content, &content_length, NULL,
13072                    NULL, NULL, NULL, NULL,
13073                    &report_format, NULL);
13074           if (ret || report_content == NULL)
13075             {
13076               g_warning ("%s: Empty Report", __func__);
13077               return -1;
13078             }
13079 
13080           host = alert_data (alert, "method", "send_host");
13081           port = alert_data (alert, "method", "send_port");
13082 
13083           g_debug ("send host: %s", host);
13084           g_debug ("send port: %s", port);
13085 
13086           ret = send_to_host (host, port, report_content, content_length,
13087                               script_message);
13088 
13089           free (host);
13090           free (port);
13091           g_free (report_content);
13092 
13093           return ret;
13094         }
13095       case ALERT_METHOD_SMB:
13096         {
13097           char *credential_id, *username, *password;
13098           char *share_path, *file_path_format;
13099           gboolean file_path_is_dir;
13100           report_format_t report_format;
13101           gchar *file_path, *report_content, *extension;
13102           gsize content_length;
13103           credential_t credential;
13104           int ret;
13105 
13106           if ((event == EVENT_TICKET_RECEIVED)
13107               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
13108               || (event == EVENT_OWNED_TICKET_CHANGED))
13109             {
13110               g_warning ("%s: Ticket events with method"
13111                          " \"SMP\" not support",
13112                          __func__);
13113               return -1;
13114             }
13115 
13116           if (report == 0)
13117             switch (sql_int64 (&report,
13118                                 "SELECT max (id) FROM reports"
13119                                 " WHERE task = %llu",
13120                                 task))
13121               {
13122                 case 0:
13123                   if (report)
13124                     break;
13125                 case 1:        /* Too few rows in result of query. */
13126                 case -1:
13127                   return -1;
13128                   break;
13129                 default:       /* Programming error. */
13130                   assert (0);
13131                   return -1;
13132               }
13133 
13134           if (task == 0 && report)
13135             {
13136               ret = report_task (report, &task);
13137               if (ret)
13138                 return ret;
13139             }
13140 
13141           credential_id = alert_data (alert, "method", "smb_credential");
13142           share_path = alert_data (alert, "method", "smb_share_path");
13143 
13144           file_path_format
13145             = sql_string ("SELECT value FROM tags"
13146                           " WHERE name = 'smb-alert:file_path'"
13147                           "   AND EXISTS"
13148                           "         (SELECT * FROM tag_resources"
13149                           "           WHERE resource_type = 'task'"
13150                           "             AND resource = %llu"
13151                           "             AND tag = tags.id)"
13152                           " ORDER BY modification_time LIMIT 1;",
13153                           task);
13154 
13155           if (file_path_format == NULL)
13156             file_path_format = alert_data (alert, "method", "smb_file_path");
13157 
13158           file_path_is_dir = (g_str_has_suffix (file_path_format, "\\")
13159                               || g_str_has_suffix (file_path_format, "/"));
13160 
13161           report_content = NULL;
13162           extension = NULL;
13163           report_format = 0;
13164 
13165           g_debug ("smb_credential: %s", credential_id);
13166           g_debug ("smb_share_path: %s", share_path);
13167           g_debug ("smb_file_path: %s (%s)",
13168                    file_path_format, file_path_is_dir ? "dir" : "file");
13169 
13170           ret = report_content_for_alert
13171                   (alert, report, task, get,
13172                    "smb_report_format",
13173                    NULL,
13174                    "a994b278-1f62-11e1-96ac-406186ea4fc5", /* XML fallback */
13175                    notes_details, overrides_details,
13176                    &report_content, &content_length, &extension,
13177                    NULL, NULL, NULL, NULL, &report_format, NULL);
13178           if (ret || report_content == NULL)
13179             {
13180               free (credential_id);
13181               free (share_path);
13182               free (file_path_format);
13183               g_free (report_content);
13184               g_free (extension);
13185               return ret ? ret : -1;
13186             }
13187 
13188           if (file_path_is_dir)
13189             {
13190               char *dirname, *filename;
13191 
13192               dirname = generate_report_filename (report, report_format,
13193                                                   file_path_format, FALSE);
13194               filename = generate_report_filename (report, report_format,
13195                                                    NULL, TRUE);
13196 
13197               file_path = g_strdup_printf ("%s\\%s", dirname, filename);
13198 
13199               free (dirname);
13200               free (filename);
13201             }
13202           else
13203             {
13204               file_path = generate_report_filename (report, report_format,
13205                                                     file_path_format, TRUE);
13206             }
13207 
13208           credential = 0;
13209           ret = find_credential_with_permission (credential_id, &credential,
13210                                                  "get_credentials");
13211           if (ret || credential == 0)
13212             {
13213               if (ret == 0)
13214                 {
13215                   g_warning ("%s: Could not find credential %s",
13216                              __func__, credential_id);
13217                 }
13218               free (credential_id);
13219               free (share_path);
13220               free (file_path);
13221               g_free (report_content);
13222               g_free (extension);
13223               return ret ? -1 : -4;
13224             }
13225 
13226           username = credential_value (credential, "username");
13227           password = credential_encrypted_value (credential, "password");
13228 
13229           ret = smb_send_to_host (password, username, share_path, file_path,
13230                                   report_content, content_length,
13231                                   script_message);
13232 
13233           g_free (username);
13234           g_free (password);
13235           free (credential_id);
13236           free (share_path);
13237           free (file_path);
13238           g_free (report_content);
13239           g_free (extension);
13240           return ret;
13241         }
13242       case ALERT_METHOD_SNMP:
13243         {
13244           char *community, *agent, *snmp_message;
13245           int ret;
13246           gchar *message;
13247 
13248           if ((event == EVENT_TICKET_RECEIVED)
13249               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
13250               || (event == EVENT_OWNED_TICKET_CHANGED))
13251             {
13252               g_warning ("%s: Ticket events with method"
13253                          " \"SNMP\" not support",
13254                          __func__);
13255               return -1;
13256             }
13257 
13258           community = alert_data (alert, "method", "snmp_community");
13259           agent = alert_data (alert, "method", "snmp_agent");
13260           snmp_message = alert_data (alert, "method", "snmp_message");
13261 
13262           g_debug ("snmp_message: %s", snmp_message);
13263           g_debug ("snmp_community: %s", community);
13264           g_debug ("snmp_agent: %s", agent);
13265 
13266           if (snmp_message)
13267             {
13268               if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
13269                 {
13270                   int count;
13271                   gchar *list, *example, *type;
13272 
13273                   type = g_strdup (event_data);
13274 
13275                   if (type && (example = strstr (type, "_example")))
13276                     example[0] = '\0';
13277 
13278                   list = new_secinfo_list (event, event_data, alert, &count);
13279                   g_free (list);
13280 
13281                   message = alert_subject_print (snmp_message, event, type,
13282                                                  alert, task, count);
13283 
13284                   g_free (type);
13285                 }
13286               else
13287                 message = alert_subject_print (snmp_message, event, event_data,
13288                                                alert, task, 0);
13289             }
13290           else
13291             {
13292               gchar *event_desc;
13293               event_desc = event_description (event, event_data, NULL);
13294               message = g_strdup_printf ("%s", event_desc);
13295               g_free (event_desc);
13296             }
13297 
13298           ret = snmp_to_host (community, agent, message, script_message);
13299 
13300           free (agent);
13301           free (community);
13302           free (snmp_message);
13303           g_free (message);
13304           return ret;
13305         }
13306       case ALERT_METHOD_SOURCEFIRE:
13307         {
13308           char *ip, *port, *pkcs12, *pkcs12_credential_id;
13309           credential_t pkcs12_credential;
13310           gchar *pkcs12_password, *report_content;
13311           gsize content_length;
13312           report_format_t report_format;
13313           int ret;
13314 
13315           if ((event == EVENT_TICKET_RECEIVED)
13316               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
13317               || (event == EVENT_OWNED_TICKET_CHANGED))
13318             {
13319               g_warning ("%s: Ticket events with method"
13320                          " \"Sourcefire\" not support",
13321                          __func__);
13322               return -1;
13323             }
13324 
13325           if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
13326             {
13327               g_warning ("%s: Event \"%s NVTs arrived\" with method"
13328                          " \"Sourcefire\" not support",
13329                          __func__,
13330                          event == EVENT_NEW_SECINFO ? "New" : "Updated");
13331               return -1;
13332             }
13333 
13334           ret = report_content_for_alert
13335                   (alert, report, task, get,
13336                    NULL,
13337                    "Sourcefire",
13338                    NULL,
13339                    notes_details, overrides_details,
13340                    &report_content, &content_length, NULL,
13341                    NULL, NULL, NULL, NULL, &report_format, NULL);
13342           if (ret || report_content == NULL)
13343             {
13344               g_warning ("%s: Empty Report", __func__);
13345               return -1;
13346             }
13347 
13348           ip = alert_data (alert, "method", "defense_center_ip");
13349           port = alert_data (alert, "method", "defense_center_port");
13350           if (port == NULL)
13351             port = g_strdup ("8307");
13352           pkcs12 = alert_data (alert, "method", "pkcs12");
13353           pkcs12_credential_id = alert_data (alert, "method",
13354                                              "pkcs12_credential");
13355 
13356           if (pkcs12_credential_id == NULL
13357               || strcmp (pkcs12_credential_id, "") == 0)
13358             {
13359               pkcs12_password = g_strdup ("");
13360             }
13361           else if (find_credential_with_permission (pkcs12_credential_id,
13362                                                &pkcs12_credential,
13363                                                "get_credentials"))
13364             {
13365               g_free (ip);
13366               g_free (port);
13367               g_free (pkcs12);
13368               g_free (pkcs12_credential_id);
13369               return -1;
13370             }
13371           else if (pkcs12_credential == 0)
13372             {
13373               g_free (ip);
13374               g_free (port);
13375               g_free (pkcs12);
13376               g_free (pkcs12_credential_id);
13377               return -4;
13378             }
13379           else
13380             {
13381               g_free (pkcs12_credential_id);
13382               pkcs12_password = credential_encrypted_value (pkcs12_credential,
13383                                                             "password");
13384             }
13385 
13386           g_debug ("  sourcefire   ip: %s", ip);
13387           g_debug ("  sourcefire port: %s", port);
13388           g_debug ("sourcefire pkcs12: %s", pkcs12);
13389 
13390           ret = send_to_sourcefire (ip, port, pkcs12, pkcs12_password,
13391                                     report_content);
13392 
13393           free (ip);
13394           g_free (port);
13395           free (pkcs12);
13396           g_free (report_content);
13397           g_free (pkcs12_password);
13398 
13399           return ret;
13400         }
13401       case ALERT_METHOD_SYSLOG:
13402         {
13403           char *submethod;
13404           gchar *message, *event_desc, *level;
13405 
13406           event_desc = event_description (event, event_data, NULL);
13407           message = g_strdup_printf ("%s: %s", event_name (event), event_desc);
13408           g_free (event_desc);
13409 
13410           submethod = alert_data (alert, "method", "submethod");
13411           level = g_strdup_printf ("event %s", submethod);
13412           g_free (submethod);
13413 
13414           g_debug ("  syslog level: %s", level);
13415           g_debug ("syslog message: %s", message);
13416 
13417           g_log (level, G_LOG_LEVEL_MESSAGE, "%s", message);
13418 
13419           g_free (level);
13420           g_free (message);
13421 
13422           return 0;
13423         }
13424       case ALERT_METHOD_TIPPINGPOINT:
13425         {
13426           int ret;
13427           report_format_t report_format;
13428           gchar *report_content, *extension;
13429           size_t content_length;
13430           char *credential_id, *username, *password, *hostname, *certificate;
13431           credential_t credential;
13432           char *tls_cert_workaround_str;
13433           int tls_cert_workaround;
13434 
13435           if ((event == EVENT_TICKET_RECEIVED)
13436               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
13437               || (event == EVENT_OWNED_TICKET_CHANGED))
13438             {
13439               g_warning ("%s: Ticket events with method"
13440                          " \"TippingPoint SMS\" not support",
13441                          __func__);
13442               return -1;
13443             }
13444 
13445           /* TLS certificate subject workaround setting */
13446           tls_cert_workaround_str
13447             = alert_data (alert, "method",
13448                           "tp_sms_tls_workaround");
13449           if (tls_cert_workaround_str)
13450             tls_cert_workaround = !!(atoi (tls_cert_workaround_str));
13451           else
13452             tls_cert_workaround = 0;
13453           g_free (tls_cert_workaround_str);
13454 
13455           /* SSL / TLS Certificate */
13456           certificate = alert_data (alert, "method",
13457                                     "tp_sms_tls_certificate");
13458 
13459           /* Hostname / IP address */
13460           hostname = alert_data (alert, "method",
13461                                  "tp_sms_hostname");
13462 
13463           /* Credential */
13464           credential_id = alert_data (alert, "method",
13465                                       "tp_sms_credential");
13466           if (find_credential_with_permission (credential_id, &credential,
13467                                                "get_credentials"))
13468             {
13469               g_free (certificate);
13470               g_free (hostname);
13471               g_free (credential_id);
13472               return -1;
13473             }
13474           else if (credential == 0)
13475             {
13476               g_free (certificate);
13477               g_free (hostname);
13478               g_free (credential_id);
13479               return -4;
13480             }
13481           else
13482             {
13483               g_free (credential_id);
13484               username = credential_value (credential, "username");
13485               password = credential_encrypted_value (credential, "password");
13486             }
13487 
13488           /* Report content */
13489           extension = NULL;
13490           ret = report_content_for_alert
13491                   (alert, report, task, get,
13492                    NULL, /* Report format not configurable */
13493                    NULL,
13494                    "a994b278-1f62-11e1-96ac-406186ea4fc5", /* XML fallback */
13495                    notes_details, overrides_details,
13496                    &report_content, &content_length, &extension,
13497                    NULL, NULL, NULL, NULL, &report_format, NULL);
13498           g_free (extension);
13499           if (ret)
13500             {
13501               g_free (username);
13502               g_free (password);
13503               g_free (hostname);
13504               g_free (certificate);
13505               return ret;
13506             }
13507 
13508           /* Send report */
13509           ret = send_to_tippingpoint (report_content, content_length,
13510                                       username, password, hostname,
13511                                       certificate, tls_cert_workaround,
13512                                       script_message);
13513 
13514           g_free (username);
13515           g_free (password);
13516           g_free (hostname);
13517           g_free (certificate);
13518           g_free (report_content);
13519           return ret;
13520         }
13521       case ALERT_METHOD_VERINICE:
13522         {
13523           credential_t credential;
13524           char *url, *username, *password;
13525           gchar *credential_id, *report_content;
13526           gsize content_length;
13527           report_format_t report_format;
13528           int ret;
13529 
13530           if ((event == EVENT_TICKET_RECEIVED)
13531               || (event == EVENT_ASSIGNED_TICKET_CHANGED)
13532               || (event == EVENT_OWNED_TICKET_CHANGED))
13533             {
13534               g_warning ("%s: Ticket events with method"
13535                          " \"Verinice\" not support",
13536                          __func__);
13537               return -1;
13538             }
13539 
13540           if (event == EVENT_NEW_SECINFO || event == EVENT_UPDATED_SECINFO)
13541             {
13542               g_warning ("%s: Event \"%s NVTs arrived\" with method"
13543                          " \"Verinice\" not support",
13544                          __func__,
13545                          event == EVENT_NEW_SECINFO ? "New" : "Updated");
13546               return -1;
13547             }
13548 
13549           ret = report_content_for_alert
13550                   (alert, report, task, get,
13551                    "verinice_server_report_format",
13552                    "Verinice ISM",
13553                    NULL,
13554                    notes_details, overrides_details,
13555                    &report_content, &content_length, NULL,
13556                    NULL, NULL, NULL, NULL, &report_format, NULL);
13557           if (ret || report_content == NULL)
13558             {
13559               g_warning ("%s: Empty Report", __func__);
13560               return -1;
13561             }
13562 
13563           url = alert_data (alert, "method", "verinice_server_url");
13564 
13565           credential_id = alert_data (alert, "method",
13566                                       "verinice_server_credential");
13567           if (find_credential_with_permission (credential_id, &credential,
13568                                                "get_credentials"))
13569             {
13570               free (url);
13571               g_free (report_content);
13572               return -1;
13573             }
13574           else if (credential == 0)
13575             {
13576               free (url);
13577               g_free (report_content);
13578               return -4;
13579             }
13580           else
13581             {
13582               username = credential_value (credential, "username");
13583               password = credential_encrypted_value (credential, "password");
13584 
13585               g_debug ("    verinice  url: %s", url);
13586               g_debug ("verinice username: %s", username);
13587 
13588               ret = send_to_verinice (url, username, password, report_content,
13589                                       content_length);
13590 
13591               free (url);
13592               g_free (username);
13593               g_free (password);
13594               g_free (report_content);
13595 
13596               return ret;
13597             }
13598         }
13599       case ALERT_METHOD_VFIRE:
13600         {
13601           int ret;
13602           ret = escalate_to_vfire (alert, task, report, event,
13603                                    event_data, method, condition,
13604                                    get, notes_details, overrides_details,
13605                                    script_message);
13606           return ret;
13607         }
13608       case ALERT_METHOD_START_TASK:
13609         {
13610           gvm_connection_t connection;
13611           char *task_id, *report_id, *owner_id, *owner_name;
13612           gmp_authenticate_info_opts_t auth_opts;
13613 
13614           /* Run the callback to fork a child connected to the Manager. */
13615 
13616           if (manage_fork_connection == NULL)
13617             {
13618               g_warning ("%s: no connection fork available", __func__);
13619               return -1;
13620             }
13621 
13622           task_id = alert_data (alert, "method", "start_task_task");
13623           if (task_id == NULL || strcmp (task_id, "") == 0)
13624             {
13625               g_warning ("%s: start_task_task missing or empty", __func__);
13626               return -1;
13627             }
13628 
13629           owner_id = sql_string ("SELECT uuid FROM users"
13630                                  " WHERE id = (SELECT owner FROM alerts"
13631                                  "              WHERE id = %llu)",
13632                                  alert);
13633           owner_name = sql_string ("SELECT name FROM users"
13634                                    " WHERE id = (SELECT owner FROM alerts"
13635                                    "              WHERE id = %llu)",
13636                                    alert);
13637 
13638           if (owner_id == NULL)
13639             {
13640               g_warning ("%s: could not find alert owner",
13641                          __func__);
13642               free (owner_id);
13643               free (owner_name);
13644               return -1;
13645             }
13646 
13647           switch (manage_fork_connection (&connection, owner_id))
13648             {
13649               case 0:
13650                 /* Child.  Break, stop task, exit. */
13651                 break;
13652 
13653               case -1:
13654                 /* Parent on error. */
13655                 g_free (task_id);
13656                 g_warning ("%s: fork failed", __func__);
13657                 return -1;
13658                 break;
13659 
13660               default:
13661                 /* Parent.  Continue with whatever lead to this escalation. */
13662                 g_free (task_id);
13663                 free (owner_id);
13664                 free (owner_name);
13665                 return 0;
13666                 break;
13667             }
13668 
13669           /* Start the task. */
13670 
13671           auth_opts = gmp_authenticate_info_opts_defaults;
13672           auth_opts.username = owner_name;
13673           auth_opts.password = "dummy";
13674           if (gmp_authenticate_info_ext_c (&connection, auth_opts))
13675             {
13676               g_free (task_id);
13677               free (owner_id);
13678               free (owner_name);
13679               gvm_connection_free (&connection);
13680               exit (EXIT_FAILURE);
13681             }
13682           if (gmp_start_task_report_c (&connection, task_id, &report_id))
13683             {
13684               g_free (task_id);
13685               free (owner_id);
13686               free (owner_name);
13687               gvm_connection_free (&connection);
13688               exit (EXIT_FAILURE);
13689             }
13690 
13691           g_free (task_id);
13692           g_free (report_id);
13693           free (owner_id);
13694           free (owner_name);
13695           gvm_connection_free (&connection);
13696           exit (EXIT_SUCCESS);
13697         }
13698       case ALERT_METHOD_ERROR:
13699       default:
13700         break;
13701     }
13702   return -1;
13703 }
13704 
13705 /**
13706  * @brief Escalate an event with preset report filtering.
13707  *
13708  * @param[in]  alert       Alert.
13709  * @param[in]  task        Task.
13710  * @param[in]  report      Report.
13711  * @param[in]  event       Event.
13712  * @param[in]  event_data  Event data.
13713  * @param[in]  method      Method from alert.
13714  * @param[in]  condition   Condition from alert, which was met by event.
13715  * @param[out] script_message  Custom error message from alert script.
13716  *
13717  * @return 0 success, -1 error, -2 failed to find report format for alert,
13718  *         -3 failed to find filter for alert, -4 failed to find credential,
13719  *         -5 alert script failed.
13720  */
13721 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)13722 escalate_1 (alert_t alert, task_t task, report_t report, event_t event,
13723             const void* event_data, alert_method_t method,
13724             alert_condition_t condition, gchar **script_message)
13725 {
13726   int ret;
13727   get_data_t get;
13728   char *results_filter;
13729 
13730   memset (&get, 0, sizeof (get_data_t));
13731   get.details = 1;
13732 
13733   results_filter = setting_filter ("Results");
13734   if (results_filter && strlen (results_filter))
13735     {
13736       get.filt_id = results_filter;
13737       get.filter = filter_term (results_filter);
13738     }
13739   else
13740     {
13741       get.filt_id = g_strdup ("0");
13742       get.filter = g_strdup_printf ("notes=1 overrides=1 sort-reverse=severity"
13743                                     " rows=%d",
13744                                     method == ALERT_METHOD_EMAIL ? 1000 : -1);
13745     }
13746 
13747   ret = escalate_2 (alert, task, report, event, event_data, method, condition,
13748                     &get, 1, 1, script_message);
13749   free (results_filter);
13750   g_free (get.filter);
13751   return ret;
13752 }
13753 
13754 /**
13755  * @brief Escalate an alert with task and event data.
13756  *
13757  * @param[in]  alert_id    Alert UUID.
13758  * @param[in]  task_id     Task UUID.
13759  * @param[in]  event       Event.
13760  * @param[in]  event_data  Event data.
13761  * @param[out] script_message  Custom error message from alert script.
13762  *
13763  * @return 0 success, 1 failed to find alert, 2 failed to find task,
13764  *         99 permission denied, -1 error, -2 failed to find report format
13765  *         for alert, -3 failed to find filter for alert, -4 failed to find
13766  *         credential for alert, -5 alert script failed.
13767  */
13768 int
manage_alert(const char * alert_id,const char * task_id,event_t event,const void * event_data,gchar ** script_message)13769 manage_alert (const char *alert_id, const char *task_id, event_t event,
13770               const void* event_data, gchar **script_message)
13771 {
13772   alert_t alert;
13773   task_t task;
13774   alert_condition_t condition;
13775   alert_method_t method;
13776 
13777   if (acl_user_may ("test_alert") == 0)
13778     return 99;
13779 
13780   if (find_alert_with_permission (alert_id, &alert, "test_alert"))
13781     return -1;
13782   if (alert == 0)
13783     return 1;
13784 
13785   if (task_id == NULL || strcmp (task_id, "0") == 0)
13786     task = 0;
13787   else
13788     {
13789       if (find_task_with_permission (task_id, &task, NULL))
13790         return -1;
13791       if (task == 0)
13792         return 2;
13793     }
13794 
13795   condition = alert_condition (alert);
13796   method = alert_method (alert);
13797   return escalate_1 (alert, task, 0, event, event_data, method, condition,
13798                      script_message);
13799 }
13800 
13801 /**
13802  * @brief Header for "New NVTs" alert message.
13803  */
13804 #define NEW_NVTS_HEADER                                                       \
13805 /* Open-Xchange (OX) AppSuite XHTML File HTML Injection Vuln...  NoneAvailable       0.0 100% */ \
13806   "Name                                                          Solution Type  Severity  QOD\n" \
13807   "------------------------------------------------------------------------------------------\n"
13808 
13809 /**
13810  * @brief Header for "New NVTs" alert message, when there's an OID.
13811  */
13812 #define NEW_NVTS_HEADER_OID                                                   \
13813 /* Open-Xchange (OX) AppSuite XHTML File HTML Injection Vuln...  NoneAvailable       0.0 100%  1.3... */ \
13814   "Name                                                          Solution Type  Severity  QOD  OID\n" \
13815   "------------------------------------------------------------------------------------------------\n"
13816 
13817 /**
13818  * @brief Header for "New CVEs" alert message.
13819  */
13820 #define NEW_CVES_HEADER                                                         \
13821 /* CVE-2014-100001       6.8  Cross-site request forgery (CSRF) vulnerability in... */ \
13822   "Name             Severity  Description\n"                                    \
13823   "--------------------------------------------------------------------------------\n"
13824 
13825 /**
13826  * @brief Header for "New CPEs" alert message.
13827  */
13828 #define NEW_CPES_HEADER                                                        \
13829 /* cpe:/a:.joomclan:com_joomclip                                 1024cms... */ \
13830   "Name                                                          Title\n"      \
13831   "------------------------------------------------------------------------------------------\n"
13832 
13833 /**
13834  * @brief Header for "New CERT-Bund Advisories" alert message.
13835  */
13836 #define NEW_CERT_BUNDS_HEADER                                                       \
13837 /* CB-K13/0849  Novell SUSE Linux Enterprise Server: Mehrere Schwachstellen... */   \
13838   "Name         Title\n"                                                            \
13839   "------------------------------------------------------------------------------------------\n"
13840 
13841 /**
13842  * @brief Header for "New DFN-CERT Advisories" alert message.
13843  */
13844 #define NEW_DFN_CERTS_HEADER                                                   \
13845 /* DFN-CERT-2008-1100  Denial of Service Schwachstelle in der... */            \
13846   "Name                Title\n"                                                \
13847   "------------------------------------------------------------------------------------------\n"
13848 
13849 /**
13850  * @brief Header for "New CERT-Bund Advisories" alert message.
13851  */
13852 #define NEW_OVAL_DEFS_HEADER                                                   \
13853 /* oval:org.mitre.oval:def:100116  libtiff Malloc Error Denial of Service */   \
13854   "OVAL ID                         Title\n"                                    \
13855   "------------------------------------------------------------------------------------------\n"
13856 
13857 /**
13858  * @brief Test an alert.
13859  *
13860  * @param[in]  alert_id    Alert UUID.
13861  * @param[out] script_message  Custom message from the alert script.
13862  *
13863  * @return 0 success, 1 failed to find alert, 2 failed to find task,
13864  *         99 permission denied, -1 error, -2 failed to find report format
13865  *         for alert, -3 failed to find filter for alert, -4 failed to find
13866  *         credential for alert, -5 alert script failed.
13867  */
13868 int
manage_test_alert(const char * alert_id,gchar ** script_message)13869 manage_test_alert (const char *alert_id, gchar **script_message)
13870 {
13871   int ret;
13872   alert_t alert;
13873   task_t task;
13874   report_t report;
13875   result_t result;
13876   char *task_id, *report_id;
13877   time_t now;
13878   char now_string[26];
13879   gchar *clean;
13880 
13881   if (acl_user_may ("test_alert") == 0)
13882     return 99;
13883 
13884   if (find_alert_with_permission (alert_id, &alert, "test_alert"))
13885     return -1;
13886   if (alert == 0)
13887     return 1;
13888 
13889   if (alert_event (alert) == EVENT_NEW_SECINFO
13890       || alert_event (alert) == EVENT_UPDATED_SECINFO)
13891     {
13892       char *alert_event_data;
13893       gchar *type;
13894 
13895       alert_event_data = alert_data (alert, "event", "secinfo_type");
13896       type = g_strdup_printf ("%s_example", alert_event_data ?: "NVT");
13897       free (alert_event_data);
13898 
13899       if (alert_event (alert) == EVENT_NEW_SECINFO)
13900         ret = manage_alert (alert_id, "0", EVENT_NEW_SECINFO, (void*) type,
13901                             script_message);
13902       else
13903         ret = manage_alert (alert_id, "0", EVENT_UPDATED_SECINFO, (void*) type,
13904                             script_message);
13905 
13906       g_free (type);
13907 
13908       return ret;
13909     }
13910 
13911   task = make_task (g_strdup ("Temporary Task for Alert"),
13912                     g_strdup (""),
13913                     0,  /* Exclude from assets. */
13914                     0); /* Skip event and log. */
13915 
13916   report_id = gvm_uuid_make ();
13917   if (report_id == NULL)
13918     return -1;
13919   task_uuid (task, &task_id);
13920   report = make_report (task, report_id, TASK_STATUS_DONE);
13921   result = make_result (task, "127.0.0.1", "localhost", "telnet (23/tcp)",
13922                         "1.3.6.1.4.1.25623.1.0.10330", "Alarm",
13923                         "A telnet server seems to be running on this port.",
13924                         NULL);
13925   now = time (NULL);
13926   if (strlen (ctime_r (&now, now_string)) == 0)
13927     {
13928       ret = -1;
13929       goto exit;
13930     }
13931   clean = g_strdup (now_string);
13932   if (clean[strlen (clean) - 1] == '\n')
13933     clean[strlen (clean) - 1] = '\0';
13934   set_task_start_time_ctime (task, g_strdup (clean));
13935   set_scan_start_time_ctime (report, g_strdup (clean));
13936   set_scan_host_start_time_ctime (report, "127.0.0.1", clean);
13937   if (result)
13938     report_add_result (report, result);
13939   set_scan_host_end_time_ctime (report, "127.0.0.1", clean);
13940   set_scan_end_time_ctime (report, clean);
13941   g_free (clean);
13942   ret = manage_alert (alert_id,
13943                       task_id,
13944                       EVENT_TASK_RUN_STATUS_CHANGED,
13945                       (void*) TASK_STATUS_DONE,
13946                       script_message);
13947  exit:
13948   /* No one should be running this task, so we don't worry about the lock.  We
13949    * could guarantee that no one runs the task, but this is a very rare case. */
13950   delete_task (task, 1);
13951   free (task_id);
13952   free (report_id);
13953   return ret;
13954 }
13955 
13956 /**
13957  * @brief Return whether an event applies to a task and an alert.
13958  *
13959  * @param[in]  event           Event.
13960  * @param[in]  event_data      Event data.
13961  * @param[in]  event_resource  Event resource.
13962  * @param[in]  alert           Alert.
13963  *
13964  * @return 1 if event applies, else 0.
13965  */
13966 static int
event_applies(event_t event,const void * event_data,resource_t event_resource,alert_t alert)13967 event_applies (event_t event, const void *event_data,
13968                resource_t event_resource, alert_t alert)
13969 {
13970   switch (event)
13971     {
13972       case EVENT_TASK_RUN_STATUS_CHANGED:
13973         {
13974           int ret;
13975           char *alert_event_data;
13976 
13977           if (alert_applies_to_task (alert, event_resource) == 0)
13978             return 0;
13979 
13980           alert_event_data = alert_data (alert, "event", "status");
13981           if (alert_event_data == NULL)
13982             return 0;
13983           ret = (task_run_status (event_resource) == (task_status_t) event_data)
13984                 && (strcmp (alert_event_data,
13985                             run_status_name_internal ((task_status_t)
13986                                                       event_data))
13987                     == 0);
13988           free (alert_event_data);
13989           return ret;
13990         }
13991       case EVENT_NEW_SECINFO:
13992       case EVENT_UPDATED_SECINFO:
13993         {
13994           char *alert_event_data;
13995 
13996           alert_event_data = alert_data (alert, "event", "secinfo_type");
13997           if (alert_event_data == NULL)
13998             return 0;
13999           if (strcasecmp (alert_event_data, event_data) == 0)
14000             return 1;
14001           return 0;
14002         }
14003       case EVENT_TICKET_RECEIVED:
14004       case EVENT_ASSIGNED_TICKET_CHANGED:
14005         return ticket_assigned_to (event_resource) == alert_owner (alert);
14006       case EVENT_OWNED_TICKET_CHANGED:
14007         return ticket_owner (event_resource) == alert_owner (alert);
14008       default:
14009         return 0;
14010     }
14011 }
14012 
14013 /**
14014  * @brief Return the SecInfo count.
14015  *
14016  * @param[in]  alert      Alert.
14017  * @param[in]  filter_id  Condition filter id.
14018  *
14019  * @return 1 if met, else 0.
14020  */
14021 static time_t
alert_secinfo_count(alert_t alert,char * filter_id)14022 alert_secinfo_count (alert_t alert, char *filter_id)
14023 {
14024   get_data_t get;
14025   int db_count, uuid_was_null;
14026   event_t event;
14027   gboolean get_modified;
14028   time_t feed_version_epoch;
14029   char *secinfo_type;
14030 
14031   event = alert_event (alert);
14032   get_modified = (event == EVENT_UPDATED_SECINFO);
14033 
14034   if (current_credentials.uuid == NULL)
14035     {
14036       current_credentials.uuid = alert_owner_uuid (alert);
14037       uuid_was_null = 1;
14038     }
14039   else
14040     uuid_was_null = 0;
14041 
14042   memset (&get, '\0', sizeof (get));
14043   if (filter_id && strlen (filter_id) && strcmp (filter_id, "0"))
14044     get.filt_id = filter_id;
14045 
14046   secinfo_type = alert_data (alert, "event", "secinfo_type");
14047 
14048   if (strcmp (secinfo_type, "nvt") == 0)
14049     {
14050       feed_version_epoch = nvts_feed_version_epoch ();
14051       db_count = nvt_info_count_after (&get,
14052                                        feed_version_epoch,
14053                                        get_modified);
14054     }
14055   else if (strcmp (secinfo_type, "cert_bund_adv") == 0
14056            || strcmp (secinfo_type, "dfn_cert_adv") == 0)
14057     {
14058       feed_version_epoch = cert_check_time ();
14059       db_count = secinfo_count_after (&get,
14060                                       secinfo_type,
14061                                       feed_version_epoch,
14062                                       get_modified);
14063     }
14064   else // assume SCAP data
14065     {
14066       feed_version_epoch = scap_check_time ();
14067       db_count = secinfo_count_after (&get,
14068                                       secinfo_type,
14069                                       feed_version_epoch,
14070                                       get_modified);
14071     }
14072 
14073   if (uuid_was_null)
14074     {
14075       free (current_credentials.uuid);
14076       current_credentials.uuid = NULL;
14077     }
14078 
14079   return db_count;
14080 }
14081 
14082 /**
14083  * @brief Return whether the condition of an alert is met by a task.
14084  *
14085  * @param[in]  task       Task.
14086  * @param[in]  report     Report.
14087  * @param[in]  alert      Alert.
14088  * @param[in]  condition  Condition.
14089  *
14090  * @return 1 if met, else 0.
14091  */
14092 static int
condition_met(task_t task,report_t report,alert_t alert,alert_condition_t condition)14093 condition_met (task_t task, report_t report, alert_t alert,
14094                alert_condition_t condition)
14095 {
14096   switch (condition)
14097     {
14098       case ALERT_CONDITION_ALWAYS:
14099         return 1;
14100         break;
14101       case ALERT_CONDITION_FILTER_COUNT_AT_LEAST:
14102         {
14103           char *filter_id, *count_string;
14104           report_t last_report;
14105           int holes, infos, logs, warnings, false_positives;
14106           int count;
14107           double severity;
14108 
14109           /* True if there are at least the given number of results matched by
14110            * the given filter in the last finished report. */
14111 
14112           filter_id = alert_data (alert, "condition", "filter_id");
14113           count_string = alert_data (alert, "condition", "count");
14114           if (count_string)
14115             {
14116               count = atoi (count_string);
14117               free (count_string);
14118             }
14119           else
14120             count = 0;
14121 
14122           if (task == 0)
14123             {
14124               int db_count;
14125 
14126               /* SecInfo event. */
14127 
14128               db_count = alert_secinfo_count (alert, filter_id);
14129 
14130               if (db_count >= count)
14131                 return 1;
14132               break;
14133             }
14134 
14135           if (report)
14136             last_report = report;
14137           else
14138             {
14139               last_report = 0;
14140               if (task_last_report (task, &last_report))
14141                 g_warning ("%s: failed to get last report", __func__);
14142             }
14143 
14144           g_debug ("%s: last_report: %llu", __func__, last_report);
14145           if (last_report)
14146             {
14147               int db_count;
14148               get_data_t get;
14149               memset (&get, 0, sizeof (get_data_t));
14150               get.type = "result";
14151               get.filt_id = filter_id;
14152               report_counts_id (last_report, &holes, &infos, &logs,
14153                                 &warnings, &false_positives, &severity,
14154                                 &get, NULL);
14155 
14156               db_count = holes + infos + logs + warnings
14157                          + false_positives;
14158 
14159               g_debug ("%s: count: %i vs %i", __func__, db_count, count);
14160               if (db_count >= count)
14161                 {
14162                   g_free (filter_id);
14163                   return 1;
14164                 }
14165             }
14166           g_free (filter_id);
14167           break;
14168         }
14169       case ALERT_CONDITION_FILTER_COUNT_CHANGED:
14170         {
14171           char *direction, *filter_id, *count_string;
14172           report_t last_report;
14173           int holes, infos, logs, warnings, false_positives;
14174           int count;
14175           double severity;
14176 
14177           /* True if the number of results matched by the given filter in the
14178            * last finished report changed in the given direction with respect
14179            * to the second last finished report. */
14180 
14181           direction = alert_data (alert, "condition", "direction");
14182           filter_id = alert_data (alert, "condition", "filter_id");
14183           count_string = alert_data (alert, "condition", "count");
14184           if (count_string)
14185             {
14186               count = atoi (count_string);
14187               free (count_string);
14188             }
14189           else
14190             count = 0;
14191 
14192           if (report)
14193             last_report = report;
14194           else
14195             {
14196               last_report = 0;
14197               if (task_last_report (task, &last_report))
14198                 g_warning ("%s: failed to get last report", __func__);
14199             }
14200 
14201           if (last_report)
14202             {
14203               report_t second_last_report;
14204               int last_count;
14205               get_data_t get;
14206               get.type = "result";
14207               get.filt_id = filter_id;
14208 
14209               report_counts_id (last_report, &holes, &infos, &logs,
14210                                 &warnings, &false_positives, &severity,
14211                                 &get, NULL);
14212               last_count = holes + infos + logs + warnings
14213                             + false_positives;
14214 
14215               second_last_report = 0;
14216               if (task_second_last_report (task, &second_last_report))
14217                 g_warning ("%s: failed to get second last report", __func__);
14218 
14219               if (second_last_report)
14220                 {
14221                   int cmp, second_last_count;
14222 
14223                   report_counts_id (second_last_report, &holes, &infos,
14224                                     &logs, &warnings, &false_positives,
14225                                     &severity, &get, NULL);
14226                   second_last_count = holes + infos + logs + warnings
14227                                       + false_positives;
14228 
14229                   cmp = last_count - second_last_count;
14230                   g_debug ("cmp: %i (vs %i)", cmp, count);
14231                   g_debug ("direction: %s", direction);
14232                   g_debug ("last_count: %i", last_count);
14233                   g_debug ("second_last_count: %i", second_last_count);
14234                   if (count < 0)
14235                     {
14236                       count = -count;
14237                       if (direction == NULL
14238                           || strcasecmp (direction, "increased") == 0)
14239                         {
14240                           free (direction);
14241                           direction = g_strdup ("decreased");
14242                         }
14243                       else if (strcasecmp (direction, "decreased") == 0)
14244                         {
14245                           free (direction);
14246                           direction = g_strdup ("increased");
14247                         }
14248                     }
14249                   if (direction == NULL)
14250                     {
14251                       /* Same as "increased". */
14252                       if (cmp >= count)
14253                         return 1;
14254                     }
14255                   else if (((strcasecmp (direction, "changed") == 0)
14256                             && (abs (cmp) >= count))
14257                            || ((strcasecmp (direction, "increased") == 0)
14258                                && (cmp >= count))
14259                            || ((strcasecmp (direction, "decreased") == 0)
14260                                && (cmp <= count)))
14261                     {
14262                       free (direction);
14263                       free (filter_id);
14264                       return 1;
14265                     }
14266                 }
14267               else
14268                 {
14269                   g_debug ("direction: %s", direction);
14270                   g_debug ("last_count: %i", last_count);
14271                   g_debug ("second_last_count NULL");
14272                   if (((strcasecmp (direction, "changed") == 0)
14273                        || (strcasecmp (direction, "increased") == 0))
14274                       && (last_count > 0))
14275                     {
14276                       free (direction);
14277                       free (filter_id);
14278                       return 1;
14279                     }
14280                 }
14281             }
14282           free (direction);
14283           free (filter_id);
14284           break;
14285         }
14286       case ALERT_CONDITION_SEVERITY_AT_LEAST:
14287         {
14288           char *condition_severity_str;
14289 
14290           /* True if the threat level of the last finished report is at
14291            * least the given level. */
14292 
14293           condition_severity_str = alert_data (alert, "condition", "severity");
14294 
14295           if (condition_severity_str)
14296             {
14297               double condition_severity_dbl, task_severity_dbl;
14298 
14299               condition_severity_dbl = g_ascii_strtod (condition_severity_str,
14300                                                        0);
14301               task_severity_dbl = task_severity_double (task, 1,
14302                                                         MIN_QOD_DEFAULT, 0);
14303 
14304               if (task_severity_dbl >= condition_severity_dbl)
14305                 {
14306                   free (condition_severity_str);
14307                   return 1;
14308                 }
14309             }
14310           free (condition_severity_str);
14311           break;
14312         }
14313       case ALERT_CONDITION_SEVERITY_CHANGED:
14314         {
14315           char *direction;
14316           double last_severity, second_last_severity;
14317 
14318           /* True if the threat level of the last finished report changed
14319            * in the given direction with respect to the second last finished
14320            * report. */
14321 
14322           direction = alert_data (alert, "condition", "direction");
14323           last_severity = task_severity_double (task, 1,
14324                                                 MIN_QOD_DEFAULT, 0);
14325           second_last_severity = task_severity_double (task, 1,
14326                                                        MIN_QOD_DEFAULT, 1);
14327           if (direction
14328               && last_severity > SEVERITY_MISSING
14329               && second_last_severity > SEVERITY_MISSING)
14330             {
14331               double cmp = last_severity - second_last_severity;
14332               g_debug ("cmp: %f", cmp);
14333               g_debug ("direction: %s", direction);
14334               g_debug ("last_level: %1.1f", last_severity);
14335               g_debug ("second_last_level: %1.1f", second_last_severity);
14336               if (((strcasecmp (direction, "changed") == 0) && cmp)
14337                   || ((strcasecmp (direction, "increased") == 0) && (cmp > 0))
14338                   || ((strcasecmp (direction, "decreased") == 0) && (cmp < 0)))
14339                 {
14340                   free (direction);
14341                   return 1;
14342                 }
14343             }
14344           else if (direction
14345                    && last_severity > SEVERITY_MISSING)
14346             {
14347               g_debug ("direction: %s", direction);
14348               g_debug ("last_level: %1.1f", last_severity);
14349               g_debug ("second_last_level NULL");
14350               if ((strcasecmp (direction, "changed") == 0)
14351                   || (strcasecmp (direction, "increased") == 0))
14352                 {
14353                   free (direction);
14354                   return 1;
14355                 }
14356             }
14357           free (direction);
14358           break;
14359         }
14360       default:
14361         break;
14362     }
14363   return 0;
14364 }
14365 
14366 /**
14367  * @brief Produce an event.
14368  *
14369  * @param[in]  event       Event.
14370  * @param[in]  event_data  Event type specific details.
14371  * @param[in]  resource_1  Event type specific resource 1.  For example,
14372  *                         a task for EVENT_TASK_RUN_STATUS_CHANGED.
14373  * @param[in]  resource_2  Event type specific resource 2.
14374  */
14375 void
event(event_t event,void * event_data,resource_t resource_1,resource_t resource_2)14376 event (event_t event, void* event_data, resource_t resource_1,
14377        resource_t resource_2)
14378 {
14379   iterator_t alerts;
14380   GArray *alerts_triggered;
14381   guint index;
14382 
14383   g_debug ("   EVENT %i on resource %llu", event, resource_1);
14384 
14385   alerts_triggered = g_array_new (TRUE, TRUE, sizeof (alert_t));
14386 
14387   if ((event == EVENT_TASK_RUN_STATUS_CHANGED)
14388       && (((task_status_t) event_data) == TASK_STATUS_DONE))
14389     check_tickets (resource_1);
14390 
14391   init_event_alert_iterator (&alerts, event);
14392   while (next (&alerts))
14393     {
14394       alert_t alert = event_alert_iterator_alert (&alerts);
14395       if (event_alert_iterator_active (&alerts)
14396           && event_applies (event, event_data, resource_1, alert))
14397         {
14398           alert_condition_t condition;
14399 
14400           condition = alert_condition (alert);
14401           if (condition_met (resource_1, resource_2, alert, condition))
14402             g_array_append_val (alerts_triggered, alert);
14403         }
14404     }
14405   cleanup_iterator (&alerts);
14406 
14407   /* Run the alerts outside the iterator, because they may take some
14408    * time and the iterator would prevent update processes (GMP MODIFY_XXX,
14409    * CREATE_XXX, ...) from locking the database. */
14410   index = alerts_triggered->len;
14411   while (index--)
14412     {
14413       alert_t alert;
14414       alert_condition_t condition;
14415 
14416       alert = g_array_index (alerts_triggered, alert_t, index);
14417       condition = alert_condition (alert);
14418       escalate_1 (alert,
14419                   resource_1,
14420                   resource_2,
14421                   event,
14422                   event_data,
14423                   alert_method (alert),
14424                   condition,
14425                   NULL);
14426     }
14427 
14428   g_array_free (alerts_triggered, TRUE);
14429 }
14430 
14431 /**
14432  * @brief Initialise an alert task iterator.
14433  *
14434  * Iterate over all tasks that use the alert.
14435  *
14436  * @param[in]  iterator   Iterator.
14437  * @param[in]  alert  Alert.
14438  * @param[in]  ascending  Whether to sort ascending or descending.
14439  */
14440 void
init_alert_task_iterator(iterator_t * iterator,alert_t alert,int ascending)14441 init_alert_task_iterator (iterator_t* iterator, alert_t alert,
14442                               int ascending)
14443 {
14444   gchar *available, *with_clause;
14445   get_data_t get;
14446   array_t *permissions;
14447 
14448   assert (alert);
14449 
14450   get.trash = 0;
14451   permissions = make_array ();
14452   array_add (permissions, g_strdup ("get_tasks"));
14453   available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
14454                                &with_clause);
14455   array_free (permissions);
14456 
14457   init_iterator (iterator,
14458                  "%s"
14459                  " SELECT tasks.name, tasks.uuid, %s FROM tasks, task_alerts"
14460                  " WHERE tasks.id = task_alerts.task"
14461                  " AND task_alerts.alert = %llu"
14462                  " AND hidden = 0"
14463                  " ORDER BY tasks.name %s;",
14464                  with_clause ? with_clause : "",
14465                  available,
14466                  alert,
14467                  ascending ? "ASC" : "DESC");
14468 
14469   g_free (with_clause);
14470   g_free (available);
14471 }
14472 
14473 /**
14474  * @brief Return the name from an alert task iterator.
14475  *
14476  * @param[in]  iterator  Iterator.
14477  *
14478  * @return Name of the task or NULL if iteration is complete.
14479  */
14480 const char*
alert_task_iterator_name(iterator_t * iterator)14481 alert_task_iterator_name (iterator_t* iterator)
14482 {
14483   const char *ret;
14484   if (iterator->done) return NULL;
14485   ret = iterator_string (iterator, 0);
14486   return ret;
14487 }
14488 
14489 /**
14490  * @brief Return the uuid from an alert task iterator.
14491  *
14492  * @param[in]  iterator  Iterator.
14493  *
14494  * @return UUID of the task or NULL if iteration is complete.
14495  */
14496 const char*
alert_task_iterator_uuid(iterator_t * iterator)14497 alert_task_iterator_uuid (iterator_t* iterator)
14498 {
14499   const char *ret;
14500   if (iterator->done) return NULL;
14501   ret = iterator_string (iterator, 1);
14502   return ret;
14503 }
14504 
14505 /**
14506  * @brief Get the read permission status from a GET iterator.
14507  *
14508  * @param[in]  iterator  Iterator.
14509  *
14510  * @return 1 if may read, else 0.
14511  */
14512 int
alert_task_iterator_readable(iterator_t * iterator)14513 alert_task_iterator_readable (iterator_t* iterator)
14514 {
14515   if (iterator->done) return 0;
14516   return iterator_int (iterator, 2);
14517 }
14518 
14519 
14520 /* Task functions. */
14521 
14522 /**
14523  * @brief  Generate an extra WHERE clause for selecting tasks
14524  *
14525  * @param[in]  trash        Whether to get tasks from the trashcan.
14526  * @param[in]  usage_type   The usage type to limit the selection to.
14527  *
14528  * @return Newly allocated where clause string.
14529  */
14530 static gchar *
tasks_extra_where(int trash,const char * usage_type)14531 tasks_extra_where (int trash, const char *usage_type)
14532 {
14533   gchar *extra_where = NULL;
14534   if (usage_type && strcmp (usage_type, ""))
14535     {
14536       gchar *quoted_usage_type;
14537       quoted_usage_type = sql_quote (usage_type);
14538       extra_where = g_strdup_printf (" AND hidden = %d"
14539                                      " AND usage_type = '%s'",
14540                                      trash ? 2 : 0,
14541                                      quoted_usage_type);
14542       g_free (quoted_usage_type);
14543     }
14544   else
14545     extra_where = g_strdup_printf (" AND hidden = %d",
14546                                    trash ? 2 : 0);
14547 
14548   return extra_where;
14549 }
14550 
14551 /**
14552  * @brief Append value to field of task.
14553  *
14554  * @param[in]  task   Task.
14555  * @param[in]  field  Field.
14556  * @param[in]  value  Value.
14557  */
14558 static void
append_to_task_string(task_t task,const char * field,const char * value)14559 append_to_task_string (task_t task, const char* field, const char* value)
14560 {
14561   char* current;
14562   gchar* quote;
14563   current = sql_string ("SELECT %s FROM tasks WHERE id = %llu;",
14564                         field,
14565                         task);
14566   if (current)
14567     {
14568       gchar* new = g_strconcat ((const gchar*) current, value, NULL);
14569       free (current);
14570       quote = sql_nquote (new, strlen (new));
14571       g_free (new);
14572     }
14573   else
14574     quote = sql_nquote (value, strlen (value));
14575   sql ("UPDATE tasks SET %s = '%s', modification_time = m_now ()"
14576        " WHERE id = %llu;",
14577        field,
14578        quote,
14579        task);
14580   g_free (quote);
14581 }
14582 
14583 /**
14584  * @brief Filter columns for task iterator.
14585  */
14586 #define TASK_ITERATOR_FILTER_COLUMNS                                          \
14587  { GET_ITERATOR_FILTER_COLUMNS, "status", "total", "first_report",            \
14588    "last_report", "threat", "trend", "severity", "schedule", "next_due",      \
14589    "first", "last", "false_positive", "log", "low", "medium", "high",         \
14590    "hosts", "result_hosts", "fp_per_host", "log_per_host", "low_per_host",    \
14591    "medium_per_host", "high_per_host", "target", "usage_type", NULL }
14592 
14593 /**
14594  * @brief Task iterator columns.
14595  */
14596 #define TASK_ITERATOR_COLUMNS_INNER                                         \
14597    { "run_status", NULL, KEYWORD_TYPE_INTEGER },                            \
14598    {                                                                        \
14599      "(SELECT count(*) FROM reports"                                        \
14600      " WHERE task = tasks.id)",                                             \
14601      "total",                                                               \
14602      KEYWORD_TYPE_INTEGER                                                   \
14603    },                                                                       \
14604    {                                                                        \
14605      "(SELECT uuid FROM reports WHERE task = tasks.id"                      \
14606      /* TODO 1 == TASK_STATUS_DONE */                                       \
14607      " AND scan_run_status = 1"                                             \
14608      " ORDER BY date ASC LIMIT 1)",                                         \
14609      "first_report",                                                        \
14610      KEYWORD_TYPE_STRING                                                    \
14611    },                                                                       \
14612    { "run_status_name (run_status)", "status", KEYWORD_TYPE_STRING },       \
14613    {                                                                        \
14614      "(SELECT uuid FROM reports WHERE task = tasks.id"                      \
14615      /* TODO 1 == TASK_STATUS_DONE */                                       \
14616      " AND scan_run_status = 1"                                             \
14617      " ORDER BY date DESC LIMIT 1)",                                        \
14618      "last_report",                                                         \
14619      KEYWORD_TYPE_STRING                                                    \
14620    },                                                                       \
14621    {                                                                        \
14622      "(SELECT count(*) FROM reports"                                        \
14623      /* TODO 1 == TASK_STATUS_DONE */                                       \
14624      " WHERE task = tasks.id AND scan_run_status = 1)",                     \
14625      NULL,                                                                  \
14626      KEYWORD_TYPE_INTEGER                                                   \
14627    },                                                                       \
14628    { "hosts_ordering", NULL, KEYWORD_TYPE_STRING },                         \
14629    { "scanner", NULL, KEYWORD_TYPE_INTEGER },                               \
14630    { "usage_type", NULL, KEYWORD_TYPE_STRING }
14631 
14632 /**
14633  * @brief Task iterator WHERE columns.
14634  */
14635 #define TASK_ITERATOR_WHERE_COLUMNS_INNER                                    \
14636    {                                                                         \
14637      "task_threat_level (id, opts.override, opts.min_qod)",                  \
14638      "threat",                                                               \
14639      KEYWORD_TYPE_STRING                                                     \
14640    },                                                                        \
14641    {                                                                         \
14642      "task_trend (id, opts.override, opts.min_qod)",                         \
14643      "trend",                                                                \
14644      KEYWORD_TYPE_STRING                                                     \
14645    },                                                                        \
14646    {                                                                         \
14647      "task_severity (id, opts.override, opts.min_qod)",                      \
14648      "severity",                                                             \
14649      KEYWORD_TYPE_DOUBLE                                                     \
14650    },                                                                        \
14651    {                                                                         \
14652      "(SELECT schedules.name FROM schedules"                                 \
14653      " WHERE schedules.id = tasks.schedule)",                                \
14654      "schedule",                                                             \
14655      KEYWORD_TYPE_STRING                                                     \
14656    },                                                                        \
14657    {                                                                         \
14658      "(CASE WHEN schedule_next_time IS NULL"                                 \
14659      " THEN -1"                                                              \
14660      " WHEN schedule_next_time = 0 AND tasks.schedule > 0"                   \
14661      " THEN (SELECT first_time"                                              \
14662      "       FROM schedules"                                                 \
14663      "       WHERE schedules.id = tasks.schedule)"                           \
14664      " ELSE schedule_next_time"                                              \
14665      " END)",                                                                \
14666      "next_due",                                                             \
14667      KEYWORD_TYPE_INTEGER                                                    \
14668    },                                                                        \
14669    {                                                                         \
14670      "(SELECT date FROM reports WHERE task = tasks.id"                       \
14671      /* TODO 1 == TASK_STATUS_DONE */                                        \
14672      " AND scan_run_status = 1"                                              \
14673      " ORDER BY date ASC LIMIT 1)",                                          \
14674      "first",                                                                \
14675      KEYWORD_TYPE_INTEGER                                                    \
14676    },                                                                        \
14677    {                                                                         \
14678      "(SELECT date FROM reports WHERE task = tasks.id"                       \
14679      /* TODO 1 == TASK_STATUS_DONE */                                        \
14680      " AND scan_run_status = 1"                                              \
14681      " ORDER BY date DESC LIMIT 1)",                                         \
14682      "last",                                                                 \
14683      KEYWORD_TYPE_INTEGER                                                    \
14684    },                                                                        \
14685    {                                                                         \
14686      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14687      " report_severity_count (task_last_report (id),"                        \
14688      "                        opts.override, opts.min_qod,"                  \
14689      "                        'False Positive')"                             \
14690      " END",                                                                 \
14691      "false_positive",                                                       \
14692      KEYWORD_TYPE_INTEGER                                                    \
14693    },                                                                        \
14694    {                                                                         \
14695      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14696      " report_severity_count (task_last_report (id),"                        \
14697      "                        opts.override, opts.min_qod, 'Log')"           \
14698      " END",                                                                 \
14699      "log",                                                                  \
14700      KEYWORD_TYPE_INTEGER                                                    \
14701    },                                                                        \
14702    {                                                                         \
14703      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14704      " report_severity_count (task_last_report (id),"                        \
14705      "                        opts.override, opts.min_qod, 'Low')"           \
14706      " END",                                                                 \
14707      "low",                                                                  \
14708      KEYWORD_TYPE_INTEGER                                                    \
14709    },                                                                        \
14710    {                                                                         \
14711      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14712      " report_severity_count (task_last_report (id),"                        \
14713      "                        opts.override, opts.min_qod, 'Medium')"        \
14714      " END",                                                                 \
14715      "medium",                                                               \
14716      KEYWORD_TYPE_INTEGER                                                    \
14717    },                                                                        \
14718    {                                                                         \
14719      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14720      " report_severity_count (task_last_report (id),"                        \
14721      "                        opts.override, opts.min_qod, 'High')"          \
14722      " END",                                                                 \
14723      "high",                                                                 \
14724      KEYWORD_TYPE_INTEGER                                                    \
14725    },                                                                        \
14726    {                                                                         \
14727      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14728      " report_host_count (task_last_report (id))"                            \
14729      " END",                                                                 \
14730      "hosts",                                                                \
14731      KEYWORD_TYPE_INTEGER                                                    \
14732    },                                                                        \
14733    {                                                                         \
14734      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14735      " report_result_host_count (task_last_report (id), opts.min_qod)"       \
14736      " END",                                                                 \
14737      "result_hosts",                                                         \
14738      KEYWORD_TYPE_INTEGER                                                    \
14739    },                                                                        \
14740    {                                                                         \
14741      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14742      " coalesce (report_severity_count (task_last_report (id),"              \
14743      "                                 opts.override, opts.min_qod,"         \
14744      "                                 'False Positive') * 1.0"              \
14745      "            / nullif (report_result_host_count (task_last_report (id),"\
14746      "                                                opts.min_qod), 0),"    \
14747      "          0)"                                                          \
14748      " END",                                                                 \
14749      "fp_per_host",                                                          \
14750      KEYWORD_TYPE_INTEGER                                                    \
14751    },                                                                        \
14752    {                                                                         \
14753      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14754      " coalesce (report_severity_count (task_last_report (id),"              \
14755      "                                 opts.override, opts.min_qod,"         \
14756      "                                 'Log') * 1.0"                         \
14757      "            / nullif (report_result_host_count (task_last_report (id),"\
14758      "                                                opts.min_qod), 0),"    \
14759      "          0)"                                                          \
14760      " END",                                                                 \
14761      "log_per_host",                                                         \
14762      KEYWORD_TYPE_INTEGER                                                    \
14763    },                                                                        \
14764    {                                                                         \
14765      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14766      " coalesce (report_severity_count (task_last_report (id),"              \
14767      "                                 opts.override, opts.min_qod,"         \
14768      "                                 'Low') * 1.0"                         \
14769      "            / nullif (report_result_host_count (task_last_report (id),"\
14770      "                                                opts.min_qod), 0),"    \
14771      "          0)"                                                          \
14772      " END",                                                                 \
14773      "low_per_host",                                                         \
14774      KEYWORD_TYPE_INTEGER                                                    \
14775    },                                                                        \
14776    {                                                                         \
14777      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14778      " coalesce (report_severity_count (task_last_report (id),"              \
14779      "                                 opts.override, opts.min_qod,"         \
14780      "                                 'Medium') * 1.0"                      \
14781      "            / nullif (report_result_host_count (task_last_report (id),"\
14782      "                                                opts.min_qod), 0),"    \
14783      "          0)"                                                          \
14784      " END",                                                                 \
14785      "medium_per_host",                                                      \
14786      KEYWORD_TYPE_INTEGER                                                    \
14787    },                                                                        \
14788    {                                                                         \
14789      "CASE WHEN target IS null OR opts.ignore_severity != 0 THEN 0 ELSE"     \
14790      " coalesce (report_severity_count (task_last_report (id),"              \
14791      "                                 opts.override, opts.min_qod,"         \
14792      "                                 'High') * 1.0"                        \
14793      "            / nullif (report_result_host_count (task_last_report (id),"\
14794      "                                                opts.min_qod), 0),"    \
14795      "          0)"                                                          \
14796      " END",                                                                 \
14797      "high_per_host",                                                        \
14798      KEYWORD_TYPE_INTEGER                                                    \
14799    },                                                                        \
14800    {                                                                         \
14801      "(SELECT name FROM targets WHERE id = target)",                         \
14802      "target",                                                               \
14803      KEYWORD_TYPE_STRING                                                     \
14804    }
14805 
14806 /**
14807  * @brief Task iterator WHERE columns.
14808  */
14809 #define TASK_ITERATOR_WHERE_COLUMNS                                         \
14810  {                                                                          \
14811    TASK_ITERATOR_WHERE_COLUMNS_INNER,                                       \
14812    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                     \
14813  }
14814 
14815 /**
14816  * @brief Task iterator columns.
14817  */
14818 #define TASK_ITERATOR_COLUMNS                                               \
14819  {                                                                          \
14820    GET_ITERATOR_COLUMNS (tasks),                                            \
14821    TASK_ITERATOR_COLUMNS_INNER,                                             \
14822    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                     \
14823  }
14824 
14825 /**
14826  * @brief Task iterator minimal columns.
14827  */
14828 #define TASK_ITERATOR_COLUMNS_MIN                                           \
14829  {                                                                          \
14830    GET_ITERATOR_COLUMNS (tasks),                                            \
14831    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                     \
14832  }
14833 
14834 /**
14835  * @brief Task iterator minimal WHERE columns.
14836  */
14837 #define TASK_ITERATOR_WHERE_COLUMNS_MIN                                     \
14838  {                                                                          \
14839    TASK_ITERATOR_COLUMNS_INNER,                                             \
14840    TASK_ITERATOR_WHERE_COLUMNS_INNER,                                       \
14841    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                     \
14842  }
14843 
14844 /**
14845  * @brief Generate the extra_tables string for a task iterator.
14846  *
14847  * @param[in]  override  Whether to apply overrides.
14848  * @param[in]  min_qod   Minimum QoD of results to count.
14849  * @param[in]  ignore_severity  Whether to ignore severity data.
14850  * @return Newly allocated string with the extra_tables clause.
14851  */
14852 static gchar*
task_iterator_opts_table(int override,int min_qod,int ignore_severity)14853 task_iterator_opts_table (int override, int min_qod, int ignore_severity)
14854 {
14855   return g_strdup_printf (", (SELECT"
14856                           "   %d AS override,"
14857                           "   %d AS min_qod,"
14858                           "   %d AS ignore_severity)"
14859                           "  AS opts",
14860                           override,
14861                           min_qod,
14862                           ignore_severity);
14863 }
14864 
14865 /**
14866  * @brief Initialise a task iterator, limited to current user's tasks.
14867  *
14868  * @param[in]  iterator    Task iterator.
14869  * @param[in]  trash       Whether to iterate over trashcan tasks.
14870  * @param[in]  ignore_severity  Whether to ignore severity data.
14871  */
14872 static void
init_user_task_iterator(iterator_t * iterator,int trash,int ignore_severity)14873 init_user_task_iterator (iterator_t* iterator, int trash, int ignore_severity)
14874 {
14875   static column_t select_columns[] = TASK_ITERATOR_COLUMNS;
14876   gchar *extra_tables;
14877   gchar *columns;
14878 
14879   extra_tables = task_iterator_opts_table (0, MIN_QOD_DEFAULT,
14880                                            ignore_severity);
14881 
14882   columns = columns_build_select (select_columns);
14883 
14884   init_iterator (iterator,
14885                  "SELECT %s"
14886                  " FROM tasks%s"
14887                  " WHERE " ACL_USER_OWNS ()
14888                  "%s;",
14889                  columns,
14890                  extra_tables,
14891                  current_credentials.uuid,
14892                  trash ? " AND hidden = 2" : " AND hidden < 2");
14893 
14894   g_free (extra_tables);
14895   g_free (columns);
14896 }
14897 
14898 /**
14899  * @brief Initialise a task iterator.
14900  *
14901  * @param[in]  iterator    Task iterator.
14902  * @param[in]  get         GET data.
14903  *
14904  * @return 0 success, 1 failed to find target, 2 failed to find filter,
14905  *         -1 error.
14906  */
14907 int
init_task_iterator(iterator_t * iterator,const get_data_t * get)14908 init_task_iterator (iterator_t* iterator, const get_data_t *get)
14909 {
14910   static const char *filter_columns[] = TASK_ITERATOR_FILTER_COLUMNS;
14911   static column_t columns[] = TASK_ITERATOR_COLUMNS;
14912   static column_t where_columns[] = TASK_ITERATOR_WHERE_COLUMNS;
14913   static column_t columns_min[] = TASK_ITERATOR_COLUMNS_MIN;
14914   static column_t where_columns_min[] = TASK_ITERATOR_WHERE_COLUMNS_MIN;
14915   char *filter;
14916   int overrides, min_qod;
14917   const char *usage_type;
14918   gchar *extra_tables, *extra_where;
14919   int ret;
14920 
14921   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
14922     {
14923       filter = filter_term (get->filt_id);
14924       if (filter == NULL)
14925         return 2;
14926     }
14927   else
14928     filter = NULL;
14929 
14930   overrides = filter_term_apply_overrides (filter ? filter : get->filter);
14931   min_qod = filter_term_min_qod (filter ? filter : get->filter);
14932 
14933   free (filter);
14934 
14935   extra_tables = task_iterator_opts_table (overrides, min_qod, 0);
14936   usage_type = get_data_get_extra (get, "usage_type");
14937   extra_where = tasks_extra_where (get->trash, usage_type);
14938 
14939   ret = init_get_iterator2 (iterator,
14940                             "task",
14941                             get,
14942                             /* SELECT columns. */
14943                             get->minimal ? columns_min : columns,
14944                             get->minimal ? columns_min : columns,
14945                             /* Filterable columns not in SELECT columns. */
14946                             get->minimal ? where_columns_min : where_columns,
14947                             get->minimal ? where_columns_min : where_columns,
14948                             filter_columns,
14949                             0,
14950                             extra_tables,
14951                             extra_where,
14952                             NULL,
14953                             current_credentials.uuid ? TRUE : FALSE,
14954                             FALSE,
14955                             NULL);
14956 
14957   g_free (extra_tables);
14958   g_free (extra_where);
14959   return ret;
14960 }
14961 
14962 /**
14963  * @brief Get the run status from a task iterator.
14964  *
14965  * @param[in]  iterator  Iterator.
14966  *
14967  * @return Task run status.
14968  */
14969 task_status_t
task_iterator_run_status(iterator_t * iterator)14970 task_iterator_run_status (iterator_t* iterator)
14971 {
14972   task_status_t ret;
14973   if (iterator->done) return TASK_STATUS_INTERRUPTED;
14974   ret = (unsigned int) iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT);
14975   return ret;
14976 }
14977 
14978 /**
14979  * @brief Get the number of reports of a task iterator.
14980  *
14981  * @param[in]  iterator  Iterator.
14982  *
14983  * @return Count of all task reports.
14984  */
14985 int
task_iterator_total_reports(iterator_t * iterator)14986 task_iterator_total_reports (iterator_t* iterator)
14987 {
14988   if (iterator->done) return 0;
14989   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
14990 }
14991 
14992 /**
14993  * @brief Get the first report UUID from a task iterator.
14994  *
14995  * @param[in]  iterator  Iterator.
14996  *
14997  * @return First report UUID.
14998  */
14999 const char *
task_iterator_first_report(iterator_t * iterator)15000 task_iterator_first_report (iterator_t* iterator)
15001 {
15002   if (iterator->done) return 0;
15003   return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
15004 }
15005 
15006 /**
15007  * @brief Get the run status name from a task iterator.
15008  *
15009  * @param[in]  iterator  Iterator.
15010  *
15011  * @return Task run status name.
15012  */
15013 const char *
task_iterator_run_status_name(iterator_t * iterator)15014 task_iterator_run_status_name (iterator_t* iterator)
15015 {
15016   if (iterator->done) return 0;
15017   return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
15018 }
15019 
15020 /**
15021  * @brief Get the last report UUID from a task iterator.
15022  *
15023  * @param[in]  iterator  Iterator.
15024  *
15025  * @return Last report UUID.
15026  */
15027 const char *
task_iterator_last_report(iterator_t * iterator)15028 task_iterator_last_report (iterator_t* iterator)
15029 {
15030   if (iterator->done) return 0;
15031   return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
15032 }
15033 
15034 /**
15035  * @brief Get the number of reports of a task iterator.
15036  *
15037  * @param[in]  iterator  Iterator.
15038  *
15039  * @return Count of all task reports.
15040  */
15041 int
task_iterator_finished_reports(iterator_t * iterator)15042 task_iterator_finished_reports (iterator_t *iterator)
15043 {
15044   if (iterator->done) return 0;
15045   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
15046 }
15047 
15048 /**
15049  * @brief Get the hosts ordering value from a task iterator.
15050  *
15051  * @param[in]  iterator  Iterator.
15052  *
15053  * @return Task hosts ordering.
15054  */
15055 const char *
task_iterator_hosts_ordering(iterator_t * iterator)15056 task_iterator_hosts_ordering (iterator_t* iterator)
15057 {
15058   if (iterator->done) return 0;
15059   return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
15060 }
15061 
15062 /**
15063  * @brief Get the UUID of task scanner from a task iterator.
15064  *
15065  * @param[in]  iterator  Iterator.
15066  *
15067  * @return Task scanner if found, NULL otherwise.
15068  */
15069 scanner_t
task_iterator_scanner(iterator_t * iterator)15070 task_iterator_scanner (iterator_t* iterator)
15071 {
15072   if (iterator->done) return 0;
15073   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
15074 }
15075 
15076 /**
15077  * @brief Get the UUID of task scanner from a task iterator.
15078  *
15079  * @param[in]  iterator  Iterator.
15080  *
15081  * @return Task scanner if found, NULL otherwise.
15082  */
15083 const char *
task_iterator_usage_type(iterator_t * iterator)15084 task_iterator_usage_type (iterator_t* iterator)
15085 {
15086   if (iterator->done) return 0;
15087   return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
15088 }
15089 
15090 /**
15091  * @brief Return whether a task is in use by a task.
15092  *
15093  * @param[in]  task  Task.
15094  *
15095  * @return 0.
15096  */
15097 int
task_in_use(task_t task)15098 task_in_use (task_t task)
15099 {
15100   task_status_t status;
15101   status = task_run_status (task);
15102   return status == TASK_STATUS_DELETE_REQUESTED
15103          || status == TASK_STATUS_DELETE_WAITING
15104          || status == TASK_STATUS_DELETE_ULTIMATE_REQUESTED
15105          || status == TASK_STATUS_DELETE_ULTIMATE_WAITING
15106          || status == TASK_STATUS_REQUESTED
15107          || status == TASK_STATUS_RUNNING
15108          || status == TASK_STATUS_QUEUED
15109          || status == TASK_STATUS_STOP_REQUESTED
15110          || status == TASK_STATUS_STOP_WAITING;
15111 }
15112 
15113 /**
15114  * @brief Return whether a trashcan task is referenced by a task.
15115  *
15116  * @param[in]  task  Task.
15117  *
15118  * @return 0.
15119  */
15120 int
trash_task_in_use(task_t task)15121 trash_task_in_use (task_t task)
15122 {
15123   return task_in_use (task);
15124 }
15125 
15126 /**
15127  * @brief Return whether a task is an Alterable Task.
15128  *
15129  * @param[in]  task  Task.
15130  *
15131  * @return 1 if Alterable, else 0.
15132  */
15133 int
task_alterable(task_t task)15134 task_alterable (task_t task)
15135 {
15136   return sql_int ("SELECT alterable FROM tasks"
15137                   " WHERE id = %llu",
15138                   task);
15139 }
15140 
15141 /**
15142  * @brief Return whether a task is writable.
15143  *
15144  * @param[in]  task  Task.
15145  *
15146  * @return 1 if writable, else 0.
15147  */
15148 int
task_writable(task_t task)15149 task_writable (task_t task)
15150 {
15151   return sql_int ("SELECT hidden = 0 FROM tasks"
15152                   " WHERE id = %llu",
15153                   task);
15154 }
15155 
15156 /**
15157  * @brief Return whether a trashcan task is writable.
15158  *
15159  * @param[in]  task  Task.
15160  *
15161  * @return 1 if writable, else 0.
15162  */
15163 int
trash_task_writable(task_t task)15164 trash_task_writable (task_t task)
15165 {
15166   return sql_int ("SELECT hidden = 2 FROM tasks"
15167                   " WHERE id = %llu",
15168                   task);
15169 }
15170 
15171 /**
15172  * @brief Get the average duration of all finished reports of a task.
15173  *
15174  * @param[in]  task  Task.
15175  *
15176  * @return Average scan duration in seconds.
15177  */
15178 int
task_average_scan_duration(task_t task)15179 task_average_scan_duration (task_t task)
15180 {
15181   return sql_int ("SELECT avg (end_time - start_time) FROM reports"
15182                   " WHERE task = %llu"
15183                   "   AND scan_run_status = %d"
15184                   "   AND coalesce (end_time, 0) != 0"
15185                   "   AND coalesce (start_time, 0) != 0;",
15186                   task, TASK_STATUS_DONE);
15187 }
15188 
15189 /**
15190  * @brief Initialize the manage library: open db.
15191  *
15192  * @param[in]  database          Location of manage database.
15193  *
15194  * @return 1 if open already, else 0.
15195  */
15196 static int
init_manage_open_db(const db_conn_info_t * database)15197 init_manage_open_db (const db_conn_info_t *database)
15198 {
15199   if (sql_is_open ())
15200     return 1;
15201 
15202   /* Open the database. */
15203   if (sql_open (database))
15204     {
15205       g_warning ("%s: sql_open failed", __func__);
15206       abort ();
15207     }
15208 
15209   /* Ensure the user session variables always exists. */
15210   sql ("SET SESSION \"gvmd.user.id\" = 0;");
15211   sql ("SET SESSION \"gvmd.tz_override\" = '';");
15212 
15213   /* Attach the SCAP and CERT databases. */
15214   manage_attach_databases ();
15215 
15216   return 0;
15217 }
15218 
15219 /**
15220  * @brief Initialize the manage library: define SQL functions.
15221  */
15222 static void
init_manage_create_functions()15223 init_manage_create_functions ()
15224 {
15225   lockfile_t lockfile;
15226 
15227   /* Lock to avoid an error return from Postgres when multiple processes
15228    * create a function at the same time. */
15229   if (lockfile_lock (&lockfile, "gvm-create-functions"))
15230     abort ();
15231   if (manage_create_sql_functions ())
15232     {
15233       lockfile_unlock (&lockfile);
15234       g_warning ("%s: failed to create functions", __func__);
15235       abort ();
15236     }
15237   lockfile_unlock (&lockfile);
15238 }
15239 
15240 /**
15241  * @brief Initialize the manage library for a process.
15242  *
15243  * Open the SQL database, attach secondary databases, and define functions.
15244  *
15245  * @param[in]  database          Location of manage database.
15246  */
15247 void
init_manage_process(const db_conn_info_t * database)15248 init_manage_process (const db_conn_info_t *database)
15249 {
15250   if (init_manage_open_db (database))
15251     return;
15252   init_manage_create_functions ();
15253 }
15254 
15255 /**
15256  * @brief Reinitialize the manage library for a process.
15257  *
15258  * This is mandatory after a fork, to not carry open databases around (refer
15259  * to database documentation).
15260  */
15261 void
reinit_manage_process()15262 reinit_manage_process ()
15263 {
15264   cleanup_manage_process (FALSE);
15265   init_manage_process (&gvmd_db_conn_info);
15266 }
15267 
15268 /**
15269  * @brief Update the memory cache of NVTs.
15270  *
15271  * @param[in]  nvt  NVT.
15272  *
15273  * @return NVTi if found, else NULL.
15274  */
15275 nvti_t *
lookup_nvti(const gchar * nvt)15276 lookup_nvti (const gchar *nvt)
15277 {
15278   return nvtis_lookup (nvti_cache, nvt);
15279 }
15280 
15281 /**
15282  * @brief Update the memory cache of NVTs.
15283  */
15284 static void
update_nvti_cache()15285 update_nvti_cache ()
15286 {
15287   iterator_t nvts;
15288 
15289   nvtis_free (nvti_cache);
15290 
15291   nvti_cache = nvtis_new ();
15292 
15293   /* Because there are many NVTs and many refs it's slow to query the refs
15294    * for each NVT.  So this query gets the NVTs and their refs at the same
15295    * time.
15296    *
15297    * The NVT data is duplicated in the result of the query when there are
15298    * multiple refs for an NVT, but the loop below uses nvtis_lookup to
15299    * check if we've already seen the NVT.  This also means we don't have
15300    * to sort the data by NVT, which would make the query too slow. */
15301   init_iterator (&nvts,
15302                  "SELECT nvts.oid, vt_refs.type, vt_refs.ref_id,"
15303                  "       vt_refs.ref_text"
15304                  " FROM nvts"
15305                  " LEFT OUTER JOIN vt_refs ON nvts.oid = vt_refs.vt_oid;");
15306 
15307   while (next (&nvts))
15308     {
15309       nvti_t *nvti;
15310 
15311       nvti = nvtis_lookup (nvti_cache, iterator_string (&nvts, 0));
15312       if (nvti == NULL)
15313         {
15314           nvti = nvti_new ();
15315           nvti_set_oid (nvti, iterator_string (&nvts, 0));
15316 
15317           nvtis_add (nvti_cache, nvti);
15318         }
15319 
15320       if (iterator_null (&nvts, 2))
15321         /* No refs. */;
15322       else
15323         nvti_add_vtref (nvti,
15324                         vtref_new (iterator_string (&nvts, 1),
15325                                    iterator_string (&nvts, 2),
15326                                    iterator_string (&nvts, 3)));
15327     }
15328 
15329   cleanup_iterator (&nvts);
15330 }
15331 
15332 /**
15333  * @brief Update the memory cache of NVTs, if this has been requested.
15334  *
15335  * @return 0 success, 1 failed to get lock, -1 error.
15336  */
15337 int
manage_update_nvti_cache()15338 manage_update_nvti_cache ()
15339 {
15340   int ret;
15341 
15342   ret = sql_begin_immediate_giveup ();
15343   if (ret)
15344     return ret;
15345   if (sql_int ("SELECT value FROM %s.meta"
15346                " WHERE name = 'update_nvti_cache';",
15347                sql_schema ()))
15348     {
15349       update_nvti_cache ();
15350       sql ("UPDATE %s.meta SET value = 0 WHERE name = 'update_nvti_cache';",
15351            sql_schema ());
15352     }
15353   sql_commit ();
15354   return 0;
15355 }
15356 
15357 /**
15358  * @brief Ensure the predefined scanner exists.
15359  *
15360  * @return 0 if success, -1 if error.
15361  */
15362 static int
check_db_scanners()15363 check_db_scanners ()
15364 {
15365   if (sql_int ("SELECT count(*) FROM scanners WHERE uuid = '%s';",
15366                SCANNER_UUID_DEFAULT) == 0)
15367     {
15368       sql ("INSERT INTO scanners"
15369            " (uuid, owner, name, host, port, type, ca_pub, credential,"
15370            "  creation_time, modification_time)"
15371            " VALUES ('" SCANNER_UUID_DEFAULT "', NULL, 'OpenVAS Default',"
15372            " '%s', 0, %d, NULL, NULL, m_now (),"
15373            " m_now ());",
15374            OPENVAS_DEFAULT_SOCKET,
15375            SCANNER_TYPE_OPENVAS);
15376     }
15377 
15378   if (sql_int ("SELECT count(*) FROM scanners WHERE uuid = '%s';",
15379                SCANNER_UUID_CVE) == 0)
15380     sql ("INSERT INTO scanners"
15381          " (uuid, owner, name, host, port, type, ca_pub, credential,"
15382          "  creation_time, modification_time)"
15383          " VALUES ('" SCANNER_UUID_CVE "', NULL, 'CVE',"
15384          " '', 0, %d, NULL, NULL, m_now (), m_now ());",
15385          SCANNER_TYPE_CVE);
15386 
15387   return 0;
15388 }
15389 
15390 /**
15391  * @brief Initialize the default settings.
15392  *
15393  * Ensure all the default manager settings exist.
15394  */
15395 static void
check_db_settings()15396 check_db_settings ()
15397 {
15398   if (sql_int ("SELECT count(*) FROM settings"
15399                " WHERE uuid = '6765549a-934e-11e3-b358-406186ea4fc5'"
15400                " AND " ACL_IS_GLOBAL () ";")
15401       == 0)
15402     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15403          " VALUES"
15404          " ('6765549a-934e-11e3-b358-406186ea4fc5', NULL,"
15405          "  'User Interface Language',"
15406          "  'Preferred language to be used in client user interfaces.',"
15407          "  'Browser Language');");
15408 
15409   if (sql_int ("SELECT count(*) FROM settings"
15410                " WHERE uuid = '" SETTING_UUID_ROWS_PER_PAGE "'"
15411                " AND " ACL_IS_GLOBAL () ";")
15412       == 0)
15413     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15414          " VALUES"
15415          " ('" SETTING_UUID_ROWS_PER_PAGE "', NULL, 'Rows Per Page',"
15416          "  'The default number of rows displayed in any listing.',"
15417          "  10);");
15418 
15419   if (sql_int ("SELECT count(*) FROM settings"
15420                " WHERE uuid = '" SETTING_UUID_MAX_ROWS_PER_PAGE "'"
15421                " AND " ACL_IS_GLOBAL () ";")
15422       == 0)
15423     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15424          " VALUES"
15425          " ('" SETTING_UUID_MAX_ROWS_PER_PAGE "', NULL, 'Max Rows Per Page',"
15426          "  'The default maximum number of rows displayed in any listing.',"
15427          "  1000);");
15428 
15429   if (sql_int ("SELECT count(*) FROM settings"
15430                " WHERE uuid = '77ec2444-e7f2-4a80-a59b-f4237782d93f'"
15431                " AND " ACL_IS_GLOBAL () ";")
15432       == 0)
15433     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15434          " VALUES"
15435          " ('77ec2444-e7f2-4a80-a59b-f4237782d93f', NULL, 'Dynamic Severity',"
15436          "  'Whether to use dynamic severity scores by default.',"
15437          "  '0');");
15438 
15439   if (sql_int ("SELECT count(*) FROM settings"
15440                " WHERE uuid = '578a1c14-e2dc-45ef-a591-89d31391d007'"
15441                " AND " ACL_IS_GLOBAL () ";")
15442       == 0)
15443     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15444          " VALUES"
15445          " ('578a1c14-e2dc-45ef-a591-89d31391d007', NULL, 'Auto-Refresh',"
15446          "  'The delay between automatic page refreshs in seconds.',"
15447          "  '0');");
15448 
15449   if (sql_int ("SELECT count(*) FROM settings"
15450                " WHERE uuid = 'a6ac88c5-729c-41ba-ac0a-deea4a3441f2'"
15451                " AND " ACL_IS_GLOBAL () ";")
15452       == 0)
15453     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15454          " VALUES"
15455          " ('a6ac88c5-729c-41ba-ac0a-deea4a3441f2', NULL,"
15456          "  'Details Export File Name',"
15457          "  'File name format string for the export of resource details.',"
15458          "  '%%T-%%U');");
15459 
15460   if (sql_int ("SELECT count(*) FROM settings"
15461                " WHERE uuid = '0872a6ed-4f85-48c5-ac3f-a5ef5e006745'"
15462                " AND " ACL_IS_GLOBAL () ";")
15463       == 0)
15464     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15465          " VALUES"
15466          " ('0872a6ed-4f85-48c5-ac3f-a5ef5e006745', NULL,"
15467          "  'List Export File Name',"
15468          "  'File name format string for the export of resource lists.',"
15469          "  '%%T-%%D');");
15470 
15471   if (sql_int ("SELECT count(*) FROM settings"
15472                " WHERE uuid = 'e1a2ae0b-736e-4484-b029-330c9e15b900'"
15473                " AND " ACL_IS_GLOBAL () ";")
15474       == 0)
15475     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15476          " VALUES"
15477          " ('e1a2ae0b-736e-4484-b029-330c9e15b900', NULL,"
15478          "  'Report Export File Name',"
15479          "  'File name format string for the export of reports.',"
15480          "  '%%T-%%U');");
15481 
15482   if (sql_int ("SELECT count(*) FROM settings"
15483                " WHERE uuid = '7eda49c5-096c-4bef-b1ab-d080d87300df'"
15484                " AND " ACL_IS_GLOBAL () ";")
15485       == 0)
15486     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15487          " VALUES"
15488          " ('7eda49c5-096c-4bef-b1ab-d080d87300df', NULL,"
15489          "  'Default Severity',"
15490          "  'Severity to use if none is specified or available from SecInfo.',"
15491          "  '10.0');");
15492 
15493   if (sql_int ("SELECT count(*) FROM settings"
15494                " WHERE uuid = 'a09285b0-2d47-49b6-a4ef-946ee71f1d5c'"
15495                " AND " ACL_IS_GLOBAL () ";")
15496       == 0)
15497     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15498          " VALUES"
15499          " ('a09285b0-2d47-49b6-a4ef-946ee71f1d5c', NULL,"
15500          "  'Auto Cache Rebuild',"
15501          "  'Whether to rebuild report caches on changes affecting severity.',"
15502          "  '1');");
15503 
15504   if (sql_int ("SELECT count(*) FROM settings"
15505                " WHERE uuid = '" SETTING_UUID_LSC_DEB_MAINTAINER "'"
15506                " AND " ACL_IS_GLOBAL () ";")
15507       == 0)
15508     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15509          " VALUES"
15510          " ('" SETTING_UUID_LSC_DEB_MAINTAINER "', NULL,"
15511          "  'Debian LSC Package Maintainer',"
15512          "  'Maintainer email address used in generated Debian LSC packages.',"
15513          "  '');");
15514 
15515   if (sql_int ("SELECT count(*) FROM settings"
15516                " WHERE uuid = '" SETTING_UUID_FEED_IMPORT_ROLES "'"
15517                " AND " ACL_IS_GLOBAL () ";")
15518       == 0)
15519     sql ("INSERT into settings (uuid, owner, name, comment, value)"
15520          " VALUES"
15521          " ('" SETTING_UUID_FEED_IMPORT_ROLES "', NULL,"
15522          "  'Feed Import Roles',"
15523          "  'Roles given access to new resources from feed.',"
15524          "  '" ROLE_UUID_ADMIN "," ROLE_UUID_USER "');");
15525 }
15526 
15527 /**
15528  * @brief Add command permission to role.
15529  *
15530  * Caller must ensure args are SQL escaped.
15531  *
15532  * @param[in]  role_id     Role.
15533  * @param[in]  permission  Permission.
15534  */
15535 static void
add_role_permission(const gchar * role_id,const gchar * permission)15536 add_role_permission (const gchar *role_id, const gchar *permission)
15537 {
15538   if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
15539                "               WHERE owner IS NULL"
15540                "               AND name = lower ('%s')"
15541                "               AND resource_type = ''"
15542                "               AND resource = 0"
15543                "               AND subject_type = 'role'"
15544                "               AND subject = (SELECT id FROM roles"
15545                "                              WHERE uuid = '%s'));",
15546                permission,
15547                role_id) == 0)
15548     sql ("INSERT INTO permissions"
15549          " (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
15550          "  resource_location, subject_type, subject, subject_location,"
15551          "  creation_time, modification_time)"
15552          " VALUES"
15553          " (make_uuid (), NULL, lower ('%s'), '', '',"
15554          "  0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
15555          "  (SELECT id FROM roles WHERE uuid = '%s'),"
15556          "  " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
15557          permission,
15558          role_id);
15559 }
15560 
15561 /**
15562  * @brief Add resource permission to role.
15563  *
15564  * Caller must ensure args are SQL escaped.
15565  *
15566  * @param[in]  role_id      Role ID.
15567  * @param[in]  permission   Permission.
15568  * @param[in]  type         Resource type.
15569  * @param[in]  resource_id  Resource ID.
15570  */
15571 void
add_role_permission_resource(const gchar * role_id,const gchar * permission,const gchar * type,const gchar * resource_id)15572 add_role_permission_resource (const gchar *role_id, const gchar *permission,
15573                               const gchar *type, const gchar *resource_id)
15574 {
15575   if (sql_int ("SELECT EXISTS (SELECT * FROM permissions"
15576                "               WHERE owner IS NULL"
15577                "               AND name = lower ('%s')"
15578                "               AND resource_type = '%s'"
15579                "               AND resource = (SELECT id FROM %ss"
15580                "                               WHERE uuid = '%s')"
15581                "               AND subject_type = 'role'"
15582                "               AND subject = (SELECT id FROM roles"
15583                "                              WHERE uuid = '%s'));",
15584                permission,
15585                type,
15586                type,
15587                resource_id,
15588                role_id)
15589       == 0)
15590     sql ("INSERT INTO permissions"
15591          " (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
15592          "  resource_location, subject_type, subject, subject_location,"
15593          "  creation_time, modification_time)"
15594          " VALUES"
15595          " (make_uuid (), NULL, lower ('%s'), '', '%s',"
15596          "  (SELECT id FROM %ss WHERE uuid = '%s'), '%s',"
15597          "  " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
15598          "  (SELECT id FROM roles WHERE uuid = '%s'),"
15599          "  " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
15600          permission,
15601          type,
15602          type,
15603          resource_id,
15604          resource_id,
15605          role_id);
15606 }
15607 
15608 /**
15609  * @brief Ensure that the databases are the right versions.
15610  *
15611  * @return 0 success, -1 error, -2 database is wrong version.
15612  */
15613 static int
check_db_versions()15614 check_db_versions ()
15615 {
15616   char *database_version;
15617   int scap_db_version, cert_db_version;
15618 
15619   database_version = sql_string ("SELECT value FROM %s.meta"
15620                                  " WHERE name = 'database_version';",
15621                                  sql_schema ());
15622 
15623   if (database_version)
15624     {
15625       if (strcmp (database_version,
15626                   G_STRINGIFY (GVMD_DATABASE_VERSION)))
15627         {
15628           g_message ("%s: database version of database: %s",
15629                      __func__,
15630                      database_version);
15631           g_message ("%s: database version supported by manager: %s",
15632                      __func__,
15633                      G_STRINGIFY (GVMD_DATABASE_VERSION));
15634           g_free (database_version);
15635           return -2;
15636         }
15637       g_free (database_version);
15638     }
15639 
15640   /* Check SCAP database version. */
15641 
15642   scap_db_version = manage_scap_db_version ();
15643   if (scap_db_version == -1)
15644     g_message ("No SCAP database found");
15645   else if (scap_db_version != manage_scap_db_supported_version ())
15646     {
15647       g_message ("%s: database version of SCAP database: %i",
15648                  __func__,
15649                  scap_db_version);
15650       g_message ("%s: SCAP database version supported by manager: %s",
15651                  __func__,
15652                  G_STRINGIFY (GVMD_SCAP_DATABASE_VERSION));
15653       return -2;
15654     }
15655 
15656   /* Check CERT database version. */
15657 
15658   cert_db_version = manage_cert_db_version ();
15659   if (cert_db_version == -1)
15660     g_message ("No CERT database found");
15661   else if (cert_db_version != manage_cert_db_supported_version ())
15662     {
15663       g_message ("%s: database version of CERT database: %i",
15664                  __func__,
15665                  cert_db_version);
15666       g_message ("%s: CERT database version supported by manager: %s",
15667                  __func__,
15668                  G_STRINGIFY (GVMD_CERT_DATABASE_VERSION));
15669       return -2;
15670     }
15671   return 0;
15672 }
15673 
15674 /**
15675  * @brief Ensures the sanity of nvts cache in DB.
15676  */
15677 static void
check_db_nvt_selectors()15678 check_db_nvt_selectors ()
15679 {
15680   /* Ensure every part of the predefined selector exists.
15681    * This restores entries lost due to the error solved 2010-08-13 by r8805. */
15682   if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
15683                " '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
15684                " AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_ALL) ";")
15685       == 0)
15686     {
15687       sql ("INSERT into nvt_selectors (name, exclude, type, family_or_nvt)"
15688            " VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 0, "
15689            G_STRINGIFY (NVT_SELECTOR_TYPE_ALL) ", NULL);");
15690     }
15691 
15692   if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
15693                " '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
15694                " AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
15695                " AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.810002';")
15696       == 0)
15697     {
15698       sql ("INSERT into nvt_selectors"
15699            " (name, exclude, type, family_or_nvt, family)"
15700            " VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
15701            G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
15702            /* OID of the "CPE Inventory" NVT. */
15703            " '1.3.6.1.4.1.25623.1.0.810002', 'Service detection');");
15704     }
15705 
15706   if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
15707                " '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
15708                " AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
15709                " AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.810003';")
15710       == 0)
15711     {
15712       sql ("INSERT into nvt_selectors"
15713            " (name, exclude, type, family_or_nvt, family)"
15714            " VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
15715            G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
15716            /* OID of the "Host Summary" NVT. */
15717            " '1.3.6.1.4.1.25623.1.0.810003', 'General');");
15718     }
15719 
15720   if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
15721                " '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
15722                " AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_FAMILY)
15723                " AND family_or_nvt = 'Port scanners';")
15724       == 0)
15725     {
15726       sql ("INSERT into nvt_selectors"
15727            " (name, exclude, type, family_or_nvt, family)"
15728            " VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
15729            G_STRINGIFY (NVT_SELECTOR_TYPE_FAMILY) ","
15730            " 'Port scanners', 'Port scanners');");
15731     }
15732 
15733   if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
15734                " '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
15735                " AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
15736                " AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.14259';")
15737       == 0)
15738     {
15739       sql ("INSERT into nvt_selectors"
15740            " (name, exclude, type, family_or_nvt, family)"
15741            " VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 0, "
15742            G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
15743            /* OID of the "Nmap (NASL wrapper)" NVT. */
15744            " '1.3.6.1.4.1.25623.1.0.14259', 'Port scanners');");
15745     }
15746 
15747   if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
15748                " '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
15749                " AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
15750                " AND family_or_nvt = '" OID_PING_HOST "';")
15751       == 0)
15752     {
15753       sql ("INSERT into nvt_selectors"
15754            " (name, exclude, type, family_or_nvt, family)"
15755            " VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 0, "
15756            G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
15757            " '" OID_PING_HOST "', 'Port scanners');");
15758     }
15759 
15760   if (sql_int ("SELECT count(*) FROM nvt_selectors WHERE name ="
15761                " '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
15762                " AND type = " G_STRINGIFY (NVT_SELECTOR_TYPE_NVT)
15763                " AND family_or_nvt = '1.3.6.1.4.1.25623.1.0.80109';")
15764       == 0)
15765     {
15766       sql ("INSERT into nvt_selectors"
15767            " (name, exclude, type, family_or_nvt, family)"
15768            " VALUES ('" MANAGE_NVT_SELECTOR_UUID_ALL "', 1, "
15769            G_STRINGIFY (NVT_SELECTOR_TYPE_NVT) ","
15770            /* OID of the "w3af (NASL wrapper)" NVT. */
15771            " '1.3.6.1.4.1.25623.1.0.80109', 'Web application abuses');");
15772     }
15773 }
15774 
15775 /**
15776  * @brief Add permissions for all global resources.
15777  *
15778  * @param[in]  role_uuid  UUID of role.
15779  */
15780 static void
add_permissions_on_globals(const gchar * role_uuid)15781 add_permissions_on_globals (const gchar *role_uuid)
15782 {
15783   iterator_t scanners;
15784 
15785   /* Scanners are global when created from the command line. */
15786   init_iterator (&scanners,
15787                   "SELECT id, uuid FROM scanners WHERE owner is NULL;");
15788   while (next (&scanners))
15789     add_role_permission_resource (role_uuid, "GET_SCANNERS",
15790                                   "scanner",
15791                                   iterator_string (&scanners, 1));
15792   cleanup_iterator (&scanners);
15793 
15794   add_role_permission_resource (role_uuid, "GET_ROLES",
15795                                 "role",
15796                                 ROLE_UUID_ADMIN);
15797   add_role_permission_resource (role_uuid, "GET_ROLES",
15798                                 "role",
15799                                 ROLE_UUID_GUEST);
15800   add_role_permission_resource (role_uuid, "GET_ROLES",
15801                                 "role",
15802                                 ROLE_UUID_INFO);
15803   add_role_permission_resource (role_uuid, "GET_ROLES",
15804                                 "role",
15805                                 ROLE_UUID_MONITOR);
15806   add_role_permission_resource (role_uuid, "GET_ROLES",
15807                                 "role",
15808                                 ROLE_UUID_USER);
15809   add_role_permission_resource (role_uuid, "GET_ROLES",
15810                                 "role",
15811                                 ROLE_UUID_OBSERVER);
15812 }
15813 
15814 /**
15815  * @brief Ensure the predefined permissions exists.
15816  */
15817 static void
check_db_permissions()15818 check_db_permissions ()
15819 {
15820   command_t *command;
15821 
15822   if (sql_int ("SELECT count(*) FROM permissions"
15823                " WHERE uuid = '" PERMISSION_UUID_ADMIN_EVERYTHING "';")
15824       == 0)
15825     sql ("INSERT INTO permissions"
15826          " (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
15827          "  resource_location, subject_type, subject, subject_location,"
15828          "  creation_time, modification_time)"
15829          " VALUES"
15830          " ('" PERMISSION_UUID_ADMIN_EVERYTHING "', NULL, 'Everything', '', '',"
15831          "  0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
15832          "  (SELECT id FROM roles WHERE uuid = '" ROLE_UUID_ADMIN "'),"
15833          "  " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());");
15834 
15835   if (sql_int ("SELECT count(*) FROM permissions"
15836                " WHERE uuid = '" PERMISSION_UUID_SUPER_ADMIN_EVERYTHING "';")
15837       == 0)
15838     {
15839       sql ("INSERT INTO permissions"
15840            " (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
15841            "  resource_location, subject_type, subject, subject_location,"
15842            "  creation_time, modification_time)"
15843            " VALUES"
15844            " ('" PERMISSION_UUID_SUPER_ADMIN_EVERYTHING "', NULL, 'Everything',"
15845            "  '', '', 0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
15846            "  (SELECT id FROM roles WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "'),"
15847            "  " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());");
15848       sql ("INSERT INTO permissions"
15849            " (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
15850            "  resource_location, subject_type, subject, subject_location,"
15851            "  creation_time, modification_time)"
15852            " VALUES"
15853            " (make_uuid (), NULL, 'Super',"
15854            "  '', '', 0, '', " G_STRINGIFY (LOCATION_TABLE) ", 'role',"
15855            "  (SELECT id FROM roles WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "'),"
15856            "  " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());");
15857     }
15858 
15859   if (sql_int ("SELECT count(*) FROM permissions"
15860                " WHERE subject_type = 'role'"
15861                " AND subject = (SELECT id FROM roles"
15862                "                WHERE uuid = '" ROLE_UUID_GUEST "')"
15863                " AND resource = 0;")
15864       <= 1)
15865     {
15866       /* Clean-up any remaining permissions. */
15867       sql ("DELETE FROM permissions WHERE subject_type = 'role'"
15868            " AND subject = (SELECT id FROM roles"
15869            "                WHERE uuid = '" ROLE_UUID_GUEST "');");
15870     }
15871   add_role_permission (ROLE_UUID_GUEST, "AUTHENTICATE");
15872   add_role_permission (ROLE_UUID_GUEST, "HELP");
15873   add_role_permission (ROLE_UUID_GUEST, "GET_AGGREGATES");
15874   add_role_permission (ROLE_UUID_GUEST, "GET_FILTERS");
15875   add_role_permission (ROLE_UUID_GUEST, "GET_INFO");
15876   add_role_permission (ROLE_UUID_GUEST, "GET_NVTS");
15877   add_role_permission (ROLE_UUID_GUEST, "GET_SETTINGS");
15878 
15879   if (sql_int ("SELECT count(*) FROM permissions"
15880                " WHERE subject_type = 'role'"
15881                " AND subject = (SELECT id FROM roles"
15882                "                WHERE uuid = '" ROLE_UUID_INFO "')"
15883                " AND resource = 0;")
15884       <= 1)
15885     {
15886       /* Clean-up any remaining permissions. */
15887       sql ("DELETE FROM permissions WHERE subject_type = 'role'"
15888            " AND subject = (SELECT id FROM roles"
15889            "                WHERE uuid = '" ROLE_UUID_INFO "');");
15890     }
15891   add_role_permission (ROLE_UUID_INFO, "AUTHENTICATE");
15892   add_role_permission (ROLE_UUID_INFO, "HELP");
15893   add_role_permission (ROLE_UUID_INFO, "GET_AGGREGATES");
15894   add_role_permission (ROLE_UUID_INFO, "GET_INFO");
15895   add_role_permission (ROLE_UUID_INFO, "GET_NVTS");
15896   add_role_permission (ROLE_UUID_INFO, "GET_SETTINGS");
15897   add_role_permission (ROLE_UUID_INFO, "MODIFY_SETTING");
15898 
15899   if (sql_int ("SELECT count(*) FROM permissions"
15900                " WHERE subject_type = 'role'"
15901                " AND subject = (SELECT id FROM roles"
15902                "                WHERE uuid = '" ROLE_UUID_MONITOR "')"
15903                " AND resource = 0;")
15904       <= 1)
15905     {
15906       /* Clean-up any remaining permissions. */
15907       sql ("DELETE FROM permissions WHERE subject_type = 'role'"
15908            " AND subject = (SELECT id FROM roles"
15909            "                WHERE uuid = '" ROLE_UUID_MONITOR "');");
15910     }
15911   add_role_permission (ROLE_UUID_MONITOR, "AUTHENTICATE");
15912   add_role_permission (ROLE_UUID_MONITOR, "GET_SETTINGS");
15913   add_role_permission (ROLE_UUID_MONITOR, "GET_SYSTEM_REPORTS");
15914   add_role_permission (ROLE_UUID_MONITOR, "HELP");
15915 
15916   if (sql_int ("SELECT count(*) FROM permissions"
15917                " WHERE subject_type = 'role'"
15918                " AND subject = (SELECT id FROM roles"
15919                "                WHERE uuid = '" ROLE_UUID_USER "')"
15920                " AND resource = 0;")
15921       <= 1)
15922     {
15923       /* Clean-up any remaining permissions. */
15924       sql ("DELETE FROM permissions WHERE subject_type = 'role'"
15925            " AND subject = (SELECT id FROM roles"
15926            "                WHERE uuid = '" ROLE_UUID_USER "');");
15927     }
15928   command = gmp_commands;
15929   while (command[0].name)
15930     {
15931       if (strstr (command[0].name, "DESCRIBE_AUTH") == NULL
15932           && strcmp (command[0].name, "GET_VERSION")
15933           && strstr (command[0].name, "GROUP") == NULL
15934           && strstr (command[0].name, "ROLE") == NULL
15935           && strstr (command[0].name, "SYNC") == NULL
15936           && strstr (command[0].name, "USER") == NULL)
15937         add_role_permission (ROLE_UUID_USER, command[0].name);
15938       command++;
15939     }
15940 
15941   if (sql_int ("SELECT count(*) FROM permissions"
15942                " WHERE subject_type = 'role'"
15943                " AND subject = (SELECT id FROM roles"
15944                "                WHERE uuid = '" ROLE_UUID_OBSERVER "')"
15945                " AND resource = 0;")
15946       <= 1)
15947     {
15948       /* Clean-up any remaining permissions. */
15949       sql ("DELETE FROM permissions WHERE subject_type = 'role'"
15950            " AND subject = (SELECT id FROM roles"
15951            "                WHERE uuid = '" ROLE_UUID_OBSERVER "');");
15952     }
15953   command = gmp_commands;
15954   while (command[0].name)
15955     {
15956       if ((strstr (command[0].name, "GET") == command[0].name)
15957           && strcmp (command[0].name, "GET_GROUPS")
15958           && strcmp (command[0].name, "GET_ROLES")
15959           && strcmp (command[0].name, "GET_USERS")
15960           && strcmp (command[0].name, "GET_VERSION"))
15961         add_role_permission (ROLE_UUID_OBSERVER, command[0].name);
15962       command++;
15963     }
15964   add_role_permission (ROLE_UUID_OBSERVER, "AUTHENTICATE");
15965   add_role_permission (ROLE_UUID_OBSERVER, "HELP");
15966   add_role_permission (ROLE_UUID_OBSERVER, "MODIFY_SETTING");
15967 
15968 
15969   add_permissions_on_globals (ROLE_UUID_ADMIN);
15970   add_permissions_on_globals (ROLE_UUID_GUEST);
15971   add_permissions_on_globals (ROLE_UUID_OBSERVER);
15972   add_permissions_on_globals (ROLE_UUID_USER);
15973 }
15974 
15975 /**
15976  * @brief Ensure the predefined roles exists.
15977  */
15978 static void
check_db_roles()15979 check_db_roles ()
15980 {
15981   if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_ADMIN "';")
15982       == 0)
15983     sql ("INSERT INTO roles"
15984          " (uuid, owner, name, comment, creation_time, modification_time)"
15985          " VALUES"
15986          " ('" ROLE_UUID_ADMIN "', NULL, 'Admin',"
15987          "  'Administrator.  Full privileges.',"
15988          " m_now (), m_now ());");
15989 
15990   if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_GUEST "';")
15991       == 0)
15992     sql ("INSERT INTO roles"
15993          " (uuid, owner, name, comment, creation_time, modification_time)"
15994          " VALUES"
15995          " ('" ROLE_UUID_GUEST "', NULL, 'Guest',"
15996          "  'Guest.',"
15997          " m_now (), m_now ());");
15998 
15999   if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_INFO "';")
16000       == 0)
16001     sql ("INSERT INTO roles"
16002          " (uuid, owner, name, comment, creation_time, modification_time)"
16003          " VALUES"
16004          " ('" ROLE_UUID_INFO "', NULL, 'Info',"
16005          "  'Information browser.',"
16006          " m_now (), m_now ());");
16007 
16008   if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_MONITOR "';")
16009       == 0)
16010     sql ("INSERT INTO roles"
16011          " (uuid, owner, name, comment, creation_time, modification_time)"
16012          " VALUES"
16013          " ('" ROLE_UUID_MONITOR "', NULL, 'Monitor',"
16014          "  'Performance monitor.',"
16015          " m_now (), m_now ());");
16016 
16017   if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_USER "';")
16018       == 0)
16019     sql ("INSERT INTO roles"
16020          " (uuid, owner, name, comment, creation_time, modification_time)"
16021          " VALUES"
16022          " ('" ROLE_UUID_USER "', NULL, 'User',"
16023          "  'Standard user.',"
16024          " m_now (), m_now ());");
16025 
16026   if (sql_int ("SELECT count(*) FROM roles WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "';")
16027       == 0)
16028     sql ("INSERT INTO roles"
16029          " (uuid, owner, name, comment, creation_time, modification_time)"
16030          " VALUES"
16031          " ('" ROLE_UUID_SUPER_ADMIN "', NULL, 'Super Admin',"
16032          "  'Super administrator.  Full privileges with access to all users.',"
16033          " m_now (), m_now ());");
16034 
16035   if (sql_int ("SELECT count(*) FROM roles"
16036                " WHERE uuid = '" ROLE_UUID_OBSERVER "';")
16037       == 0)
16038     sql ("INSERT INTO roles"
16039          " (uuid, owner, name, comment, creation_time, modification_time)"
16040          " VALUES"
16041          " ('" ROLE_UUID_OBSERVER "', NULL, 'Observer',"
16042          "  'Observer.',"
16043          " m_now (), m_now ());");
16044 }
16045 
16046 /**
16047  * @brief Cleanup the auth_cache table.
16048  */
16049 static void
clean_auth_cache()16050 clean_auth_cache ()
16051 {
16052   sql ("DELETE FROM auth_cache;");
16053 }
16054 
16055 /**
16056  * @brief Tries to migrate sensor type scanners to match the relays.
16057  *
16058  * @return A string describing the results or NULL on error.
16059  */
16060 static gchar *
manage_migrate_relay_sensors()16061 manage_migrate_relay_sensors ()
16062 {
16063   iterator_t scanners;
16064   int gmp_successes, gmp_failures, osp_failures;
16065 
16066   gmp_successes = gmp_failures = osp_failures = 0;
16067 
16068   if (get_relay_mapper_path () == NULL)
16069     {
16070       g_warning ("%s: No relay mapper set", __func__);
16071       return NULL;
16072     }
16073 
16074   init_iterator (&scanners,
16075                  "SELECT id, uuid, type, host, port FROM scanners"
16076                  " WHERE type = %d",
16077                  SCANNER_TYPE_OSP_SENSOR);
16078 
16079   while (next (&scanners))
16080     {
16081       scanner_type_t type;
16082       const char *scanner_id, *host;
16083       int port;
16084 
16085       scanner_id = iterator_string (&scanners, 1);
16086       type = iterator_int (&scanners, 2);
16087       host = iterator_string (&scanners, 3);
16088       port = iterator_int (&scanners, 4);
16089 
16090       if (relay_supports_scanner_type (host, port, type) == FALSE)
16091         {
16092           if (type == SCANNER_TYPE_OSP_SENSOR)
16093             {
16094               g_message ("%s: No relay found for OSP Sensor %s (%s:%d).",
16095                          __func__, scanner_id, host, port);
16096               osp_failures++;
16097             }
16098           else
16099             g_warning ("%s: Unexpected type for scanner %s: %d",
16100                        __func__, scanner_id, type);
16101         }
16102     }
16103   cleanup_iterator (&scanners);
16104 
16105   if (gmp_successes == 0 && gmp_failures == 0 && osp_failures == 0)
16106     return g_strdup ("All GMP or OSP sensors up to date.");
16107   else
16108     {
16109       GString *message = g_string_new ("");
16110       g_string_append_printf (message,
16111                               "%d sensors(s) not matching:",
16112                               gmp_successes + gmp_failures + osp_failures);
16113       if (gmp_successes)
16114         g_string_append_printf (message,
16115                                 " %d GMP scanner(s) migrated to OSP.",
16116                                 gmp_successes);
16117       if (gmp_failures)
16118         g_string_append_printf (message,
16119                                 " %d GMP scanner(s) not migrated.",
16120                                 gmp_failures);
16121       if (osp_failures)
16122         g_string_append_printf (message,
16123                                 " %d OSP sensor(s) not migrated.",
16124                                 osp_failures);
16125 
16126       return g_string_free (message, FALSE);
16127     }
16128 }
16129 
16130 /**
16131  * @brief Ensure that the database is in order.
16132  *
16133  * Only called by init_manage_internal, and ultimately only by the main process.
16134  *
16135  * @param[in]  check_encryption_key  Whether to check encryption key.
16136  *
16137  * @return 0 success, -1 error.
16138  */
16139 static int
check_db(int check_encryption_key)16140 check_db (int check_encryption_key)
16141 {
16142   /* The file locks managed at startup ensure that this is the only Manager
16143    * process accessing the db.  Nothing else should be accessing the db, access
16144    * should always go through Manager. */
16145   sql_begin_immediate ();
16146   if (check_db_extensions ())
16147     goto fail;
16148   create_tables ();
16149   check_db_sequences ();
16150   set_db_version (GVMD_DATABASE_VERSION);
16151   check_db_roles ();
16152   check_db_nvt_selectors ();
16153   check_db_nvts ();
16154   check_db_port_lists ();
16155   clean_auth_cache ();
16156   if (check_db_scanners ())
16157     goto fail;
16158   if (check_db_report_formats ())
16159     goto fail;
16160   if (check_db_report_formats_trash ())
16161     goto fail;
16162   check_db_permissions ();
16163   check_db_settings ();
16164   cleanup_schedule_times ();
16165   if (check_encryption_key && check_db_encryption_key ())
16166     goto fail;
16167 
16168   sql_commit ();
16169   return 0;
16170 
16171  fail:
16172   sql_rollback ();
16173   return -1;
16174 }
16175 
16176 /**
16177  * @brief Stop any active tasks.
16178  */
16179 static void
stop_active_tasks()16180 stop_active_tasks ()
16181 {
16182   iterator_t tasks;
16183   get_data_t get;
16184 
16185   /* Set requested and running tasks to stopped. */
16186 
16187   assert (current_credentials.uuid == NULL);
16188   memset (&get, '\0', sizeof (get));
16189   get.ignore_pagination = 1;
16190   init_task_iterator (&tasks, &get);
16191   while (next (&tasks))
16192     {
16193       switch (task_iterator_run_status (&tasks))
16194         {
16195           case TASK_STATUS_DELETE_REQUESTED:
16196           case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
16197           case TASK_STATUS_DELETE_ULTIMATE_WAITING:
16198           case TASK_STATUS_DELETE_WAITING:
16199           case TASK_STATUS_REQUESTED:
16200           case TASK_STATUS_RUNNING:
16201           case TASK_STATUS_QUEUED:
16202           case TASK_STATUS_STOP_REQUESTED:
16203           case TASK_STATUS_STOP_WAITING:
16204             {
16205               task_t index = get_iterator_resource (&tasks);
16206               /* Set the current user, for event checks. */
16207               current_credentials.uuid = task_owner_uuid (index);
16208               task_last_report_any_status (index, &global_current_report);
16209               set_task_interrupted (index,
16210                                     "Task process exited abnormally"
16211                                     " (e.g. machine lost power or process was"
16212                                     " sent SIGKILL)."
16213                                     "  Setting scan status to Interrupted.");
16214               global_current_report = 0;
16215               free (current_credentials.uuid);
16216               break;
16217             }
16218           default:
16219             break;
16220         }
16221     }
16222   cleanup_iterator (&tasks);
16223   current_credentials.uuid = NULL;
16224 
16225   /* Set requested and running reports to stopped. */
16226 
16227   sql ("UPDATE reports SET scan_run_status = %u"
16228        " WHERE scan_run_status = %u"
16229        " OR scan_run_status = %u"
16230        " OR scan_run_status = %u"
16231        " OR scan_run_status = %u"
16232        " OR scan_run_status = %u"
16233        " OR scan_run_status = %u"
16234        " OR scan_run_status = %u"
16235        " OR scan_run_status = %u"
16236        " OR scan_run_status = %u;",
16237        TASK_STATUS_INTERRUPTED,
16238        TASK_STATUS_DELETE_REQUESTED,
16239        TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
16240        TASK_STATUS_DELETE_ULTIMATE_WAITING,
16241        TASK_STATUS_DELETE_WAITING,
16242        TASK_STATUS_REQUESTED,
16243        TASK_STATUS_RUNNING,
16244        TASK_STATUS_QUEUED,
16245        TASK_STATUS_STOP_REQUESTED,
16246        TASK_STATUS_STOP_WAITING);
16247 }
16248 
16249 /**
16250  * @brief Clean up database tables.
16251  *
16252  * Remove superfluous entries from tables.
16253  */
16254 static void
cleanup_tables()16255 cleanup_tables ()
16256 {
16257   /* Remove group and role assignments of deleted users.
16258    *
16259    * This should be a migrator, but this way is easier to backport.  */
16260 
16261   sql ("DELETE FROM group_users"
16262        " WHERE \"user\" NOT IN (SELECT id FROM users);");
16263   sql ("DELETE FROM group_users_trash"
16264        " WHERE \"user\" NOT IN (SELECT id FROM users);");
16265   sql ("DELETE FROM role_users"
16266        " WHERE \"user\" NOT IN (SELECT id FROM users);");
16267   sql ("DELETE FROM role_users_trash"
16268        " WHERE \"user\" NOT IN (SELECT id FROM users);");
16269 
16270   /*
16271    * Remove permissions of deleted users, groups and roles.
16272    */
16273   sql ("DELETE FROM permissions"
16274        " WHERE (subject_type = 'user'"
16275        "        AND subject NOT IN (SELECT id FROM users))"
16276        "    OR (subject_type = 'group'"
16277        "        AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
16278        "        AND subject NOT IN (SELECT id FROM groups))"
16279        "    OR (subject_type = 'group'"
16280        "        AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
16281        "        AND subject NOT IN (SELECT id FROM groups_trash))"
16282        "    OR (subject_type = 'role'"
16283        "        AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
16284        "        AND subject NOT IN (SELECT id FROM roles))"
16285        "    OR (subject_type = 'role'"
16286        "        AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
16287        "        AND subject NOT IN (SELECT id FROM roles_trash));");
16288 
16289   sql ("DELETE FROM permissions_trash"
16290        " WHERE (subject_type = 'user'"
16291        "        AND subject NOT IN (SELECT id FROM users))"
16292        "    OR (subject_type = 'group'"
16293        "        AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
16294        "        AND subject NOT IN (SELECT id FROM groups))"
16295        "    OR (subject_type = 'group'"
16296        "        AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
16297        "        AND subject NOT IN (SELECT id FROM groups_trash))"
16298        "    OR (subject_type = 'role'"
16299        "        AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
16300        "        AND subject NOT IN (SELECT id FROM roles))"
16301        "    OR (subject_type = 'role'"
16302        "        AND subject_location = " G_STRINGIFY (LOCATION_TRASH)
16303        "        AND subject NOT IN (SELECT id FROM roles_trash));");
16304 
16305   sql ("DELETE FROM permissions_get_tasks"
16306        " WHERE \"user\" NOT IN (SELECT id FROM users);");
16307 }
16308 
16309 /**
16310  * @brief Initialize the manage library.
16311  *
16312  * Check DB version, do startup database checks, load the NVT cache.
16313  * Optionally also stop active tasks.
16314  *
16315  * @param[in]  log_config      Log configuration.
16316  * @param[in]  database        Location of database.
16317  * @param[in]  max_ips_per_target  Max number of IPs per target.
16318  * @param[in]  max_email_attachment_size  Max size of email attachments.
16319  * @param[in]  max_email_include_size     Max size of email inclusions.
16320  * @param[in]  max_email_message_size     Max size of email user message text.
16321  * @param[in]  stop_tasks          Stop any active tasks.
16322  * @param[in]  fork_connection     Function to fork a connection that will
16323  *                                 accept GMP requests.  Used to start tasks
16324  *                                 with GMP when an alert occurs.
16325  * @param[in]  skip_db_check       Skip DB check.
16326  * @param[in]  check_encryption_key  Check encryption key if doing DB check.
16327  *
16328  * @return 0 success, -1 error, -2 database is wrong version,
16329  *         -4 max_ips_per_target out of range.
16330  */
16331 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)16332 init_manage_internal (GSList *log_config,
16333                       const db_conn_info_t *database,
16334                       int max_ips_per_target,
16335                       int max_email_attachment_size,
16336                       int max_email_include_size,
16337                       int max_email_message_size,
16338                       int stop_tasks,
16339                       manage_connection_forker_t fork_connection,
16340                       int skip_db_check,
16341                       int check_encryption_key)
16342 {
16343   int ret;
16344 
16345   /* Summary of init cases:
16346    *
16347    *     daemon [--foreground]
16348    *         init_gmpd  cache 0
16349    *             init_manage
16350    *         serve_and_schedule
16351    *             forks child (serve_gmp)
16352    *                 init_gmpd_process
16353    *                     init_manage_process
16354    *                 ...
16355    *                 event
16356    *                   fork_connection_for_event
16357    *                       fork one
16358    *                           init_gmpd_process
16359    *                               init_manage_process
16360    *                           serve_client
16361    *                       fork two
16362    *                           gmp_auth, gmp_start_task_report.
16363    *                 ...
16364    *             manage_schedule
16365    *                 fork_connection_for_scheduler
16366    *                     fork one
16367    *                         init_gmpd_process
16368    *                             init_manage_process
16369    *                         serve_client
16370    *                     fork two
16371    *                         gmp_auth, gmp_start_task_report, gmp_resume_task_report.
16372    *     --create-user --delete-user --get-users
16373    *         manage_create, ...
16374    *             init_manage_helper
16375    *     --encrypt/decrypt-all-credentials
16376    *         manage_encrypt_...
16377    *             init_manage_helper
16378    *     --migrate
16379    *         manage_migrate
16380    *             init_manage_process (sorts out db state itself) */
16381 
16382   if ((max_ips_per_target <= 0)
16383       || (max_ips_per_target > MANAGE_ABSOLUTE_MAX_IPS_PER_TARGET))
16384     return -4;
16385 
16386   max_hosts = max_ips_per_target;
16387   if (max_email_attachment_size)
16388     max_attach_length = max_email_attachment_size;
16389   if (max_email_include_size)
16390     max_content_length = max_email_include_size;
16391   if (max_email_message_size)
16392     max_email_message_length = max_email_message_size;
16393 
16394   g_log_set_handler (G_LOG_DOMAIN,
16395                      ALL_LOG_LEVELS,
16396                      (GLogFunc) gvm_log_func,
16397                      log_config);
16398 
16399   memset (&current_credentials, '\0', sizeof (current_credentials));
16400 
16401   init_manage_open_db (database);
16402 
16403   /* Check that the versions of the databases are correct. */
16404 
16405   ret = check_db_versions ();
16406   if (ret)
16407     return ret;
16408 
16409   init_manage_create_functions ();
16410 
16411   /* Ensure the database is complete, removing superfluous rows.
16412    *
16413    * Assume that all other running processes are from the same Manager version,
16414    * because some of these checks will modify the database if it is out of
16415    * date.  This is relevant because the caller may be a command option process
16416    * like a --create-user process.  */
16417 
16418   if (skip_db_check == 0)
16419     {
16420       /* This only happens for init_manage callers with skip_db_check set to 0
16421        * and init_manage_helper callers.  So there are only 2 callers:
16422        *
16423        *   1 the main process
16424        *   2 a helper processes (--create-user, --get-users, etc) when the
16425        *     main process is not running. */
16426 
16427       ret = check_db (check_encryption_key);
16428       if (ret)
16429         return ret;
16430 
16431       cleanup_tables ();
16432 
16433       /* Set max_hosts in db, so database server side can access it. */
16434 
16435       sql ("DELETE FROM meta WHERE name = 'max_hosts';");
16436       sql ("INSERT INTO meta (name, value) VALUES ('max_hosts', %i);", max_hosts);
16437     }
16438 
16439   if (stop_tasks)
16440     /* Stop any active tasks. */
16441     stop_active_tasks ();
16442 
16443   /* Load the NVT cache into memory. */
16444 
16445   if (nvti_cache == NULL)
16446     update_nvti_cache ();
16447 
16448   if (skip_db_check == 0)
16449     /* Requires NVT cache. */
16450     check_db_configs ();
16451 
16452   sql_close ();
16453   gvmd_db_conn_info.name = database->name ? g_strdup (database->name) : NULL;
16454   gvmd_db_conn_info.host = database->host ? g_strdup (database->host) : NULL;
16455   gvmd_db_conn_info.port = database->port ? g_strdup (database->port) : NULL;
16456   gvmd_db_conn_info.user = database->user ? g_strdup (database->user) : NULL;
16457 
16458   if (fork_connection)
16459     manage_fork_connection = fork_connection;
16460   return 0;
16461 }
16462 
16463 /**
16464  * @brief Initialize the manage library.
16465  *
16466  * Check DB version, do startup database checks, load the NVT cache.
16467  *
16468  * Ensure all tasks are in a clean initial state.
16469  *
16470  * Beware that calling this function while tasks are running may lead to
16471  * problems.
16472  *
16473  * @param[in]  log_config      Log configuration.
16474  * @param[in]  database        Location of database.
16475  * @param[in]  max_ips_per_target  Max number of IPs per target.
16476  * @param[in]  max_email_attachment_size  Max size of email attachments.
16477  * @param[in]  max_email_include_size     Max size of email inclusions.
16478  * @param[in]  max_email_message_size     Max size of email user message text.
16479  * @param[in]  fork_connection     Function to fork a connection that will
16480  *                                 accept GMP requests.  Used to start tasks
16481  *                                 with GMP when an alert occurs.
16482  * @param[in]  skip_db_check       Skip DB check.
16483  *
16484  * @return 0 success, -1 error, -2 database is wrong version, -3 database needs
16485  *         to be initialised from server, -4 max_ips_per_target out of range.
16486  */
16487 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)16488 init_manage (GSList *log_config, const db_conn_info_t *database,
16489              int max_ips_per_target, int max_email_attachment_size,
16490              int max_email_include_size, int max_email_message_size,
16491              manage_connection_forker_t fork_connection,
16492              int skip_db_check)
16493 {
16494   return init_manage_internal (log_config,
16495                                database,
16496                                max_ips_per_target,
16497                                max_email_attachment_size,
16498                                max_email_include_size,
16499                                max_email_message_size,
16500                                1,  /* Stop active tasks. */
16501                                fork_connection,
16502                                skip_db_check,
16503                                1); /* Check encryption key if checking db. */
16504 }
16505 
16506 /**
16507  * @brief Initialize the manage library for a helper program.
16508  *
16509  * This should be called at the beginning of any program that accesses the
16510  * database.  Forked processes should call init_manage_process.  The daemon
16511  * itself calls init_manage, including in NVT cache mode.
16512  *
16513  * @param[in]  log_config      Log configuration.
16514  * @param[in]  database        Location of database.
16515  * @param[in]  max_ips_per_target  Max number of IPs per target.
16516  *
16517  * @return 0 success, -1 error, -2 database is wrong version, -3 database needs
16518  *         to be initialised from server, -4 max_ips_per_target out of range.
16519  */
16520 int
init_manage_helper(GSList * log_config,const db_conn_info_t * database,int max_ips_per_target)16521 init_manage_helper (GSList *log_config, const db_conn_info_t *database,
16522                     int max_ips_per_target)
16523 {
16524   return init_manage_internal (log_config,
16525                                database,
16526                                max_ips_per_target,
16527                                0,   /* Default max_email_attachment_size. */
16528                                0,   /* Default max_email_include_size. */
16529                                0,   /* Default max_email_message_size */
16530                                0,   /* Stop active tasks. */
16531                                NULL,
16532                                /* Skip DB check if main process is running, to
16533                                 * avoid locking issues when creating tables.
16534                                 *
16535                                 * Safe because main process did the check. */
16536                                lockfile_locked ("gvm-serving")
16537                                 ? 1    /* Skip DB check. */
16538                                 : 0,   /* Do DB check. */
16539                                0);  /* Dummy. */
16540 }
16541 
16542 /**
16543  * @brief Cleanup the manage library.
16544  *
16545  * Optionally put any running task in the interrupted state and close the
16546  * database.
16547  *
16548  * @param[in]  cleanup  If TRUE perform all cleanup operations, else only
16549  *                      those required at the start of a forked process.
16550  */
16551 void
cleanup_manage_process(gboolean cleanup)16552 cleanup_manage_process (gboolean cleanup)
16553 {
16554   if (sql_is_open ())
16555     {
16556       if (cleanup)
16557         {
16558           if (current_scanner_task)
16559             {
16560               if (global_current_report)
16561                 {
16562                   result_t result;
16563                   result = make_result (current_scanner_task,
16564                                         "", "", "", "", "Error Message",
16565                                         "Interrupting scan because GVM is"
16566                                         " exiting.",
16567                                         NULL);
16568                   report_add_result (global_current_report, result);
16569                 }
16570               set_task_run_status (current_scanner_task, TASK_STATUS_INTERRUPTED);
16571             }
16572           sql_close ();
16573         }
16574       else
16575         sql_close_fork ();
16576     }
16577 }
16578 
16579 /**
16580  * @brief Cleanup as immediately as possible.
16581  *
16582  * Put any running task in the error state and close the database.
16583  *
16584  * Intended for handlers for signals like SIGSEGV and SIGABRT.
16585  *
16586  * @param[in]  signal  Dummy argument for use as signal handler.
16587  */
16588 void
manage_cleanup_process_error(int signal)16589 manage_cleanup_process_error (int signal)
16590 {
16591   g_debug ("Received %s signal", strsignal (signal));
16592   if (sql_is_open ())
16593     {
16594       if (current_scanner_task)
16595         {
16596           g_warning ("%s: Error exit, setting running task to Interrupted",
16597                      __func__);
16598           set_task_interrupted (current_scanner_task,
16599                                 "Error exit, setting running task to"
16600                                 " Interrupted.");
16601         }
16602       sql_close ();
16603     }
16604 }
16605 
16606 /**
16607  * @brief Cleanup as immediately as possible.
16608  */
16609 void
manage_reset_currents()16610 manage_reset_currents ()
16611 {
16612   global_current_report = 0;
16613   current_scanner_task = (task_t) 0;
16614   free_credentials (&current_credentials);
16615 }
16616 
16617 /**
16618  * @brief Get user hash.
16619  *
16620  * This is for "file" users, now entirely stored in db.
16621  *
16622  * @param[in]  username  User name.
16623  *
16624  * @return Hash.
16625  */
16626 gchar *
manage_user_hash(const gchar * username)16627 manage_user_hash (const gchar *username)
16628 {
16629   gchar *hash, *quoted_username;
16630   quoted_username = sql_quote (username);
16631   hash = sql_string ("SELECT password FROM users WHERE name = '%s';",
16632                      quoted_username);
16633   g_free (quoted_username);
16634   return hash;
16635 }
16636 
16637 /**
16638  * @brief Get user uuid.
16639  *
16640  * @param[in]  username  User name.
16641  * @param[in]  method    Authentication method.
16642  *
16643  * @return UUID.
16644  */
16645 static gchar *
user_uuid_method(const gchar * username,auth_method_t method)16646 user_uuid_method (const gchar *username, auth_method_t method)
16647 {
16648   gchar *uuid, *quoted_username, *quoted_method;
16649   quoted_username = sql_quote (username);
16650   quoted_method = sql_quote (auth_method_name (method));
16651   uuid = sql_string ("SELECT uuid FROM users"
16652                      " WHERE name = '%s' AND method = '%s';",
16653                      quoted_username,
16654                      quoted_method);
16655   g_free (quoted_username);
16656   g_free (quoted_method);
16657   return uuid;
16658 }
16659 
16660 /**
16661  * @brief Check whether LDAP is enabled.
16662  *
16663  * @return 0 no, else yes.
16664  */
16665 static int
ldap_auth_enabled()16666 ldap_auth_enabled ()
16667 {
16668   if (gvm_auth_ldap_enabled ())
16669     return sql_int ("SELECT coalesce ((SELECT CAST (value AS INTEGER) FROM meta"
16670                     "                  WHERE name = 'ldap_enable'),"
16671                     "                 0);");
16672   return 0;
16673 }
16674 
16675 /**
16676  * @brief Check whether RADIUS is enabled.
16677  *
16678  * @return 0 no, else yes.
16679  */
16680 static int
radius_auth_enabled()16681 radius_auth_enabled ()
16682 {
16683   if (gvm_auth_radius_enabled ())
16684     return sql_int ("SELECT coalesce ((SELECT CAST (value AS INTEGER) FROM meta"
16685                     "                  WHERE name = 'radius_enable'),"
16686                     "                 0);");
16687   return 0;
16688 }
16689 
16690 
16691 /**
16692  * @brief Check if user exists.
16693  *
16694  * @param[in]  name    User name.
16695  * @param[in]  method  Auth method.
16696  *
16697  * @return 1 yes, 0 no.
16698  */
16699 static int
user_exists_method(const gchar * name,auth_method_t method)16700 user_exists_method (const gchar *name, auth_method_t method)
16701 {
16702   gchar *quoted_name, *quoted_method;
16703   int ret;
16704 
16705   quoted_name = sql_quote (name);
16706   quoted_method = sql_quote (auth_method_name (method));
16707   ret = sql_int ("SELECT count (*) FROM users"
16708                  " WHERE name = '%s' AND method = '%s';",
16709                  quoted_name,
16710                  quoted_method);
16711   g_free (quoted_name);
16712   g_free (quoted_method);
16713 
16714   return ret;
16715 }
16716 
16717 /**
16718  * @brief Get user uuid, trying all authentication methods.
16719  *
16720  * @param[in]  name    User name.
16721  *
16722  * @return UUID.
16723  */
16724 static gchar *
user_uuid_any_method(const gchar * name)16725 user_uuid_any_method (const gchar *name)
16726 {
16727   if (ldap_auth_enabled ()
16728       && user_exists_method (name, AUTHENTICATION_METHOD_LDAP_CONNECT))
16729     return user_uuid_method (name, AUTHENTICATION_METHOD_LDAP_CONNECT);
16730   if (radius_auth_enabled ()
16731       && user_exists_method (name, AUTHENTICATION_METHOD_RADIUS_CONNECT))
16732     return user_uuid_method (name, AUTHENTICATION_METHOD_RADIUS_CONNECT);
16733   if (user_exists_method (name, AUTHENTICATION_METHOD_FILE))
16734     return user_uuid_method (name, AUTHENTICATION_METHOD_FILE);
16735   return NULL;
16736 }
16737 
16738 /**
16739  * @brief Ensure the user exists in the database.
16740  *
16741  * @param[in]  name    User name.
16742  * @param[in]  method  Auth method.
16743  *
16744  * @return 0 success.
16745  */
16746 static int
user_ensure_in_db(const gchar * name,const gchar * method)16747 user_ensure_in_db (const gchar *name, const gchar *method)
16748 {
16749   gchar *quoted_name, *quoted_method;
16750 
16751   if ((method == NULL) || (strcasecmp (method, "file") == 0))
16752     /* A "file" user, now entirely stored in db. */
16753     return 0;
16754 
16755   /* SELECT then INSERT instead of using "INSERT OR REPLACE", so that the
16756    * id stays the same. */
16757 
16758   quoted_name = sql_quote (name);
16759   quoted_method = sql_quote (method);
16760 
16761   if (sql_int ("SELECT count(*)"
16762                " FROM users WHERE name = '%s' and method = '%s';",
16763                quoted_name,
16764                quoted_method))
16765     {
16766       g_free (quoted_method);
16767       g_free (quoted_name);
16768       return 0;
16769     }
16770 
16771   sql ("INSERT INTO users"
16772        " (uuid, owner, name, comment, password, timezone, method, hosts,"
16773        "  hosts_allow, ifaces, ifaces_allow, creation_time, modification_time)"
16774        " VALUES"
16775        " (make_uuid (),"
16776        "  (SELECT id FROM users WHERE users.uuid = '%s'),"
16777        "  '%s', '', NULL, NULL, '%s', '', 2, '', 2, m_now (), m_now ());",
16778        current_credentials.uuid,
16779        quoted_name,
16780        quoted_method);
16781 
16782   g_free (quoted_method);
16783   g_free (quoted_name);
16784 
16785   return 0;
16786 }
16787 
16788 /**
16789  * @brief Check if user exists.
16790  *
16791  * @param[in]  name    User name.
16792  *
16793  * @return 1 yes, 0 no.
16794  */
16795 static int
user_exists(const gchar * name)16796 user_exists (const gchar *name)
16797 {
16798   if (ldap_auth_enabled ()
16799       && user_exists_method (name, AUTHENTICATION_METHOD_LDAP_CONNECT))
16800     return 1;
16801   if (radius_auth_enabled ()
16802       && user_exists_method (name, AUTHENTICATION_METHOD_RADIUS_CONNECT))
16803     return 1;
16804   return user_exists_method (name, AUTHENTICATION_METHOD_FILE);
16805 }
16806 
16807 /**
16808  * @brief Set credentials for authenticate.
16809  *
16810  * @param[in]  credentials  Credentials.
16811  *
16812  * @return 0 success, 99 permission denied.
16813  */
16814 static int
credentials_setup(credentials_t * credentials)16815 credentials_setup (credentials_t *credentials)
16816 {
16817   assert (credentials->uuid);
16818 
16819   credentials->role
16820     = g_strdup (acl_user_is_super_admin (credentials->uuid)
16821                  ? "Super Admin"
16822                  : (acl_user_is_admin (credentials->uuid)
16823                      ? "Admin"
16824                      : (acl_user_is_observer (credentials->uuid)
16825                          ? "Observer"
16826                          : (acl_user_is_user (credentials->uuid)
16827                              ? "User"
16828                              : ""))));
16829 
16830   if (acl_user_may ("authenticate") == 0)
16831     {
16832       free (credentials->uuid);
16833       credentials->uuid = NULL;
16834       g_free (credentials->role);
16835       credentials->role = NULL;
16836       return 99;
16837     }
16838 
16839   credentials->timezone = sql_string ("SELECT timezone FROM users"
16840                                       " WHERE uuid = '%s';",
16841                                       credentials->uuid);
16842 
16843   credentials->severity_class
16844     = sql_string ("SELECT value FROM settings"
16845                   " WHERE name = 'Severity Class'"
16846                   " AND " ACL_GLOBAL_OR_USER_OWNS ()
16847                   " ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
16848                   credentials->uuid);
16849 
16850   credentials->dynamic_severity
16851     = sql_int ("SELECT value FROM settings"
16852                 " WHERE name = 'Dynamic Severity'"
16853                 " AND " ACL_GLOBAL_OR_USER_OWNS ()
16854                 " ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
16855                 credentials->uuid);
16856 
16857   credentials->default_severity
16858     = sql_double ("SELECT value FROM settings"
16859                   " WHERE name = 'Default Severity'"
16860                   " AND " ACL_GLOBAL_OR_USER_OWNS ()
16861                   " ORDER BY coalesce (owner, 0) DESC LIMIT 1;",
16862                   credentials->uuid);
16863 
16864   return 0;
16865 }
16866 
16867 /**
16868  * @brief Search for LDAP or RADIUS credentials in the recently-used
16869  * authentication cache.
16870  *
16871  * @param[in]  username     Username.
16872  * @param[in]  password     Password.
16873  * @param[in]  method       0 for LDAP, 1 for RADIUS.
16874  *
16875  * @return 0 on success, -1 on failure.
16876  */
16877 static int
auth_cache_find(const char * username,const char * password,int method)16878 auth_cache_find (const char *username, const char *password, int method)
16879 {
16880   char *hash, *quoted_username;
16881   int ret;
16882 
16883   quoted_username = sql_quote (username);
16884   hash = sql_string ("SELECT hash FROM auth_cache WHERE username = '%s'"
16885                      " AND method = %i AND creation_time >= m_now () - 300;",
16886                      quoted_username, method);
16887   g_free (quoted_username);
16888   if (!hash)
16889     return -1;
16890 
16891   // verify for VALID or OUTDATED but don't update
16892   ret = manage_authentication_verify(hash, password);
16893   switch(ret){
16894       case GMA_HASH_INVALID:
16895           ret = 1;
16896           break;
16897        case GMA_HASH_VALID_BUT_DATED:
16898           ret = 0;
16899           break;
16900         case GMA_SUCCESS:
16901           ret = 0;
16902           break;
16903         default:
16904           ret = -1;
16905           break;
16906   }
16907   g_free (hash);
16908   return ret;
16909 }
16910 
16911 /**
16912  * @brief Add LDAP or RADIUS credentials to the recently-used authentication
16913  * cache.
16914  *
16915  * @param[in]  username     Username.
16916  * @param[in]  password     Password.
16917  * @param[in]  method       0 for LDAP, 1 for RADIUS.
16918  */
16919 static void
auth_cache_insert(const char * username,const char * password,int method)16920 auth_cache_insert (const char *username, const char *password, int method)
16921 {
16922   char *hash, *quoted_username;
16923 
16924   quoted_username = sql_quote (username);
16925   hash = manage_authentication_hash(password);
16926   sql ("INSERT INTO auth_cache (username, hash, method, creation_time)"
16927        " VALUES ('%s', '%s', %i, m_now ());", quoted_username, hash, method);
16928   /* Cleanup cache */
16929   sql ("DELETE FROM auth_cache WHERE creation_time < m_now () - 300");
16930 }
16931 
16932 /**
16933  * @brief Authenticate, trying any method.
16934  *
16935  * @param[in]  username     Username.
16936  * @param[in]  password     Password.
16937  * @param[out] auth_method  Auth method return.
16938  *
16939  * @return 0 authentication success, 1 authentication failure, 99 permission
16940  *         denied, -1 error.
16941  */
16942 static int
authenticate_any_method(const gchar * username,const gchar * password,auth_method_t * auth_method)16943 authenticate_any_method (const gchar *username, const gchar *password,
16944                          auth_method_t *auth_method)
16945 {
16946   int ret;
16947   gchar *hash;
16948 
16949   if (gvm_auth_ldap_enabled ()
16950       && ldap_auth_enabled ()
16951       && user_exists_method (username, AUTHENTICATION_METHOD_LDAP_CONNECT))
16952     {
16953       ldap_auth_info_t info;
16954       int allow_plaintext;
16955       gchar *authdn, *host, *cacert;
16956 
16957       *auth_method = AUTHENTICATION_METHOD_LDAP_CONNECT;
16958       /* Search the LDAP authentication cache first. */
16959       if (auth_cache_find (username, password, 0) == 0)
16960         return 0;
16961 
16962       manage_get_ldap_info (NULL, &host, &authdn, &allow_plaintext, &cacert);
16963       info = ldap_auth_info_new (host, authdn, allow_plaintext);
16964       g_free (host);
16965       g_free (authdn);
16966       ret = ldap_connect_authenticate (username, password, info, cacert);
16967       ldap_auth_info_free (info);
16968       free (cacert);
16969 
16970       if (ret == 0)
16971         auth_cache_insert (username, password, 0);
16972       return ret;
16973     }
16974   if (gvm_auth_radius_enabled ()
16975       && radius_auth_enabled ()
16976       && user_exists_method (username, AUTHENTICATION_METHOD_RADIUS_CONNECT))
16977     {
16978       char *key = NULL, *host = NULL;
16979 
16980       *auth_method = AUTHENTICATION_METHOD_RADIUS_CONNECT;
16981       if (auth_cache_find (username, password, 1) == 0)
16982         return 0;
16983 
16984       manage_get_radius_info (NULL, &host, &key);
16985       ret = radius_authenticate (host, key, username, password);
16986       g_free (host);
16987       g_free (key);
16988       if (ret == 0)
16989         auth_cache_insert (username, password, 1);
16990       return ret;
16991     }
16992   *auth_method = AUTHENTICATION_METHOD_FILE;
16993   hash = manage_user_hash (username);
16994   ret = manage_authentication_verify(hash, password);
16995   switch(ret){
16996       case GMA_HASH_INVALID:
16997           ret = 1;
16998           break;
16999        case GMA_HASH_VALID_BUT_DATED:
17000           g_free(hash);
17001           hash = manage_authentication_hash(password);
17002           sql ("UPDATE users SET password = '%s', modification_time = m_now () WHERE name = '%s';",
17003                hash, username);
17004           ret = 0;
17005           break;
17006         case GMA_SUCCESS:
17007           ret = 0;
17008           break;
17009         default:
17010           ret = -1;
17011           break;
17012   }
17013 
17014 
17015   g_free (hash);
17016   return ret;
17017 }
17018 
17019 /**
17020  * @brief Authenticate credentials.
17021  *
17022  * @param[in]  credentials  Credentials.
17023  *
17024  * @return 0 authentication success, 1 authentication failure, 99 permission
17025  *         denied, -1 error.
17026  */
17027 int
authenticate(credentials_t * credentials)17028 authenticate (credentials_t* credentials)
17029 {
17030   if (credentials->username && credentials->password)
17031     {
17032       int fail;
17033       auth_method_t auth_method;
17034 
17035       if (authenticate_allow_all)
17036         {
17037           /* This flag is set when Manager makes a connection to itself, for
17038            * scheduled tasks and alerts.  Take the stored uuid
17039            * to be able to tell apart locally authenticated vs remotely
17040            * authenticated users (in order to fetch the correct rules). */
17041           credentials->uuid = g_strdup (get_scheduled_user_uuid ());
17042           if (*credentials->uuid)
17043             {
17044               if (credentials_setup (credentials))
17045                 return 99;
17046 
17047               manage_session_init (credentials->uuid);
17048               return 0;
17049             }
17050           return -1;
17051         }
17052 
17053       fail = authenticate_any_method (credentials->username,
17054                                       credentials->password,
17055                                       &auth_method);
17056       if (fail == 0)
17057         {
17058           gchar *quoted_name, *quoted_method;
17059 
17060           /* Authentication succeeded. */
17061 
17062           user_ensure_in_db (credentials->username,
17063                              auth_method_name (auth_method));
17064 
17065           quoted_name = sql_quote (credentials->username);
17066           quoted_method = sql_quote (auth_method_name (auth_method));
17067           credentials->uuid = sql_string ("SELECT uuid FROM users"
17068                                           " WHERE name = '%s'"
17069                                           " AND method = '%s';",
17070                                           quoted_name,
17071                                           quoted_method);
17072           g_free (quoted_name);
17073           g_free (quoted_method);
17074 
17075           if (credentials_setup (credentials))
17076             {
17077               free (credentials->uuid);
17078               credentials->uuid = NULL;
17079               credentials->role = NULL;
17080               return 99;
17081             }
17082 
17083           manage_session_init (credentials->uuid);
17084 
17085           return 0;
17086         }
17087       return fail;
17088     }
17089   return 1;
17090 }
17091 
17092 /**
17093  * @brief Return number of resources of a certain type for current user.
17094  *
17095  * @param[in]  type  Type.
17096  * @param[in]  get   GET params.
17097  *
17098  * @return The number of resources associated with the current user.
17099  */
17100 int
resource_count(const char * type,const get_data_t * get)17101 resource_count (const char *type, const get_data_t *get)
17102 {
17103   static const char *filter_columns[] = { "owner", NULL };
17104   static column_t select_columns[] = {{ "owner", NULL }, { NULL, NULL }};
17105   get_data_t count_get;
17106   gchar *extra_where;
17107   int rc;
17108 
17109   memset (&count_get, '\0', sizeof (count_get));
17110   count_get.trash = get->trash;
17111   if (type_owned (type))
17112     count_get.filter = "rows=-1 first=1 permission=any owner=any";
17113   else
17114     count_get.filter = "rows=-1 first=1 permission=any";
17115 
17116   if (strcasecmp (type, "config") == 0)
17117     {
17118       const gchar *usage_type = get_data_get_extra (get, "usage_type");
17119       extra_where = configs_extra_where (usage_type);
17120     }
17121   else if (strcmp (type, "task") == 0)
17122     {
17123       const gchar *usage_type = get_data_get_extra (get, "usage_type");
17124       extra_where = tasks_extra_where (get->trash, usage_type);
17125     }
17126   else if (strcmp (type, "report") == 0)
17127     {
17128       extra_where = g_strdup (" AND (SELECT hidden FROM tasks"
17129                               "      WHERE tasks.id = task)"
17130                               "     = 0");
17131     }
17132   else if (strcmp (type, "result") == 0)
17133     {
17134       extra_where
17135         = g_strdup (" AND (severity != " G_STRINGIFY (SEVERITY_ERROR) ")");
17136     }
17137   else if (strcmp (type, "vuln") == 0)
17138     {
17139       extra_where = g_strdup (" AND vuln_results_exist"
17140                               "      (vulns.uuid,"
17141                               "       cast (null AS integer),"
17142                               "       cast (null AS integer),"
17143                               "       cast (null AS text))");
17144     }
17145   else
17146     extra_where = NULL;
17147 
17148   rc = count (get->subtype ? get->subtype : type,
17149               &count_get,
17150               type_owned (type) ? select_columns : NULL,
17151               type_owned (type) ? select_columns : NULL,
17152               type_owned (type) ? filter_columns : NULL,
17153               0, NULL,
17154               extra_where,
17155               type_owned (type));
17156 
17157   g_free (extra_where);
17158   return rc;
17159 }
17160 
17161 /**
17162  * @brief Return the number of tasks associated with the current user.
17163  *
17164  * @param[in]  get  GET params.
17165  *
17166  * @return The number of tasks associated with the current user.
17167  */
17168 unsigned int
task_count(const get_data_t * get)17169 task_count (const get_data_t *get)
17170 {
17171   static const char *extra_columns[] = TASK_ITERATOR_FILTER_COLUMNS;
17172   static column_t columns[] = TASK_ITERATOR_COLUMNS;
17173   static column_t where_columns[] = TASK_ITERATOR_WHERE_COLUMNS;
17174   char *filter;
17175   int overrides, min_qod;
17176   const char *usage_type;
17177   gchar *extra_tables, *extra_where;
17178   int ret;
17179 
17180   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
17181     {
17182       filter = filter_term (get->filt_id);
17183       if (filter == NULL)
17184         return 2;
17185     }
17186   else
17187     filter = NULL;
17188 
17189   overrides = filter_term_apply_overrides (filter ? filter : get->filter);
17190   min_qod = filter_term_min_qod (filter ? filter : get->filter);
17191 
17192   free (filter);
17193 
17194   extra_tables = task_iterator_opts_table (overrides, min_qod, 0);
17195   usage_type = get_data_get_extra (get, "usage_type");
17196   extra_where = tasks_extra_where (get->trash, usage_type);
17197 
17198   ret = count2 ("task", get,
17199                 columns,
17200                 columns,
17201                 where_columns,
17202                 where_columns,
17203                 extra_columns, 0,
17204                 extra_tables,
17205                 extra_where,
17206                 NULL,
17207                 TRUE);
17208 
17209   g_free (extra_tables);
17210   g_free (extra_where);
17211   return ret;
17212 }
17213 
17214 /**
17215  * @brief Return the UUID of a task.
17216  *
17217  * @param[in]   task  Task.
17218  * @param[out]  id    Pointer to a newly allocated string.
17219  *
17220  * @return 0.
17221  */
17222 int
task_uuid(task_t task,char ** id)17223 task_uuid (task_t task, char ** id)
17224 {
17225   *id = sql_string ("SELECT uuid FROM tasks WHERE id = %llu;",
17226                     task);
17227   return 0;
17228 }
17229 
17230 /**
17231  * @brief Return whether a task is in the trashcan.
17232  *
17233  * @param[in]  task  Task.
17234  *
17235  * @return 1 if in trashcan, else 0.
17236  */
17237 int
task_in_trash(task_t task)17238 task_in_trash (task_t task)
17239 {
17240   return sql_int ("SELECT hidden = 2"
17241                   " FROM tasks WHERE id = %llu;",
17242                   task);
17243 }
17244 
17245 /**
17246  * @brief Return whether a task is in the trashcan.
17247  *
17248  * Assume the UUID is properly formatted.
17249  *
17250  * @param[in]  task_id  Task UUID.
17251  *
17252  * @return 1 if in trashcan, else 0.
17253  */
17254 int
task_in_trash_id(const gchar * task_id)17255 task_in_trash_id (const gchar *task_id)
17256 {
17257   return sql_int ("SELECT hidden = 2"
17258                   " FROM tasks WHERE uuid = '%s';",
17259                   task_id);
17260 }
17261 
17262 /**
17263  * @brief Return the name of the owner of a task.
17264  *
17265  * @param[in]  task  Task.
17266  *
17267  * @return Newly allocated user name.
17268  */
17269 char*
task_owner_name(task_t task)17270 task_owner_name (task_t task)
17271 {
17272   return sql_string ("SELECT name FROM users WHERE id ="
17273                      " (SELECT owner FROM tasks WHERE id = %llu);",
17274                      task);
17275 }
17276 
17277 /**
17278  * @brief Return the name of the owner of a task.
17279  *
17280  * @param[in]  task  Task.
17281  *
17282  * @return Newly allocated user name.
17283  */
17284 static char*
task_owner_uuid(task_t task)17285 task_owner_uuid (task_t task)
17286 {
17287   return sql_string ("SELECT uuid FROM users WHERE id ="
17288                      " (SELECT owner FROM tasks WHERE id = %llu);",
17289                      task);
17290 }
17291 
17292 /**
17293  * @brief Return the name of a task.
17294  *
17295  * @param[in]  task  Task.
17296  *
17297  * @return Task name.
17298  */
17299 char*
task_name(task_t task)17300 task_name (task_t task)
17301 {
17302   return sql_string ("SELECT name FROM tasks WHERE id = %llu;",
17303                      task);
17304 }
17305 
17306 /**
17307  * @brief Return the comment of a task.
17308  *
17309  * @param[in]  task  Task.
17310  *
17311  * @return Comment of task.
17312  */
17313 char*
task_comment(task_t task)17314 task_comment (task_t task)
17315 {
17316   return sql_string ("SELECT comment FROM tasks WHERE id = %llu;",
17317                      task);
17318 }
17319 
17320 /**
17321  * @brief Return the hosts ordering of a task.
17322  *
17323  * @param[in]  task  Task.
17324  *
17325  * @return Hosts ordering of task.
17326  */
17327 char*
task_hosts_ordering(task_t task)17328 task_hosts_ordering (task_t task)
17329 {
17330   return sql_string ("SELECT hosts_ordering FROM tasks WHERE id = %llu;",
17331                      task);
17332 }
17333 
17334 /**
17335  * @brief Return the observers of a task.
17336  *
17337  * @param[in]  task  Task.
17338  *
17339  * @return Observers of task.
17340  */
17341 char*
task_observers(task_t task)17342 task_observers (task_t task)
17343 {
17344   iterator_t users;
17345   GString *observers;
17346 
17347   observers = g_string_new ("");
17348 
17349   init_task_user_iterator (&users, task);
17350   if (next (&users))
17351     {
17352       g_string_append (observers, task_user_iterator_name (&users));
17353       while (next (&users))
17354         g_string_append_printf (observers,
17355                                 " %s",
17356                                 task_user_iterator_name (&users));
17357     }
17358   cleanup_iterator (&users);
17359 
17360   return g_string_free (observers, FALSE);
17361 }
17362 
17363 /**
17364  * @brief Return the config of a task.
17365  *
17366  * @param[in]  task  Task.
17367  *
17368  * @return Config of task.
17369  */
17370 config_t
task_config(task_t task)17371 task_config (task_t task)
17372 {
17373   config_t config;
17374   switch (sql_int64 (&config,
17375                      "SELECT config FROM tasks WHERE id = %llu;",
17376                      task))
17377     {
17378       case 0:
17379         return config;
17380       default:       /* Programming error. */
17381       case 1:        /* Too few rows in result of query. */
17382       case -1:       /* Error. */
17383         /* Every task should have a config. */
17384         assert (0);
17385         return 0;
17386         break;
17387     }
17388 }
17389 
17390 /**
17391  * @brief Return the UUID of the config of a task.
17392  *
17393  * @param[in]  task  Task.
17394  *
17395  * @return UUID of config of task.
17396  */
17397 char*
task_config_uuid(task_t task)17398 task_config_uuid (task_t task)
17399 {
17400   if (task_config_in_trash (task))
17401     return sql_string ("SELECT uuid FROM configs_trash WHERE id ="
17402                        " (SELECT config FROM tasks WHERE id = %llu);",
17403                        task);
17404   return sql_string ("SELECT uuid FROM configs WHERE id ="
17405                      " (SELECT config FROM tasks WHERE id = %llu);",
17406                      task);
17407 }
17408 
17409 /**
17410  * @brief Return the name of the config of a task.
17411  *
17412  * @param[in]  task  Task.
17413  *
17414  * @return Name of config of task.
17415  */
17416 char*
task_config_name(task_t task)17417 task_config_name (task_t task)
17418 {
17419   if (task_config_in_trash (task))
17420     return sql_string ("SELECT name FROM configs_trash WHERE id ="
17421                        " (SELECT config FROM tasks WHERE id = %llu);",
17422                        task);
17423   return sql_string ("SELECT name FROM configs WHERE id ="
17424                      " (SELECT config FROM tasks WHERE id = %llu);",
17425                      task);
17426 }
17427 
17428 /**
17429  * @brief Return whether the config of a task is in the trashcan.
17430  *
17431  * @param[in]  task  Task.
17432  *
17433  * @return 1 if in trashcan, else 0.
17434  */
17435 int
task_config_in_trash(task_t task)17436 task_config_in_trash (task_t task)
17437 {
17438   return sql_int ("SELECT config_location = " G_STRINGIFY (LOCATION_TRASH)
17439                   " FROM tasks WHERE id = %llu;",
17440                   task);
17441 }
17442 
17443 /**
17444  * @brief Set the config of a task.
17445  *
17446  * @param[in]  task    Task.
17447  * @param[in]  config  Config.
17448  */
17449 void
set_task_config(task_t task,config_t config)17450 set_task_config (task_t task, config_t config)
17451 {
17452   sql ("UPDATE tasks SET config = %llu, modification_time = m_now ()"
17453        " WHERE id = %llu;",
17454        config,
17455        task);
17456 }
17457 
17458 /**
17459  * @brief Return the target of a task.
17460  *
17461  * @param[in]  task  Task.
17462  *
17463  * @return Target of task.
17464  */
17465 target_t
task_target(task_t task)17466 task_target (task_t task)
17467 {
17468   target_t target = 0;
17469   switch (sql_int64 (&target,
17470                      "SELECT target FROM tasks WHERE id = %llu;",
17471                      task))
17472     {
17473       case 0:
17474         return target;
17475         break;
17476       case 1:        /* Too few rows in result of query. */
17477       default:       /* Programming error. */
17478         assert (0);
17479       case -1:
17480         return 0;
17481         break;
17482     }
17483 }
17484 
17485 /**
17486  * @brief Set the target of a task.
17487  *
17488  * @param[in]  task    Task.
17489  * @param[in]  target  Target.
17490  */
17491 void
set_task_target(task_t task,target_t target)17492 set_task_target (task_t task, target_t target)
17493 {
17494   sql ("UPDATE tasks SET target = %llu, modification_time = m_now ()"
17495        " WHERE id = %llu;",
17496        target,
17497        task);
17498 }
17499 
17500 /**
17501  * @brief Set the hosts ordering of a task.
17502  *
17503  * @param[in]  task         Task.
17504  * @param[in]  ordering     Hosts ordering.
17505  */
17506 void
set_task_hosts_ordering(task_t task,const char * ordering)17507 set_task_hosts_ordering (task_t task, const char *ordering)
17508 {
17509   char *quoted_ordering = sql_quote (ordering ?: "");
17510   sql ("UPDATE tasks SET hosts_ordering = '%s', modification_time = m_now ()"
17511        " WHERE id = %llu;", quoted_ordering, task);
17512   g_free (quoted_ordering);
17513 }
17514 
17515 /**
17516  * @brief Return whether the target of a task is in the trashcan.
17517  *
17518  * @param[in]  task  Task.
17519  *
17520  * @return 1 if in trash, else 0.
17521  */
17522 int
task_target_in_trash(task_t task)17523 task_target_in_trash (task_t task)
17524 {
17525   return sql_int ("SELECT target_location = " G_STRINGIFY (LOCATION_TRASH)
17526                   " FROM tasks WHERE id = %llu;",
17527                   task);
17528 }
17529 
17530 /**
17531  * @brief Return the scanner of a task.
17532  *
17533  * @param[in]  task  Task.
17534  *
17535  * @return scanner of task.
17536  */
17537 scanner_t
task_scanner(task_t task)17538 task_scanner (task_t task)
17539 {
17540   scanner_t scanner = 0;
17541   switch (sql_int64 (&scanner, "SELECT scanner FROM tasks WHERE id = %llu;",
17542                      task))
17543     {
17544       case 0:
17545         return scanner;
17546         break;
17547       case 1:        /* Too few rows in result of query. */
17548       default:       /* Programming error. */
17549         assert (0);
17550       case -1:
17551         return 0;
17552         break;
17553     }
17554 }
17555 
17556 /**
17557  * @brief Set the scanner of a task.
17558  *
17559  * @param[in]  task     Task.
17560  * @param[in]  scanner  Scanner.
17561  */
17562 void
set_task_scanner(task_t task,scanner_t scanner)17563 set_task_scanner (task_t task, scanner_t scanner)
17564 {
17565   sql ("UPDATE tasks SET scanner = %llu, modification_time = m_now ()"
17566        " WHERE id = %llu;", scanner, task);
17567   if (scanner_type (scanner) == SCANNER_TYPE_CVE)
17568     sql ("UPDATE tasks SET config = 0 WHERE id = %llu;", task);
17569 }
17570 
17571 /**
17572  * @brief Return whether the scanner of a task is in the trashcan.
17573  *
17574  * @param[in]  task  Task.
17575  *
17576  * @return 1 if in trash, else 0.
17577  */
17578 int
task_scanner_in_trash(task_t task)17579 task_scanner_in_trash (task_t task)
17580 {
17581   return sql_int ("SELECT scanner_location = " G_STRINGIFY (LOCATION_TRASH)
17582                   " FROM tasks WHERE id = %llu;", task);
17583 }
17584 
17585 /**
17586  * @brief Set the usage_type of a task.
17587  *
17588  * @param[in]  task       Task.
17589  * @param[in]  usage_type New usage type ("scan" or "audit").
17590  */
17591 void
set_task_usage_type(task_t task,const char * usage_type)17592 set_task_usage_type (task_t task, const char *usage_type)
17593 {
17594   const char *actual_usage_type;
17595   if (usage_type && strcasecmp (usage_type, "audit") == 0)
17596     actual_usage_type = "audit";
17597   else
17598     actual_usage_type = "scan";
17599 
17600   sql ("UPDATE tasks SET usage_type = '%s', modification_time = m_now ()"
17601        " WHERE id = %llu;", actual_usage_type, task);
17602 }
17603 
17604 /**
17605  * @brief Return the run state of a task.
17606  *
17607  * @param[in]  task  Task.
17608  *
17609  * @return Task run status.
17610  */
17611 task_status_t
task_run_status(task_t task)17612 task_run_status (task_t task)
17613 {
17614   return (unsigned int) sql_int ("SELECT run_status FROM tasks WHERE id = %llu;",
17615                                  task);
17616 }
17617 
17618 /**
17619  * @brief Set a report's scheduled flag.
17620  *
17621  * Set flag if task was scheduled, else clear flag.
17622  *
17623  * @param[in]   report  Report.
17624  */
17625 void
set_report_scheduled(report_t report)17626 set_report_scheduled (report_t report)
17627 {
17628   if (authenticate_allow_all == 1)
17629     /* The task was scheduled. */
17630     sql ("UPDATE reports SET flags = 1 WHERE id = %llu;",
17631          report);
17632   else
17633     sql ("UPDATE reports SET flags = 0 WHERE id = %llu;",
17634          report);
17635 }
17636 
17637 /**
17638  * @brief Get a report's scheduled flag.
17639  *
17640  * @param[in]   report  Report.
17641  *
17642  * @return Scheduled flag.
17643  */
17644 static int
report_scheduled(report_t report)17645 report_scheduled (report_t report)
17646 {
17647   return sql_int ("SELECT flags FROM reports WHERE id = %llu;",
17648                   report);
17649 }
17650 
17651 /**
17652  * @brief Set the run state of a task.
17653  *
17654  * @param[in]  task    Task.
17655  * @param[in]  status  New run status.
17656  */
17657 static void
set_task_run_status_internal(task_t task,task_status_t status)17658 set_task_run_status_internal (task_t task, task_status_t status)
17659 {
17660   if ((task == current_scanner_task) && global_current_report)
17661     {
17662       sql ("UPDATE reports SET scan_run_status = %u WHERE id = %llu;",
17663            status,
17664            global_current_report);
17665       if (setting_auto_cache_rebuild_int ())
17666         report_cache_counts (global_current_report, 0, 0, NULL);
17667     }
17668 
17669   sql ("UPDATE tasks SET run_status = %u WHERE id = %llu;",
17670        status,
17671        task);
17672 }
17673 
17674 /**
17675  * @brief Set the run state of a task.
17676  *
17677  * Logs and generates event.
17678  *
17679  * @param[in]  task    Task.
17680  * @param[in]  status  New run status.
17681  */
17682 void
set_task_run_status(task_t task,task_status_t status)17683 set_task_run_status (task_t task, task_status_t status)
17684 {
17685   char *uuid;
17686   char *name;
17687 
17688   set_task_run_status_internal (task, status);
17689 
17690   task_uuid (task, &uuid);
17691   name = task_name (task);
17692   g_log ("event task", G_LOG_LEVEL_MESSAGE,
17693          "Status of task %s (%s) has changed to %s",
17694          name, uuid, run_status_name (status));
17695   free (uuid);
17696   free (name);
17697 
17698   event (EVENT_TASK_RUN_STATUS_CHANGED,
17699          (void*) status,
17700          task,
17701          (task == current_scanner_task) ? global_current_report : 0);
17702 }
17703 
17704 /**
17705  * @brief Return number of results in a task.
17706  *
17707  * @param[in]  task     Task.
17708  * @param[in]  min_qod  Minimum QOD.
17709  *
17710  * @return Result count.
17711  */
17712 int
task_result_count(task_t task,int min_qod)17713 task_result_count (task_t task, int min_qod)
17714 {
17715   return sql_int ("SELECT count (*) FROM results"
17716                   " WHERE task = %llu"
17717                   " AND qod > %i"
17718                   " AND severity > " G_STRINGIFY (SEVERITY_ERROR) ";",
17719                   task,
17720                   min_qod);
17721 }
17722 
17723 /**
17724  * @brief Return the running report of a task.
17725  *
17726  * @param[in]  task  Task.
17727  *
17728  * @return Current report of task if task is active, else (report_t) 0.
17729  */
17730 report_t
task_running_report(task_t task)17731 task_running_report (task_t task)
17732 {
17733   task_status_t run_status = task_run_status (task);
17734   if (run_status == TASK_STATUS_REQUESTED
17735       || run_status == TASK_STATUS_RUNNING
17736       || run_status == TASK_STATUS_QUEUED)
17737     {
17738       return (unsigned int) sql_int ("SELECT max(id) FROM reports"
17739                                      " WHERE task = %llu AND end_time IS NULL"
17740                                      " AND (scan_run_status = %u "
17741                                      " OR scan_run_status = %u);",
17742                                      task,
17743                                      TASK_STATUS_RUNNING,
17744                                      TASK_STATUS_QUEUED);
17745     }
17746   return (report_t) 0;
17747 }
17748 
17749 /**
17750  * @brief Return the current report of a task.
17751  *
17752  * @param[in]  iterator  Iterator.
17753  *
17754  * @return Current report of task if task is active, else (report_t) 0.
17755  */
17756 report_t
task_iterator_current_report(iterator_t * iterator)17757 task_iterator_current_report (iterator_t *iterator)
17758 {
17759   task_t task = get_iterator_resource (iterator);
17760   task_status_t run_status = task_iterator_run_status (iterator);
17761   if (run_status == TASK_STATUS_REQUESTED
17762       || run_status == TASK_STATUS_RUNNING
17763       || run_status == TASK_STATUS_QUEUED
17764       || run_status == TASK_STATUS_DELETE_REQUESTED
17765       || run_status == TASK_STATUS_DELETE_ULTIMATE_REQUESTED
17766       || run_status == TASK_STATUS_STOP_REQUESTED
17767       || run_status == TASK_STATUS_STOPPED
17768       || run_status == TASK_STATUS_INTERRUPTED)
17769     {
17770       return (unsigned int) sql_int ("SELECT max(id) FROM reports"
17771                                      " WHERE task = %llu"
17772                                      " AND (scan_run_status = %u"
17773                                      " OR scan_run_status = %u"
17774                                      " OR scan_run_status = %u"
17775                                      " OR scan_run_status = %u"
17776                                      " OR scan_run_status = %u"
17777                                      " OR scan_run_status = %u"
17778                                      " OR scan_run_status = %u"
17779                                      " OR scan_run_status = %u);",
17780                                      task,
17781                                      TASK_STATUS_REQUESTED,
17782                                      TASK_STATUS_RUNNING,
17783                                      TASK_STATUS_QUEUED,
17784                                      TASK_STATUS_DELETE_REQUESTED,
17785                                      TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
17786                                      TASK_STATUS_STOP_REQUESTED,
17787                                      TASK_STATUS_STOPPED,
17788                                      TASK_STATUS_INTERRUPTED);
17789     }
17790   return (report_t) 0;
17791 }
17792 
17793 /**
17794  * @brief Return the upload progress of a task.
17795  *
17796  * @param[in]  task  Task.
17797  *
17798  * @return Task upload progress, as a percentage, or -1 on error.
17799  */
17800 int
task_upload_progress(task_t task)17801 task_upload_progress (task_t task)
17802 {
17803   report_t report;
17804   report = task_running_report (task);
17805   if (report)
17806     {
17807       int count;
17808       get_data_t get;
17809       memset (&get, 0, sizeof (get_data_t));
17810       get.filter = g_strdup ("min_qod=0");
17811       count = result_count (&get, report, NULL);
17812       get_data_reset (&get);
17813 
17814       return sql_int ("SELECT"
17815                       " greatest (least (((%i * 100) / upload_result_count), 100), -1)"
17816                       " FROM tasks"
17817                       " WHERE id = %llu;",
17818                       count,
17819                       task);
17820     }
17821   return -1;
17822 }
17823 
17824 /**
17825  * @brief Set the start time of a task.
17826  *
17827  * @param[in]  task  Task.
17828  * @param[in]  time  New time.  Seconds since epoch.
17829  */
17830 void
set_task_start_time_epoch(task_t task,int time)17831 set_task_start_time_epoch (task_t task, int time)
17832 {
17833   sql ("UPDATE tasks SET start_time = %i, modification_time = m_now ()"
17834        " WHERE id = %llu;",
17835        time,
17836        task);
17837 }
17838 
17839 /**
17840  * @brief Set the start time of a task.
17841  *
17842  * @param[in]  task  Task.
17843  * @param[in]  time  New time.  UTC ctime format.  Freed before return.
17844  */
17845 void
set_task_start_time_ctime(task_t task,char * time)17846 set_task_start_time_ctime (task_t task, char* time)
17847 {
17848   sql ("UPDATE tasks SET start_time = %i, modification_time = m_now ()"
17849        " WHERE id = %llu;",
17850        parse_utc_ctime (time),
17851        task);
17852   free (time);
17853 }
17854 
17855 /**
17856  * @brief Get most recently completed report that precedes a report.
17857  *
17858  * @param[in]  task      The task.
17859  * @param[out] report    Report.
17860  * @param[out] previous  Report return, 0 if successfully failed to select report.
17861  *
17862  * @return 0 success, -1 error.
17863  */
17864 static int
task_report_previous(task_t task,report_t report,report_t * previous)17865 task_report_previous (task_t task, report_t report, report_t *previous)
17866 {
17867   switch (sql_int64 (previous,
17868                      "SELECT id FROM reports"
17869                      " WHERE task = %llu"
17870                      " AND scan_run_status = %u"
17871                      " AND date < (SELECT date FROM reports"
17872                      "             WHERE id = %llu)"
17873                      " ORDER BY date DESC LIMIT 1;",
17874                      task,
17875                      TASK_STATUS_DONE,
17876                      report))
17877     {
17878       case 0:
17879         break;
17880       case 1:        /* Too few rows in result of query. */
17881         *previous = 0;
17882         return 0;
17883         break;
17884       default:       /* Programming error. */
17885         assert (0);
17886       case -1:
17887         return -1;
17888         break;
17889     }
17890   return 0;
17891 }
17892 
17893 /**
17894  * @brief Get the report from the most recently completed invocation of task.
17895  *
17896  * @param[in]  task    The task.
17897  * @param[out] report  Report return, 0 if successfully failed to select report.
17898  *
17899  * @return 0 success, -1 error.
17900  */
17901 int
task_last_report(task_t task,report_t * report)17902 task_last_report (task_t task, report_t *report)
17903 {
17904   switch (sql_int64 (report,
17905                      "SELECT id FROM reports WHERE task = %llu"
17906                      " AND scan_run_status = %u"
17907                      " ORDER BY date DESC LIMIT 1;",
17908                      task,
17909                      TASK_STATUS_DONE))
17910     {
17911       case 0:
17912         break;
17913       case 1:        /* Too few rows in result of query. */
17914         *report = 0;
17915         return 0;
17916         break;
17917       default:       /* Programming error. */
17918         assert (0);
17919       case -1:
17920         return -1;
17921         break;
17922     }
17923   return 0;
17924 }
17925 
17926 /**
17927  * @brief Get the report from the most recently invocation of task.
17928  *
17929  * @param[in]  task    The task.
17930  * @param[out] report  Report return, 0 if successfully failed to select report.
17931  *
17932  * @return 0 success, -1 error.
17933  */
17934 static int
task_last_report_any_status(task_t task,report_t * report)17935 task_last_report_any_status (task_t task, report_t *report)
17936 {
17937   switch (sql_int64 (report,
17938                      "SELECT id FROM reports WHERE task = %llu"
17939                      " ORDER BY date DESC LIMIT 1;",
17940                      task))
17941     {
17942       case 0:
17943         break;
17944       case 1:        /* Too few rows in result of query. */
17945         *report = 0;
17946         return 0;
17947         break;
17948       default:       /* Programming error. */
17949         assert (0);
17950       case -1:
17951         return -1;
17952         break;
17953     }
17954   return 0;
17955 }
17956 
17957 /**
17958  * @brief Get the report from second most recently completed invocation of task.
17959  *
17960  * @param[in]  task    The task.
17961  * @param[out] report  Report return, 0 if successfully failed to select report.
17962  *
17963  * @return 0 success, -1 error.
17964  */
17965 static int
task_second_last_report(task_t task,report_t * report)17966 task_second_last_report (task_t task, report_t *report)
17967 {
17968   switch (sql_int64 (report,
17969                      "SELECT id FROM reports WHERE task = %llu"
17970                      " AND scan_run_status = %u"
17971                      " ORDER BY date DESC LIMIT 1 OFFSET 1;",
17972                      task,
17973                      TASK_STATUS_DONE))
17974     {
17975       case 0:
17976         break;
17977       case 1:        /* Too few rows in result of query. */
17978         *report = 0;
17979         return 0;
17980         break;
17981       default:       /* Programming error. */
17982         assert (0);
17983       case -1:
17984         return -1;
17985         break;
17986     }
17987   return 0;
17988 }
17989 
17990 /**
17991  * @brief Get the report from the most recently stopped invocation of task.
17992  *
17993  * @param[in]  task    The task.
17994  * @param[out] report  Report return, 0 if successfully failed to select report.
17995  *
17996  * @return 0 success, -1 error.
17997  */
17998 int
task_last_resumable_report(task_t task,report_t * report)17999 task_last_resumable_report (task_t task, report_t *report)
18000 {
18001   switch (sql_int64 (report,
18002                      "SELECT id FROM reports WHERE task = %llu"
18003                      " AND (scan_run_status = %u"
18004                      "      OR scan_run_status = %u)"
18005                      " ORDER BY date DESC LIMIT 1;",
18006                      task,
18007                      TASK_STATUS_STOPPED,
18008                      TASK_STATUS_INTERRUPTED))
18009     {
18010       case 0:
18011         break;
18012       case 1:        /* Too few rows in result of query. */
18013         *report = 0;
18014         return 0;
18015         break;
18016       default:       /* Programming error. */
18017         assert (0);
18018       case -1:
18019         return -1;
18020         break;
18021     }
18022   return 0;
18023 }
18024 
18025 /**
18026  * @brief Get report ID from second most recently completed invocation of task.
18027  *
18028  * @param[in]  task  The task.
18029  *
18030  * @return The UUID of the report as a newly allocated string.
18031  */
18032 gchar*
task_second_last_report_id(task_t task)18033 task_second_last_report_id (task_t task)
18034 {
18035   return sql_string ("SELECT uuid FROM reports WHERE task = %llu"
18036                      " AND scan_run_status = %u"
18037                      " ORDER BY date DESC LIMIT 1 OFFSET 1;",
18038                      task,
18039                      TASK_STATUS_DONE);
18040 }
18041 
18042 /**
18043  * @brief Add an alert to a task.
18044  *
18045  * @param[in]  task       Task.
18046  * @param[in]  alert  Alert.
18047  */
18048 void
add_task_alert(task_t task,alert_t alert)18049 add_task_alert (task_t task, alert_t alert)
18050 {
18051   sql ("INSERT INTO task_alerts (task, alert, alert_location)"
18052        " VALUES (%llu, %llu, " G_STRINGIFY (LOCATION_TABLE) ");",
18053        task,
18054        alert);
18055 }
18056 
18057 /**
18058  * @brief Set the alerts on a task, removing any previous alerts.
18059  *
18060  * @param[in]  task    Task.
18061  * @param[in]  alerts  Alerts.
18062  * @param[out] alert_id_return  ID of alert on "failed to find" error.
18063  *
18064  * @return 0 success, -1 error, 1 failed to find alert.
18065  */
18066 static int
set_task_alerts(task_t task,array_t * alerts,gchar ** alert_id_return)18067 set_task_alerts (task_t task, array_t *alerts, gchar **alert_id_return)
18068 {
18069   alert_t alert = 0;
18070   guint index;
18071 
18072   sql_begin_immediate ();
18073 
18074   sql ("DELETE FROM task_alerts where task = %llu;", task);
18075 
18076   index = alerts->len;
18077   while (index--)
18078     {
18079       gchar *alert_id;
18080 
18081       alert_id = (gchar*) g_ptr_array_index (alerts, index);
18082       if (strcmp (alert_id, "0") == 0)
18083         continue;
18084 
18085       if (find_alert_with_permission (alert_id, &alert, "get_alerts"))
18086         {
18087           sql_rollback ();
18088           return -1;
18089         }
18090 
18091       if (alert == 0)
18092         {
18093           sql_rollback ();
18094           if (alert_id_return) *alert_id_return = alert_id;
18095           return 1;
18096         }
18097 
18098       sql ("INSERT INTO task_alerts (task, alert, alert_location)"
18099            " VALUES (%llu, %llu, " G_STRINGIFY (LOCATION_TABLE) ");",
18100            task,
18101            alert);
18102     }
18103 
18104   sql_commit ();
18105   return 0;
18106 }
18107 
18108 /**
18109  * @brief Set the alterable state of a task.
18110  *
18111  * @param[in]  task       Task.
18112  * @param[in]  alterable  Whether task is alterable.
18113  */
18114 void
set_task_alterable(task_t task,int alterable)18115 set_task_alterable (task_t task, int alterable)
18116 {
18117   sql ("UPDATE tasks SET alterable = %i WHERE id = %llu;",
18118        alterable,
18119        task);
18120 }
18121 
18122 /**
18123  * @brief Set observer groups on a task, removing any previous groups.
18124  *
18125  * @param[in]  task    Task.
18126  * @param[in]  groups  Groups.
18127  * @param[out] group_id_return  ID of group on "failed to find" error.
18128  *
18129  * @return 0 success, -1 error, 1 failed to find group.
18130  */
18131 int
set_task_groups(task_t task,array_t * groups,gchar ** group_id_return)18132 set_task_groups (task_t task, array_t *groups, gchar **group_id_return)
18133 {
18134   group_t group = 0;
18135   guint index;
18136 
18137   sql_begin_immediate ();
18138 
18139   sql ("DELETE FROM permissions"
18140        " WHERE resource_type = 'task'"
18141        " AND resource = %llu"
18142        " AND subject_type = 'group'"
18143        " AND name = 'get';",
18144        task);
18145 
18146   index = 0;
18147   while (index < groups->len)
18148     {
18149       gchar *group_id;
18150 
18151       group_id = (gchar*) g_ptr_array_index (groups, index);
18152       if (strcmp (group_id, "0") == 0)
18153         {
18154           index++;
18155           continue;
18156         }
18157 
18158       if (find_group_with_permission (group_id, &group, "modify_group"))
18159         {
18160           sql_rollback ();
18161           return -1;
18162         }
18163 
18164       if (group == 0)
18165         {
18166           sql_rollback ();
18167           if (group_id_return) *group_id_return = group_id;
18168           return 1;
18169         }
18170 
18171       sql ("INSERT INTO permissions"
18172            " (uuid, owner, name, comment, resource_type, resource,"
18173            "  resource_uuid, resource_location, subject_type, subject,"
18174            "  subject_location, creation_time, modification_time)"
18175            " VALUES"
18176            " (make_uuid (),"
18177            "  (SELECT id FROM users WHERE users.uuid = '%s'),"
18178            "  'get_tasks', '', 'task', %llu,"
18179            "  (SELECT uuid FROM tasks WHERE tasks.id = %llu),"
18180            "  " G_STRINGIFY (LOCATION_TABLE) ", 'group', %llu,"
18181            "  " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
18182            current_credentials.uuid, task, task, group);
18183 
18184       index++;
18185     }
18186 
18187   sql_commit ();
18188   return 0;
18189 }
18190 
18191 /**
18192  * @brief Set the schedule of a task.
18193  *
18194  * @param[in]  task      Task.
18195  * @param[in]  schedule  Schedule.
18196  * @param[in]  periods   Number of schedule periods.
18197  *
18198  * @return 0 success, -1 error.
18199  */
18200 int
set_task_schedule(task_t task,schedule_t schedule,int periods)18201 set_task_schedule (task_t task, schedule_t schedule, int periods)
18202 {
18203   sql ("UPDATE tasks"
18204        " SET schedule = %llu,"
18205        " schedule_periods = %i,"
18206        " schedule_next_time = (SELECT next_time_ical (icalendar, timezone)"
18207        "                       FROM schedules"
18208        "                       WHERE id = %llu),"
18209        " modification_time = m_now ()"
18210        " WHERE id = %llu;",
18211        schedule, periods, schedule, task);
18212 
18213   return 0;
18214 }
18215 
18216 /**
18217  * @brief Set the schedule of a task.
18218  *
18219  * @param[in]  task_id   Task UUID.
18220  * @param[in]  schedule  Schedule.
18221  * @param[in]  periods   Number of schedule periods.  -1 to use existing value.
18222  *
18223  * @return 0 success, -1 error.
18224  */
18225 int
set_task_schedule_uuid(const gchar * task_id,schedule_t schedule,int periods)18226 set_task_schedule_uuid (const gchar *task_id, schedule_t schedule, int periods)
18227 {
18228   gchar *quoted_task_id, *schedule_periods;
18229 
18230   if (periods == -1)
18231     schedule_periods = g_strdup ("");
18232   else
18233     schedule_periods = g_strdup_printf ("schedule_periods = %i,",
18234                                         periods);
18235 
18236   quoted_task_id = sql_quote (task_id);
18237   sql ("UPDATE tasks"
18238        " SET schedule = %llu,"
18239        "%s"
18240        " schedule_next_time = (SELECT next_time_ical (icalendar, timezone)"
18241        "                       FROM schedules"
18242        "                       WHERE id = %llu),"
18243        " modification_time = m_now ()"
18244        " WHERE uuid = '%s';",
18245        schedule, schedule_periods, schedule, quoted_task_id);
18246   g_free (quoted_task_id);
18247 
18248   g_free (schedule_periods);
18249 
18250   return 0;
18251 }
18252 
18253 /**
18254  * @brief Set the schedule periods of a task, given a UUID.
18255  *
18256  * The task modification time stays the same.
18257  *
18258  * @param[in]  task_id   Task UUID.
18259  * @param[in]  periods   Schedule periods.
18260  *
18261  * @return 0 success, -1 error.
18262  */
18263 int
set_task_schedule_periods(const gchar * task_id,int periods)18264 set_task_schedule_periods (const gchar *task_id, int periods)
18265 {
18266   gchar *quoted_task_id;
18267 
18268   quoted_task_id = sql_quote (task_id);
18269   sql ("UPDATE tasks"
18270        " SET schedule_periods = %i"
18271        " WHERE uuid = '%s';",
18272        periods, quoted_task_id);
18273   g_free (quoted_task_id);
18274 
18275   return 0;
18276 }
18277 
18278 /**
18279  * @brief Set the schedule periods of a task, given an ID.
18280  *
18281  * The task modification time stays the same.
18282  *
18283  * @param[in]  task      Task UUID.
18284  * @param[in]  periods   Schedule periods.
18285  *
18286  * @return 0 success, -1 error.
18287  */
18288 int
set_task_schedule_periods_id(task_t task,int periods)18289 set_task_schedule_periods_id (task_t task, int periods)
18290 {
18291   sql ("UPDATE tasks"
18292        " SET schedule_periods = %i"
18293        " WHERE id = %llu;",
18294        periods, task);
18295   return 0;
18296 }
18297 
18298 /**
18299  * @brief Return the schedule of a task.
18300  *
18301  * @param[in]  task  Task.
18302  *
18303  * @return Schedule.
18304  */
18305 schedule_t
task_schedule(task_t task)18306 task_schedule (task_t task)
18307 {
18308   schedule_t schedule = 0;
18309   switch (sql_int64 (&schedule,
18310                      "SELECT schedule FROM tasks WHERE id = %llu;",
18311                      task))
18312     {
18313       case 0:
18314         return schedule;
18315         break;
18316       case 1:        /* Too few rows in result of query. */
18317       default:       /* Programming error. */
18318         assert (0);
18319       case -1:
18320         return 0;
18321         break;
18322     }
18323 }
18324 
18325 /**
18326  * @brief Return the schedule of a task.
18327  *
18328  * @param[in]  task_id  ID of task.
18329  *
18330  * @return Schedule.
18331  */
18332 schedule_t
task_schedule_uuid(const gchar * task_id)18333 task_schedule_uuid (const gchar *task_id)
18334 {
18335   schedule_t schedule;
18336   gchar *quoted_task_id;
18337 
18338   quoted_task_id = sql_quote (task_id);
18339   schedule = 0;
18340   switch (sql_int64 (&schedule,
18341                      "SELECT schedule FROM tasks WHERE uuid = '%s';",
18342                      quoted_task_id))
18343     {
18344       case 0:
18345         g_free (quoted_task_id);
18346         return schedule;
18347         break;
18348       case 1:        /* Too few rows in result of query. */
18349       default:       /* Programming error. */
18350         assert (0);
18351       case -1:
18352         g_free (quoted_task_id);
18353         return 0;
18354         break;
18355     }
18356 }
18357 
18358 /**
18359  * @brief Get whether the task schedule is in the trash.
18360  *
18361  * @param[in]  task  Task.
18362  *
18363  * @return 1 if in trash, else 0.
18364  */
18365 int
task_schedule_in_trash(task_t task)18366 task_schedule_in_trash (task_t task)
18367 {
18368   return sql_int ("SELECT schedule_location = " G_STRINGIFY (LOCATION_TRASH)
18369                   " FROM tasks"
18370                   " WHERE id = %llu;",
18371                   task);
18372 }
18373 
18374 /**
18375  * @brief Get the number of times the period schedule should run on the task.
18376  *
18377  * @param[in]  task  Task.
18378  *
18379  * @return Number of times.
18380  */
18381 int
task_schedule_periods(task_t task)18382 task_schedule_periods (task_t task)
18383 {
18384   return sql_int ("SELECT schedule_periods FROM tasks WHERE id = %llu;", task);
18385 }
18386 
18387 /**
18388  * @brief Set the next time a scheduled task will be due.
18389  *
18390  * @param[in]  task_id  Task UUID.
18391  *
18392  * @return Task schedule periods.
18393  */
18394 int
task_schedule_periods_uuid(const gchar * task_id)18395 task_schedule_periods_uuid (const gchar *task_id)
18396 {
18397   gchar *quoted_task_id;
18398   int ret;
18399 
18400   quoted_task_id = sql_quote (task_id);
18401   ret = sql_int ("SELECT schedule_periods FROM tasks WHERE uuid = '%s';",
18402                  quoted_task_id);
18403   g_free (quoted_task_id);
18404   return ret;
18405 }
18406 
18407 /**
18408  * @brief Get next time a scheduled task will run, following schedule timezone.
18409  *
18410  * @param[in]  task  Task.
18411  *
18412  * @return If the task has a schedule, the next time the task will run (0 if it
18413  *         has already run), otherwise 0.
18414  */
18415 int
task_schedule_next_time(task_t task)18416 task_schedule_next_time (task_t task)
18417 {
18418   int next_time;
18419 
18420   next_time = sql_int ("SELECT schedule_next_time FROM tasks"
18421                        " WHERE id = %llu;",
18422                        task);
18423 
18424   return next_time;
18425 }
18426 
18427 /**
18428  * @brief Get the next time a scheduled task will be due.
18429  *
18430  * @param[in]  task_id  Task UUID.
18431  *
18432  * @return Next scheduled time.
18433  */
18434 time_t
task_schedule_next_time_uuid(const gchar * task_id)18435 task_schedule_next_time_uuid (const gchar *task_id)
18436 {
18437   gchar *quoted_task_id;
18438   time_t ret;
18439 
18440   quoted_task_id = sql_quote (task_id);
18441   ret = (time_t) sql_int ("SELECT schedule_next_time FROM tasks"
18442                           " WHERE uuid = '%s';",
18443                           quoted_task_id);
18444   g_free (quoted_task_id);
18445   return ret;
18446 }
18447 
18448 /**
18449  * @brief Set the next time a scheduled task will be due.
18450  *
18451  * @param[in]  task  Task.
18452  * @param[in]  time  New next time.
18453  */
18454 void
set_task_schedule_next_time(task_t task,time_t time)18455 set_task_schedule_next_time (task_t task, time_t time)
18456 {
18457   sql ("UPDATE tasks SET schedule_next_time = %i WHERE id = %llu;",
18458        time, task);
18459 }
18460 
18461 /**
18462  * @brief Set the next time a scheduled task will be due.
18463  *
18464  * @param[in]  task_id  Task UUID.
18465  * @param[in]  time     New next time.
18466  */
18467 void
set_task_schedule_next_time_uuid(const gchar * task_id,time_t time)18468 set_task_schedule_next_time_uuid (const gchar *task_id, time_t time)
18469 {
18470   gchar *quoted_task_id;
18471 
18472   quoted_task_id = sql_quote (task_id);
18473   sql ("UPDATE tasks SET schedule_next_time = %i WHERE uuid = '%s';",
18474        time, quoted_task_id);
18475   g_free (quoted_task_id);
18476 }
18477 
18478 /**
18479  * @brief Return the severity score of a task, taking overrides into account.
18480  *
18481  * @param[in]  task       Task.
18482  * @param[in]  overrides  Whether to apply overrides.
18483  * @param[in]  min_qod    Minimum QoD of results to count.
18484  * @param[in]  offset     Offset of report to get severity from:
18485  *                        0 = use last report, 1 = use next to last report
18486  *
18487  * @return Severity score of last report on task as a double if there is one,
18488  *         else SEVERITY_MISSING.
18489  */
18490 static double
task_severity_double(task_t task,int overrides,int min_qod,int offset)18491 task_severity_double (task_t task, int overrides, int min_qod, int offset)
18492 {
18493   report_t report;
18494 
18495   if (current_credentials.uuid == NULL
18496       || task_target (task) == 0 /* Container task. */)
18497     return SEVERITY_MISSING;
18498 
18499   sql_int64 (&report,
18500              "SELECT id FROM reports"
18501              "           WHERE reports.task = %llu"
18502              "           AND reports.scan_run_status = %u"
18503              "           ORDER BY reports.date DESC"
18504              "           LIMIT 1 OFFSET %d",
18505              task, TASK_STATUS_DONE, offset);
18506 
18507   return report_severity (report, overrides, min_qod);
18508 }
18509 
18510 /**
18511  * @brief Set the observers of a task.
18512  *
18513  * @param[in]  task       Task.
18514  * @param[in]  observers  Observers.
18515  *
18516  * @return 0 success, -1 error, 1 user name validation failed, 2 failed to find
18517  *         user.
18518  */
18519 int
set_task_observers(task_t task,const gchar * observers)18520 set_task_observers (task_t task, const gchar *observers)
18521 {
18522   gchar **split, **point;
18523   GList *added;
18524 
18525   // TODO the tricky bit here is if you have to own the task to set observers.
18526 
18527   assert (current_credentials.username);
18528 
18529   added = NULL;
18530   split = g_strsplit_set (observers, " ,", 0);
18531 
18532   sql_begin_immediate ();
18533 
18534   sql ("DELETE FROM permissions"
18535        " WHERE resource_type = 'task' AND resource = %llu"
18536        " AND subject_type = 'user';",
18537        task);
18538 
18539   point = split;
18540   while (*point)
18541     {
18542       user_t user;
18543       gchar *name;
18544 
18545       name = *point;
18546 
18547       g_strstrip (name);
18548 
18549       if (strcmp (name, "") == 0)
18550         {
18551           point++;
18552           continue;
18553         }
18554 
18555       if ((strcmp (name, current_credentials.username) == 0)
18556           || g_list_find_custom (added, name, (GCompareFunc) strcmp))
18557         {
18558           point++;
18559           continue;
18560         }
18561 
18562       added = g_list_prepend (added, name);
18563 
18564       if (user_exists (name) == 0)
18565         {
18566           g_list_free (added);
18567           g_strfreev (split);
18568           sql_rollback ();
18569           return 2;
18570         }
18571 
18572       if (find_user_by_name (name, &user))
18573         {
18574           g_list_free (added);
18575           g_strfreev (split);
18576           sql_rollback ();
18577           return -1;
18578         }
18579 
18580       if (user == 0)
18581         {
18582           gchar *uuid;
18583 
18584           if (validate_username (name))
18585             {
18586               g_list_free (added);
18587               g_strfreev (split);
18588               sql_rollback ();
18589               return 1;
18590             }
18591 
18592           uuid = user_uuid_any_method (name);
18593 
18594           if (uuid == NULL)
18595             {
18596               g_list_free (added);
18597               g_strfreev (split);
18598               sql_rollback ();
18599               return -1;
18600             }
18601 
18602           if (sql_int ("SELECT count(*) FROM users WHERE uuid = '%s';",
18603                        uuid)
18604               == 0)
18605             {
18606               gchar *quoted_name;
18607               quoted_name = sql_quote (name);
18608               sql ("INSERT INTO users"
18609                    " (uuid, name, creation_time, modification_time)"
18610                    " VALUES"
18611                    " ('%s', '%s', m_now (), m_now ());",
18612                    uuid,
18613                    quoted_name);
18614               g_free (quoted_name);
18615 
18616               user = sql_last_insert_id ();
18617             }
18618           else
18619             {
18620               /* user_find should have found it. */
18621               assert (0);
18622               g_free (uuid);
18623               g_list_free (added);
18624               g_strfreev (split);
18625               sql_rollback ();
18626               return -1;
18627             }
18628 
18629           g_free (uuid);
18630         }
18631 
18632       sql ("INSERT INTO permissions"
18633            " (uuid, owner, name, comment, resource_type, resource,"
18634            "  resource_uuid, resource_location, subject_type, subject,"
18635            "  subject_location, creation_time, modification_time)"
18636            " VALUES"
18637            " (make_uuid (),"
18638            "  (SELECT id FROM users WHERE users.uuid = '%s'),"
18639            "  'get_tasks', '', 'task', %llu,"
18640            "  (SELECT uuid FROM tasks WHERE tasks.id = %llu),"
18641            "  " G_STRINGIFY (LOCATION_TABLE) ", 'user', %llu,"
18642            "  " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
18643            current_credentials.uuid, task, task, user);
18644 
18645       point++;
18646     }
18647 
18648   g_list_free (added);
18649   g_strfreev (split);
18650   sql_commit ();
18651   return 0;
18652 }
18653 
18654 /**
18655  * @brief Clear once-off schedules from tasks where the duration has passed.
18656  *
18657  * @param[in]  task  Task.  0 for all.
18658  */
18659 void
clear_duration_schedules(task_t task)18660 clear_duration_schedules (task_t task)
18661 {
18662   gchar *task_element;
18663   const gchar *duration_expired_element;
18664 
18665   if (task)
18666     task_element = g_strdup_printf (" AND id = %llu", task);
18667   else
18668     task_element = g_strdup ("");
18669 
18670   duration_expired_element
18671    = " AND (SELECT first_time + duration FROM schedules"
18672      "      WHERE schedules.id = schedule)"
18673      "     < m_now ()";
18674 
18675   sql ("UPDATE tasks"
18676        " SET schedule = 0,"
18677        " schedule_next_time = 0,"
18678        " modification_time = m_now ()"
18679        " WHERE schedule > 0"
18680        "%s"
18681        " AND NOT (SELECT icalendar LIKE '%%\nBEGIN:VEVENT%%\nRRULE%%'"
18682        "              OR icalendar LIKE '%%\nBEGIN:VEVENT%%\nRDATE%%'"
18683        "            FROM schedules WHERE schedules.id = schedule)"
18684        " AND (SELECT duration FROM schedules WHERE schedules.id = schedule) > 0"
18685        "%s"
18686        " AND run_status != %i"
18687        " AND run_status != %i;",
18688        task_element,
18689        task ? "" : duration_expired_element,
18690        TASK_STATUS_RUNNING,
18691        TASK_STATUS_REQUESTED);
18692 
18693   g_free (task_element);
18694 }
18695 
18696 /**
18697  * @brief Update tasks with limited run schedules which have durations.
18698  *
18699  * If a task is given, assume that the task has finished.  Otherwise only
18700  * update the task if more time than the duration has passed the start time.
18701  *
18702  * @param[in]  task  Task.  0 for all.
18703  */
18704 void
update_duration_schedule_periods(task_t task)18705 update_duration_schedule_periods (task_t task)
18706 {
18707   gchar *task_element;
18708   const gchar *duration_expired_element;
18709 
18710   if (task)
18711     task_element = g_strdup_printf (" AND id = %llu", task);
18712   else
18713     task_element = g_strdup ("");
18714 
18715   duration_expired_element
18716    = /* The task has started, so assume that the start time was the last
18717       * most recent start of the period. */
18718      " AND (SELECT next_time_ical (icalendar, timezone, -1)"
18719      "             + duration"
18720      "      FROM schedules"
18721      "      WHERE schedules.id = schedule)"
18722      "     < m_now ()";
18723 
18724   sql ("UPDATE tasks"
18725        " SET schedule = 0,"
18726        " schedule_next_time = 0,"
18727        " modification_time = m_now ()"
18728        " WHERE schedule > 0"
18729        "%s"
18730        " AND schedule_periods = 1"
18731        " AND (SELECT icalendar LIKE '%%\nBEGIN:VEVENT%%\nRRULE%%'"
18732        "          OR icalendar LIKE '%%\nBEGIN:VEVENT%%\nRDATE%%'"
18733        "       FROM schedules WHERE schedules.id = schedule)"
18734        " AND (SELECT duration FROM schedules WHERE schedules.id = schedule) > 0"
18735        " AND schedule_next_time = 0"  /* Set as flag when starting task. */
18736        "%s"
18737        " AND run_status != %i"
18738        " AND run_status != %i;",
18739        task_element,
18740        task ? "" : duration_expired_element,
18741        TASK_STATUS_RUNNING,
18742        TASK_STATUS_REQUESTED);
18743   g_free (task_element);
18744 }
18745 
18746 /**
18747  * @brief Auto delete reports.
18748  */
18749 void
auto_delete_reports()18750 auto_delete_reports ()
18751 {
18752   iterator_t tasks;
18753 
18754   g_debug ("%s", __func__);
18755 
18756   sql_begin_immediate ();
18757 
18758   /* As in delete_report, this prevents other processes from getting the
18759    * report ID. */
18760   if (sql_int ("SELECT try_exclusive_lock('reports');") == 0)
18761     {
18762       sql_rollback ();
18763       return;
18764     }
18765 
18766   init_iterator (&tasks,
18767                  "SELECT id, name,"
18768                  "       (SELECT value FROM task_preferences"
18769                  "        WHERE name = 'auto_delete_data'"
18770                  "        AND task = tasks.id)"
18771                  " FROM tasks"
18772                  " WHERE owner is NOT NULL"
18773                  " AND hidden = 0"
18774                  " AND EXISTS (SELECT * FROM task_preferences"
18775                  "             WHERE task = tasks.id"
18776                  "             AND name = 'auto_delete'"
18777                  "             AND value = 'keep');");
18778   while (next (&tasks))
18779     {
18780       task_t task;
18781       iterator_t reports;
18782       const char *keep_string;
18783       int keep;
18784 
18785       task = iterator_int64 (&tasks, 0);
18786 
18787       keep_string = iterator_string (&tasks, 2);
18788       if (keep_string == NULL)
18789         continue;
18790       keep = atoi (keep_string);
18791       if (keep < AUTO_DELETE_KEEP_MIN || keep > AUTO_DELETE_KEEP_MAX)
18792         continue;
18793 
18794       g_debug ("%s: %s (%i)", __func__,
18795               iterator_string (&tasks, 1),
18796               keep);
18797 
18798       init_iterator (&reports,
18799                      "SELECT id FROM reports"
18800                      " WHERE task = %llu"
18801                      " AND start_time IS NOT NULL"
18802                      " AND start_time > 0"
18803                      " ORDER BY start_time DESC LIMIT %s OFFSET %i;",
18804                      task,
18805                      sql_select_limit (-1),
18806                      keep);
18807       while (next (&reports))
18808         {
18809           int ret;
18810           report_t report;
18811 
18812           report = iterator_int64 (&reports, 0);
18813           assert (report);
18814 
18815           g_debug ("%s: delete %llu", __func__, report);
18816           ret = delete_report_internal (report);
18817           if (ret == 2)
18818             {
18819               /* Report is in use. */
18820               g_debug ("%s: %llu is in use", __func__, report);
18821               continue;
18822             }
18823           if (ret)
18824             {
18825               g_warning ("%s: failed to delete %llu (%i)",
18826                          __func__, report, ret);
18827               sql_rollback ();
18828             }
18829         }
18830       cleanup_iterator (&reports);
18831     }
18832   cleanup_iterator (&tasks);
18833   sql_commit ();
18834 }
18835 
18836 
18837 /**
18838  * @brief Get definitions file from a task's config.
18839  *
18840  * @param[in]  task  Task.
18841  *
18842  * @return Definitions file.
18843  */
18844 static char *
task_definitions_file(task_t task)18845 task_definitions_file (task_t task)
18846 {
18847   assert (task);
18848   return sql_string ("SELECT value FROM config_preferences"
18849                      " WHERE name = 'definitions_file' AND config = %llu;",
18850                      task_config (task));
18851 }
18852 
18853 /**
18854  * @brief Set a task's schedule so that it runs again next scheduling round.
18855  *
18856  * @param  task_id  UUID of task.
18857  */
18858 void
reschedule_task(const gchar * task_id)18859 reschedule_task (const gchar *task_id)
18860 {
18861   task_t task;
18862   task = 0;
18863   switch (sql_int64 (&task,
18864                      "SELECT id FROM tasks WHERE uuid = '%s'"
18865                      " AND hidden != 2;",
18866                      task_id))
18867     {
18868       case 0:
18869         g_warning ("%s: rescheduling task '%s'", __func__, task_id);
18870         set_task_schedule_next_time (task, time (NULL) - 1);
18871         break;
18872       case 1:        /* Too few rows in result of query. */
18873         break;
18874       default:       /* Programming error. */
18875         assert (0);
18876       case -1:
18877         break;
18878     }
18879 }
18880 
18881 
18882 /* Results. */
18883 
18884 /**
18885  * @brief Find a result for a set of permissions, given a UUID.
18886  *
18887  * @param[in]   uuid        UUID of result.
18888  * @param[out]  result      Result return, 0 if successfully failed to find
18889  *                          result.
18890  * @param[in]   permission  Permission.
18891  *
18892  * @return FALSE on success (including if failed to find result), TRUE on error.
18893  */
18894 gboolean
find_result_with_permission(const char * uuid,result_t * result,const char * permission)18895 find_result_with_permission (const char* uuid, result_t* result,
18896                              const char *permission)
18897 {
18898   gchar *quoted_uuid = sql_quote (uuid);
18899   if (acl_user_has_access_uuid ("result", quoted_uuid, permission, 0) == 0)
18900     {
18901       g_free (quoted_uuid);
18902       *result = 0;
18903       return FALSE;
18904     }
18905   switch (sql_int64 (result,
18906                      "SELECT id FROM results WHERE uuid = '%s';",
18907                      quoted_uuid))
18908     {
18909       case 0:
18910         break;
18911       case 1:        /* Too few rows in result of query. */
18912         *result = 0;
18913         break;
18914       default:       /* Programming error. */
18915         assert (0);
18916       case -1:
18917         g_free (quoted_uuid);
18918         return TRUE;
18919         break;
18920     }
18921 
18922   g_free (quoted_uuid);
18923   return FALSE;
18924 }
18925 
18926 /**
18927  * @brief Ensure an NVT occurs in the result_nvts table.
18928  *
18929  * @param[in]  nvt  NVT OID.
18930  */
18931 static void
result_nvt_notice(const gchar * nvt)18932 result_nvt_notice (const gchar *nvt)
18933 {
18934   if (nvt == NULL)
18935     return;
18936   sql ("INSERT INTO result_nvts (nvt) VALUES ('%s') ON CONFLICT DO NOTHING;",
18937        nvt);
18938 }
18939 
18940 /**
18941  * @brief Make an OSP result.
18942  *
18943  * @param[in]  task         The task associated with the result.
18944  * @param[in]  host         Target host of result.
18945  * @param[in]  hostname     Hostname of the result.
18946  * @param[in]  nvt          The uuid of oval definition that produced the
18947  *                          result, a title for the result otherwise.
18948  * @param[in]  type         Type of result.  "Alarm", etc.
18949  * @param[in]  description  Description of the result.
18950  * @param[in]  port         Result port.
18951  * @param[in]  severity     Result severity.
18952  * @param[in]  qod          Quality of detection.
18953  * @param[in]  path         Result path, e.g. file location of a product.
18954  *
18955  * @return A result descriptor for the new result, 0 if error.
18956  */
18957 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)18958 make_osp_result (task_t task, const char *host, const char *hostname,
18959                  const char *nvt, const char *type, const char *description,
18960                  const char *port, const char *severity, int qod,
18961                  const char *path)
18962 {
18963   char *nvt_revision = NULL, *quoted_desc, *quoted_nvt, *result_severity;
18964   char *quoted_port, *quoted_hostname, *quoted_path;
18965 
18966   assert (task);
18967   assert (type);
18968 
18969   quoted_desc = sql_quote (description ?: "");
18970   quoted_nvt = sql_quote (nvt ?: "");
18971   quoted_port = sql_quote (port ?: "");
18972   quoted_hostname = sql_quote (hostname ? hostname : "");
18973   quoted_path = sql_quote (path ? path : "");
18974 
18975   if (nvt)
18976     {
18977       if (g_str_has_prefix (nvt, "1.3.6.1.4.1.25623."))
18978         nvt_revision = sql_string ("SELECT iso_time (modification_time)"
18979                                    " FROM nvts WHERE oid='%s'",
18980                                    quoted_nvt);
18981       else if (g_str_has_prefix (nvt, "oval:"))
18982         nvt_revision = ovaldef_version (nvt);
18983       else if (g_str_has_prefix (nvt, "CVE-"))
18984         nvt_revision = sql_string ("SELECT iso_time (modification_time)"
18985                                    " FROM scap.cves WHERE uuid='%s'",
18986                                    quoted_nvt);
18987     }
18988 
18989   if (!severity || !strcmp (severity, ""))
18990     {
18991       if (!strcmp (type, severity_to_type (SEVERITY_ERROR)))
18992         result_severity = g_strdup (G_STRINGIFY (SEVERITY_ERROR));
18993       else
18994         {
18995           if (nvt && g_str_has_prefix (nvt, "CVE-"))
18996             {
18997               result_severity = cve_cvss_base (nvt);
18998               if (result_severity == NULL || strcmp (result_severity, "") == 0)
18999                 {
19000                   g_free (result_severity);
19001                   result_severity
19002                     = g_strdup_printf ("%0.1f",
19003                                        setting_default_severity_dbl ());
19004                   g_debug ("%s: OSP CVE result without severity for '%s'",
19005                            __func__, nvt);
19006                 }
19007             }
19008           else
19009             {
19010               /*
19011               result_severity
19012                 = g_strdup_printf ("%0.1f",
19013                                    setting_default_severity_dbl ());
19014               */
19015               g_warning ("%s: Non-CVE OSP result without severity for test %s",
19016                          __func__, nvt ? nvt : "(unknown)");
19017               return 0;
19018             }
19019         }
19020     }
19021   else
19022     result_severity = sql_quote (severity);
19023   result_nvt_notice (quoted_nvt);
19024   sql ("INSERT into results"
19025        " (owner, date, task, host, hostname, port, nvt,"
19026        "  nvt_version, severity, type, qod, qod_type, description,"
19027        "  path, uuid, result_nvt)"
19028        " VALUES (NULL, m_now(), %llu, '%s', '%s', '%s', '%s',"
19029        "         '%s', '%s', '%s', %d, '', '%s',"
19030        "         '%s', make_uuid (),"
19031        "         (SELECT id FROM result_nvts WHERE nvt = '%s'));",
19032        task, host ?: "", quoted_hostname, quoted_port, quoted_nvt,
19033        nvt_revision ?: "", result_severity ?: "0",
19034        type, qod, quoted_desc, quoted_path, quoted_nvt);
19035   g_free (result_severity);
19036   g_free (nvt_revision);
19037   g_free (quoted_desc);
19038   g_free (quoted_nvt);
19039   g_free (quoted_port);
19040   g_free (quoted_hostname);
19041   g_free (quoted_path);
19042 
19043   return sql_last_insert_id ();
19044 }
19045 
19046 /**
19047  * @brief Get QoD percentage for a qod_type string.
19048  *
19049  * @param[in]  qod_type   The QoD type string.
19050  *
19051  * @return A QoD percentage value, QOD_DEFAULT if string is NULL or unknown.
19052  */
19053 int
qod_from_type(const char * qod_type)19054 qod_from_type (const char *qod_type)
19055 {
19056   if (qod_type == NULL)
19057     return QOD_DEFAULT;
19058   else if (strcmp (qod_type, "exploit") == 0)
19059     return 100;
19060   else if  (strcmp (qod_type, "remote_vul") == 0)
19061     return 99;
19062   else if (strcmp (qod_type, "remote_app") == 0)
19063     return 98;
19064   else if (strcmp (qod_type, "package") == 0)
19065     return 97;
19066   else if (strcmp (qod_type, "registry") == 0)
19067     return 97;
19068   else if (strcmp (qod_type, "remote_active") == 0)
19069     return 95;
19070   else if (strcmp (qod_type, "remote_banner") == 0)
19071     return 80;
19072   else if (strcmp (qod_type, "executable_version") == 0)
19073     return 80;
19074   else if (strcmp (qod_type, "remote_analysis") == 0)
19075     return 70;
19076   else if (strcmp (qod_type, "remote_probe") == 0)
19077     return 50;
19078   else if (strcmp (qod_type, "remote_banner_unreliable") == 0)
19079     return 30;
19080   else if (strcmp (qod_type, "executable_version_unreliable") == 0)
19081     return 30;
19082   else if (strcmp (qod_type, "general_note") == 0)
19083     return 1;
19084   else
19085     return QOD_DEFAULT;
19086 }
19087 
19088 /**
19089  * @brief Identify a host, given an identifier.
19090  *
19091  * Find a host which has an identifier of the same name and value, and
19092  * which has no identifiers of the same name and a different value.
19093  *
19094  * @param[in]  host_name         Host name.
19095  * @param[in]  identifier_name   Host identifier name.
19096  * @param[in]  identifier_value  Value of host identifier.
19097  * @param[in]  source_type       Source of identification: result.
19098  * @param[in]  source            Source identifier.
19099  *
19100  * @return Host if exists, else 0.
19101  */
19102 static host_t
host_identify(const char * host_name,const char * identifier_name,const char * identifier_value,const char * source_type,const char * source)19103 host_identify (const char *host_name, const char *identifier_name,
19104                const char *identifier_value, const char *source_type,
19105                const char *source)
19106 {
19107   host_t host = 0;
19108   gchar *quoted_host_name, *quoted_identifier_name, *quoted_identifier_value;
19109 
19110   quoted_host_name = sql_quote (host_name);
19111   quoted_identifier_name = sql_quote (identifier_name);
19112   quoted_identifier_value = sql_quote (identifier_value);
19113 
19114   switch (sql_int64 (&host,
19115                      "SELECT hosts.id FROM hosts, host_identifiers"
19116                      " WHERE hosts.name = '%s'"
19117                      " AND hosts.owner = (SELECT id FROM users"
19118                      "                    WHERE uuid = '%s')"
19119                      " AND host = hosts.id"
19120                      " AND host_identifiers.owner = (SELECT id FROM users"
19121                      "                               WHERE uuid = '%s')"
19122                      " AND host_identifiers.name = '%s'"
19123                      " AND value = '%s';",
19124                      quoted_host_name,
19125                      current_credentials.uuid,
19126                      current_credentials.uuid,
19127                      quoted_identifier_name,
19128                      quoted_identifier_value))
19129     {
19130       case 0:
19131         break;
19132       case 1:        /* Too few rows in result of query. */
19133         host = 0;
19134         break;
19135       default:       /* Programming error. */
19136         assert (0);
19137       case -1:
19138         host = 0;
19139         break;
19140     }
19141 
19142   if (host == 0)
19143     switch (sql_int64 (&host,
19144                        "SELECT id FROM hosts"
19145                        " WHERE name = '%s'"
19146                        " AND owner = (SELECT id FROM users"
19147                        "              WHERE uuid = '%s')"
19148                        " AND NOT EXISTS (SELECT * FROM host_identifiers"
19149                        "                 WHERE host = hosts.id"
19150                        "                 AND owner = (SELECT id FROM users"
19151                        "                              WHERE uuid = '%s')"
19152                        "                 AND name = '%s');",
19153                        quoted_host_name,
19154                        current_credentials.uuid,
19155                        current_credentials.uuid,
19156                        quoted_identifier_name))
19157       {
19158         case 0:
19159           break;
19160         case 1:        /* Too few rows in result of query. */
19161           host = 0;
19162           break;
19163         default:       /* Programming error. */
19164           assert (0);
19165         case -1:
19166           host = 0;
19167           break;
19168       }
19169 
19170   g_free (quoted_host_name);
19171   g_free (quoted_identifier_name);
19172   g_free (quoted_identifier_value);
19173 
19174   return host;
19175 }
19176 
19177 /**
19178  * @brief Notice a host.
19179  *
19180  * When a host is detected during a scan, this makes the decision about which
19181  * asset host is used for the host, as described in \ref asset_rules.  This
19182  * decision is revised at the end of the scan by \ref hosts_set_identifiers if
19183  * there are any identifiers for the host.
19184  *
19185  * @param[in]  host_name         Name of host.
19186  * @param[in]  identifier_type   Type of host identifier.
19187  * @param[in]  identifier_value  Value of host identifier.
19188  * @param[in]  source_type       Type of source identifier
19189  * @param[in]  source_id         Source identifier.
19190  * @param[in]  check_add_to_assets  Whether to check the 'Add to Assets'
19191  *                                  task preference.
19192  * @param[in]  check_for_existing_identifier  Whether to check for an existing
19193  *                                            identifier like this one.  Used
19194  *                                            for slaves, which call this
19195  *                                            repeatedly.
19196  *
19197  * @return Host if existed, else 0.
19198  */
19199 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)19200 host_notice (const char *host_name, const char *identifier_type,
19201              const char *identifier_value, const char *source_type,
19202              const char *source_id, int check_add_to_assets,
19203              int check_for_existing_identifier)
19204 {
19205   host_t host;
19206   gchar *quoted_identifier_value, *quoted_identifier_type, *quoted_source_type;
19207   gchar *quoted_source_id;
19208 
19209   /* Only add to assets if "Add to Assets" is set on the task. */
19210   if (check_add_to_assets
19211       && g_str_has_prefix (source_type, "Report")
19212       && sql_int ("SELECT value = 'no' FROM task_preferences"
19213                   " WHERE task = (SELECT task FROM reports WHERE uuid = '%s')"
19214                   " AND name = 'in_assets';",
19215                   source_id))
19216     return 0;
19217 
19218   host = host_identify (host_name, identifier_type, identifier_value,
19219                         source_type, source_id);
19220   if (host == 0)
19221     {
19222       gchar *quoted_host_name;
19223       quoted_host_name = sql_quote (host_name);
19224       sql ("INSERT into hosts"
19225            " (uuid, owner, name, comment, creation_time, modification_time)"
19226            " VALUES"
19227            " (make_uuid (), (SELECT id FROM users WHERE uuid = '%s'), '%s', '',"
19228            "  m_now (), m_now ());",
19229            current_credentials.uuid,
19230            quoted_host_name);
19231       g_free (quoted_host_name);
19232 
19233       host = sql_last_insert_id ();
19234     }
19235 
19236   quoted_identifier_value = sql_quote (identifier_value);
19237   quoted_source_id = sql_quote (source_id);
19238   quoted_source_type = sql_quote (source_type);
19239   quoted_identifier_type = sql_quote (identifier_type);
19240 
19241   if (check_for_existing_identifier
19242       && sql_int ("SELECT EXISTS (SELECT * FROM host_identifiers"
19243                   "               WHERE host = %llu"
19244                   "               AND owner = (SELECT id FROM users WHERE uuid = '%s')"
19245                   "               AND name = '%s'"
19246                   "               AND value = '%s'"
19247                   "               AND source_type = '%s'"
19248                   "               AND source_id = '%s');",
19249                   host,
19250                   current_credentials.uuid,
19251                   quoted_identifier_type,
19252                   quoted_identifier_value,
19253                   quoted_source_type,
19254                   quoted_source_id))
19255     return 0;
19256 
19257   sql ("INSERT into host_identifiers"
19258        " (uuid, host, owner, name, comment, value, source_type, source_id,"
19259        "  source_data, creation_time, modification_time)"
19260        " VALUES"
19261        " (make_uuid (), %llu, (SELECT id FROM users WHERE uuid = '%s'), '%s',"
19262        "  '', '%s', '%s', '%s', '', m_now (), m_now ());",
19263        host,
19264        current_credentials.uuid,
19265        quoted_identifier_type,
19266        quoted_identifier_value,
19267        quoted_source_type,
19268        quoted_source_id);
19269 
19270   sql ("UPDATE hosts SET modification_time = (SELECT modification_time"
19271        "                                      FROM host_identifiers"
19272        "                                      WHERE id = %llu)"
19273        " WHERE id = %llu;",
19274        sql_last_insert_id (),
19275        host);
19276 
19277   g_free (quoted_identifier_type);
19278   g_free (quoted_identifier_value);
19279   g_free (quoted_source_id);
19280   g_free (quoted_source_type);
19281 
19282   return host;
19283 }
19284 
19285 /**
19286  * @brief Get a severity string from an nvt and result type.
19287  *
19288  * @param[in]  nvt_id   NVT oid.
19289  * @param[in]  type     Result type.
19290  *
19291  * @return A severity string, NULL if unknown type or no nvt id for Alarm type.
19292  */
19293 static char *
nvt_severity(const char * nvt_id,const char * type)19294 nvt_severity (const char *nvt_id, const char *type)
19295 {
19296   char *severity = NULL;
19297 
19298   if (strcasecmp (type, "Alarm") == 0 && nvt_id)
19299     severity = sql_string ("SELECT coalesce(cvss_base, '0.0')"
19300                            " FROM nvts WHERE uuid = '%s';", nvt_id);
19301   else if (strcasecmp (type, "Alarm") == 0)
19302     g_warning ("%s result type requires an NVT", type);
19303   else if (strcasecmp (type, "Log Message") == 0)
19304     severity = g_strdup (G_STRINGIFY (SEVERITY_LOG));
19305   else if (strcasecmp (type, "Error Message") == 0)
19306     severity = g_strdup (G_STRINGIFY (SEVERITY_ERROR));
19307   else
19308     g_warning ("Invalid result nvt type %s", type);
19309   return severity;
19310 }
19311 
19312 /**
19313  * @brief Make a result.
19314  *
19315  * @param[in]  task         The task associated with the result.
19316  * @param[in]  host         Host IP address.
19317  * @param[in]  hostname     Hostname.
19318  * @param[in]  port         The port the result refers to.
19319  * @param[in]  nvt          The OID of the NVT that produced the result.
19320  * @param[in]  type         Type of result: "Alarm", "Error Message" or
19321  *                          "Log Message".
19322  * @param[in]  description  Description of the result.
19323  * @param[in]  path         Result path, e.g. file location of a product.
19324  *
19325  * @return A result descriptor for the new result, 0 if error.
19326  */
19327 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)19328 make_result (task_t task, const char* host, const char *hostname,
19329              const char* port, const char* nvt,
19330              const char* type, const char* description,
19331              const char* path)
19332 {
19333   result_t result;
19334   gchar *nvt_revision, *severity, *qod, *qod_type;
19335   gchar *quoted_nvt, *quoted_hostname, *quoted_descr, *quoted_path;
19336   nvt_t nvt_id = 0;
19337 
19338   if (nvt && strcmp (nvt, "") && (find_nvt (nvt, &nvt_id) || nvt_id <= 0))
19339     {
19340       g_warning ("NVT '%s' not found. Result not created", nvt);
19341       return 0;
19342     }
19343 
19344   severity = nvt_severity (nvt, type);
19345   if (!severity)
19346     {
19347       g_warning ("NVT '%s' has no severity.  Result not created.", nvt);
19348       return 0;
19349     }
19350 
19351   quoted_nvt = NULL;
19352   if (nvt && strcmp (nvt, ""))
19353     {
19354       quoted_nvt = sql_quote (nvt);
19355 
19356       qod = g_strdup_printf ("(SELECT qod FROM nvts WHERE id = %llu)",
19357                              nvt_id);
19358       qod_type = g_strdup_printf ("(SELECT qod_type FROM nvts WHERE id = %llu)",
19359                                   nvt_id);
19360 
19361       if (g_str_has_prefix (nvt, "1.3.6.1.4.1.25623."))
19362         nvt_revision = sql_string ("SELECT iso_time (modification_time)"
19363                                    " FROM nvts WHERE oid='%s'",
19364                                    quoted_nvt);
19365       else if (g_str_has_prefix (nvt, "oval:"))
19366         nvt_revision = ovaldef_version (nvt);
19367       else if (g_str_has_prefix (nvt, "CVE-"))
19368         nvt_revision = sql_string ("SELECT iso_time (modification_time)"
19369                                    " FROM scap.cves WHERE uuid='%s'",
19370                                    quoted_nvt);
19371       else
19372         nvt_revision = strdup ("");
19373     }
19374   else
19375     {
19376       qod = g_strdup (G_STRINGIFY (QOD_DEFAULT));
19377       qod_type = g_strdup ("''");
19378       nvt_revision = g_strdup ("");
19379     }
19380 
19381   if (!strcmp (severity, ""))
19382     {
19383       g_free (severity);
19384       severity = g_strdup ("0.0");
19385     }
19386   quoted_hostname = sql_quote (hostname ? hostname : "");
19387   quoted_descr = sql_quote (description ?: "");
19388   quoted_path = sql_quote (path ? path : "");
19389   result_nvt_notice (nvt);
19390   sql ("INSERT into results"
19391        " (owner, date, task, host, hostname, port,"
19392        "  nvt, nvt_version, severity, type,"
19393        "  description, uuid, qod, qod_type, path, result_nvt)"
19394        " VALUES"
19395        " (NULL, m_now (), %llu, '%s', '%s', '%s',"
19396        "  '%s', '%s', '%s', '%s',"
19397        "  '%s', make_uuid (), %s, %s, '%s',"
19398        "  (SELECT id FROM result_nvts WHERE nvt = '%s'));",
19399        task, host ?: "", quoted_hostname, port ?: "",
19400        quoted_nvt ?: "", nvt_revision, severity, type,
19401        quoted_descr, qod, qod_type, quoted_path, quoted_nvt ? quoted_nvt : "");
19402 
19403   g_free (quoted_nvt);
19404   g_free (quoted_hostname);
19405   g_free (quoted_descr);
19406   g_free (qod);
19407   g_free (qod_type);
19408   g_free (nvt_revision);
19409   g_free (severity);
19410   g_free (quoted_path);
19411   result = sql_last_insert_id ();
19412   return result;
19413 }
19414 
19415 /**
19416  * @brief Make a CVE result.
19417  *
19418  * @param[in]  task         The task associated with the result.
19419  * @param[in]  host         Host.
19420  * @param[in]  nvt          The OID of the NVT that produced the result.
19421  * @param[in]  cvss         CVSS base.
19422  * @param[in]  description  Description of the result.
19423  *
19424  * @return A result descriptor for the new result, 0 if error.
19425  */
19426 result_t
make_cve_result(task_t task,const char * host,const char * nvt,double cvss,const char * description)19427 make_cve_result (task_t task, const char* host, const char *nvt, double cvss,
19428                  const char* description)
19429 {
19430   gchar *quoted_descr;
19431   quoted_descr = sql_quote (description ?: "");
19432   result_nvt_notice (nvt);
19433   sql ("INSERT into results"
19434        " (owner, date, task, host, port, nvt, nvt_version, severity, type,"
19435        "  description, uuid, qod, qod_type, path, result_nvt)"
19436        " VALUES"
19437        " (NULL, m_now (), %llu, '%s', '', '%s',"
19438        "  (SELECT iso_time (modification_time)"
19439        "     FROM scap.cves WHERE uuid='%s'),"
19440        "  '%1.1f', '%s', '%s', make_uuid (), %i, '', '',"
19441        "  (SELECT id FROM result_nvts WHERE nvt = '%s'));",
19442        task, host ?: "", nvt, nvt, cvss, severity_to_type (cvss),
19443        quoted_descr, QOD_DEFAULT, nvt);
19444 
19445   g_free (quoted_descr);
19446   return sql_last_insert_id ();
19447 }
19448 
19449 /**
19450  * @brief Return the UUID of a result.
19451  *
19452  * @param[in]   result  Result.
19453  * @param[out]  id      Pointer to a newly allocated string.
19454  *
19455  * @return 0.
19456  */
19457 int
result_uuid(result_t result,char ** id)19458 result_uuid (result_t result, char ** id)
19459 {
19460   *id = sql_string ("SELECT uuid FROM results WHERE id = %llu;",
19461                     result);
19462   return 0;
19463 }
19464 
19465 /**
19466  * @brief Get product detection results corresponding to a given vulnerability
19467  *        detection result.
19468  *
19469  * @param[in]   result      Vulnerability detection result.
19470  * @param[in]   report      Report of result.
19471  * @param[in]   host        Host of result.
19472  * @param[in]   port        Port of result.
19473  * @param[in]   path        Path of result.
19474  * @param[out]  oid         Detection script OID.
19475  * @param[out]  ref         Detection result UUID.
19476  * @param[out]  product     Product name.
19477  * @param[out]  location    Product location.
19478  * @param[out]  name        Detection script name.
19479  *
19480  * @return -1 on error, 0 on success.
19481  */
19482 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)19483 result_detection_reference (result_t result, report_t report,
19484                             const char *host,
19485                             const char *port,
19486                             const char *path,
19487                             char **oid, char **ref, char **product,
19488                             char **location, char **name)
19489 {
19490   gchar *quoted_location, *quoted_host;
19491 
19492   if ((ref == NULL) || (product == NULL) || (location == NULL)
19493       || (name == NULL))
19494     return -1;
19495 
19496   if ((report == 0) || (host == NULL) || (oid == NULL))
19497     return -1;
19498 
19499   quoted_location = NULL;
19500   *oid = *ref = *product = *location = *name = NULL;
19501   quoted_host = sql_quote (host);
19502 
19503 
19504   if (path && strcmp (path, ""))
19505     {
19506       *location = strdup (path);
19507     }
19508   else if (port && strcmp (port, "")
19509            && !(g_str_has_prefix (port, "general/")))
19510     {
19511       *location = strdup (port);
19512     }
19513   else
19514     {
19515       *location = sql_string ("SELECT value"
19516                               " FROM report_host_details"
19517                               " WHERE report_host = (SELECT id"
19518                               "                      FROM report_hosts"
19519                               "                      WHERE report = %llu"
19520                               "                      AND host = '%s')"
19521                               " AND name = 'detected_at'"
19522                               " AND source_name = (SELECT nvt"
19523                               "                    FROM results"
19524                               "                    WHERE id = %llu);",
19525                               report, quoted_host, result);
19526     }
19527   if (*location == NULL)
19528     {
19529         goto detect_cleanup;
19530     }
19531 
19532   quoted_location = sql_quote (*location);
19533 
19534   *oid
19535     = sql_string ("SELECT value"
19536                   " FROM report_host_details"
19537                   " WHERE report_host = (SELECT id"
19538                   "                      FROM report_hosts"
19539                   "                      WHERE report = %llu"
19540                   "                      AND host = '%s')"
19541                   " AND name = 'detected_by@%s'"
19542                   " AND source_name = (SELECT nvt"
19543                   "                    FROM results"
19544                   "                    WHERE id = %llu)"
19545                   " LIMIT 1",
19546                   report, quoted_host, quoted_location, result);
19547   if (*oid == NULL)
19548     {
19549       *oid
19550         = sql_string ("SELECT value"
19551                       " FROM report_host_details"
19552                       " WHERE report_host = (SELECT id"
19553                       "                      FROM report_hosts"
19554                       "                      WHERE report = %llu"
19555                       "                      AND host = '%s')"
19556                       " AND name = 'detected_by'"
19557                       " AND source_name = (SELECT nvt"
19558                       "                    FROM results"
19559                       "                    WHERE id = %llu)"
19560                       " LIMIT 1",
19561                       report, quoted_host, result);
19562     }
19563 
19564   if (*oid == NULL)
19565     {
19566         goto detect_cleanup;
19567     }
19568 
19569   *product = sql_string ("SELECT name"
19570                          " FROM report_host_details"
19571                          " WHERE report_host = (SELECT id"
19572                          "                      FROM report_hosts"
19573                          "                      WHERE report = %llu"
19574                          "                      AND host = '%s')"
19575                          " AND source_name = '%s'"
19576                          " AND name != 'detected_at'"
19577                          " AND value = '%s';",
19578                          report, quoted_host, *oid, quoted_location);
19579   if (*product == NULL)
19580     goto detect_cleanup;
19581 
19582   if (g_str_has_prefix (*oid, "CVE-"))
19583     *name = g_strdup (*oid);
19584   else
19585     *name = sql_string ("SELECT name FROM nvts WHERE oid = '%s';", *oid);
19586   if (*name == NULL)
19587     goto detect_cleanup;
19588 
19589   /* Get the result produced by the detection NVT when it detected the
19590    * product.  The result port or description must include the product
19591    * location in order for this to work. */
19592   *ref = sql_string ("SELECT uuid"
19593                      " FROM results"
19594                      " WHERE report = %llu"
19595                      " AND host = '%s'"
19596                      " AND nvt = '%s'"
19597                      " AND (description LIKE '%%%s%%'"
19598                      "      OR port LIKE '%%%s%%');",
19599                      report, quoted_host, *oid, quoted_location,
19600                      quoted_location);
19601 
19602   if (*ref == NULL)
19603     goto detect_cleanup;
19604 
19605   g_free (quoted_host);
19606   g_free (quoted_location);
19607 
19608   return 0;
19609 
19610 detect_cleanup:
19611   g_free (quoted_host);
19612   g_free (quoted_location);
19613 
19614   return -1;
19615 }
19616 
19617 
19618 
19619 /* Prognostics. */
19620 
19621 /**
19622  * @brief Initialize an iterator of locations of an App for a report's host.
19623  *
19624  * @param[in]  iterator     Iterator.
19625  * @param[in]  report_host  Report host.
19626  * @param[in]  app          CPE.
19627  */
19628 void
init_app_locations_iterator(iterator_t * iterator,report_host_t report_host,const gchar * app)19629 init_app_locations_iterator (iterator_t *iterator,
19630                              report_host_t report_host,
19631                              const gchar *app)
19632 {
19633   gchar *quoted_app;
19634 
19635   assert (app);
19636 
19637   quoted_app = sql_quote (app);
19638 
19639   init_iterator (iterator,
19640                  "SELECT string_agg(DISTINCT value, ', ')"
19641                  " FROM report_host_details"
19642                  " WHERE report_host = %llu"
19643                  " AND name = '%s'"
19644                  " AND source_type = 'nvt'"
19645                  " AND source_name"
19646                  "     IN (SELECT source_name FROM report_host_details"
19647                  "         WHERE report_host = %llu"
19648                  "         AND source_type = 'nvt'"
19649                  "         AND name = 'App'"
19650                  "         AND value = '%s');",
19651                  report_host,
19652                  quoted_app,
19653                  report_host,
19654                  quoted_app);
19655 
19656   g_free (quoted_app);
19657 }
19658 
19659 /**
19660  * @brief Get a location from an app locations iterator.
19661  *
19662  * @param[in]  iterator   Iterator.
19663  *
19664  * @return  The location.
19665  */
19666 const char *
app_locations_iterator_location(iterator_t * iterator)19667 app_locations_iterator_location (iterator_t *iterator)
19668 {
19669   return iterator_string (iterator, 0);
19670 }
19671 
19672 /**
19673  * @brief Initialise a report host prognosis iterator.
19674  *
19675  * @param[in]  iterator     Iterator.
19676  * @param[in]  report_host  Report host whose prognosis the iterator loops over.
19677  *                          All report_hosts if NULL.
19678  */
19679 void
init_host_prognosis_iterator(iterator_t * iterator,report_host_t report_host)19680 init_host_prognosis_iterator (iterator_t* iterator, report_host_t report_host)
19681 {
19682   init_iterator (iterator,
19683                  "SELECT cves.name AS vulnerability,"
19684                  "       max(cves.severity) AS severity,"
19685                  "       max(cves.description) AS description,"
19686                  "       cpes.name AS location,"
19687                  "       (SELECT host FROM report_hosts"
19688                  "        WHERE id = %llu) AS host"
19689                  " FROM scap.cves, scap.cpes, scap.affected_products,"
19690                  "      report_host_details"
19691                  " WHERE report_host_details.report_host = %llu"
19692                  " AND cpes.name = report_host_details.value"
19693                  " AND report_host_details.name = 'App'"
19694                  " AND cpes.id=affected_products.cpe"
19695                  " AND cves.id=affected_products.cve"
19696                  " GROUP BY cves.id, vulnerability, location, host"
19697                  " ORDER BY cves.id ASC"
19698                  " LIMIT %s OFFSET 0;",
19699                  report_host,
19700                  report_host,
19701                  sql_select_limit (-1));
19702 }
19703 
19704 DEF_ACCESS (prognosis_iterator_cve, 0);
19705 
19706 /**
19707  * @brief Get the CVSS from a result iterator as a double.
19708  *
19709  * @param[in]  iterator  Iterator.
19710  *
19711  * @return CVSS.
19712  */
19713 double
prognosis_iterator_cvss_double(iterator_t * iterator)19714 prognosis_iterator_cvss_double (iterator_t* iterator)
19715 {
19716   if (iterator->done) return 0;
19717   return iterator_double (iterator, 1);
19718 }
19719 
19720 DEF_ACCESS (prognosis_iterator_description, 2);
19721 DEF_ACCESS (prognosis_iterator_cpe, 3);
19722 
19723 
19724 /* Reports. */
19725 
19726 /**
19727  * @brief Whether to ignore the Max Rows Per Page settings.
19728  */
19729 int ignore_max_rows_per_page = 0;
19730 
19731 /**
19732  * @brief Create a new GHashTable for containing resource rowids.
19733  *
19734  * @return The newly allocated GHashTable
19735  */
19736 static GHashTable *
new_resources_hashtable()19737 new_resources_hashtable ()
19738 {
19739   return g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, NULL);
19740 }
19741 
19742 /**
19743  * @brief Add reports affected by an override to an existing GHashtable.
19744  * This is used to add more reports to the hashtable from reports_for_override.
19745  *
19746  * @param[in]  reports_table The GHashtable to contain the report rowids.
19747  * @param[in]  override The override that selected reports must be affected by.
19748  */
19749 static void
reports_add_for_override(GHashTable * reports_table,override_t override)19750 reports_add_for_override (GHashTable *reports_table,
19751                           override_t override)
19752 {
19753   result_t result;
19754   task_t task;
19755   gchar *nvt_id;
19756   iterator_t reports;
19757 
19758   if (override == 0)
19759     return;
19760 
19761   sql_int64 (&result,
19762              "SELECT result FROM overrides WHERE id = %llu",
19763              override);
19764   sql_int64 (&task,
19765              "SELECT task FROM overrides WHERE id = %llu",
19766              override);
19767   nvt_id = sql_string ("SELECT nvt FROM overrides WHERE id = %llu",
19768                        override);
19769 
19770   if (result)
19771     {
19772       report_t *report = g_malloc0 (sizeof (report_t));
19773       sql_int64 (report,
19774                  "SELECT report FROM results"
19775                  " WHERE id = %llu AND nvt = '%s'",
19776                  result, nvt_id);
19777 
19778       if (*report)
19779         g_hash_table_add (reports_table, report);
19780       else
19781         g_free (report);
19782 
19783       return;
19784     }
19785   else if (task)
19786     {
19787       init_iterator (&reports,
19788                      "SELECT DISTINCT report FROM results"
19789                      " WHERE task = %llu AND nvt = '%s'",
19790                      task, nvt_id);
19791     }
19792   else
19793     {
19794       init_iterator (&reports,
19795                      "SELECT DISTINCT report FROM results"
19796                      " WHERE nvt = '%s'",
19797                      nvt_id);
19798     }
19799 
19800   while (next (&reports))
19801     {
19802       report_t *report = g_malloc0 (sizeof (report_t));
19803 
19804       *report = iterator_int64 (&reports, 0);
19805 
19806       if (g_hash_table_contains (reports_table, report) == 0)
19807         g_hash_table_add (reports_table, report);
19808       else
19809         g_free (report);
19810     }
19811   cleanup_iterator (&reports);
19812 }
19813 
19814 /**
19815  * @brief Get reports affected by an override in a GHashTable.
19816  *
19817  * @param[in]  override The override that selected reports must be affected by.
19818  *
19819  * @return A GHashtable containing the affected report rowids.
19820  */
19821 static GHashTable *
reports_for_override(override_t override)19822 reports_for_override (override_t override)
19823 {
19824   GHashTable *reports_table;
19825   reports_table = new_resources_hashtable ();
19826 
19827   reports_add_for_override (reports_table, override);
19828 
19829   return reports_table;
19830 }
19831 
19832 /**
19833  * @brief Add all reports to an existing GHashtable.
19834  *
19835  * @param[in]  reports_table The GHashtable to contain the report rowids.
19836  */
19837 static void
reports_add_all(GHashTable * reports_table)19838 reports_add_all (GHashTable *reports_table)
19839 {
19840   iterator_t reports;
19841 
19842   init_iterator (&reports,
19843                  "SELECT id FROM reports");
19844 
19845   while (next (&reports))
19846     {
19847       report_t *report = g_malloc0 (sizeof (report_t));
19848 
19849       *report = iterator_int64 (&reports, 0);
19850 
19851       if (g_hash_table_contains (reports_table, report) == 0)
19852         g_hash_table_add (reports_table, report);
19853       else
19854         g_free (report);
19855     }
19856 
19857   cleanup_iterator (&reports);
19858 }
19859 
19860 /**
19861  * @brief Get all reports in a GHashTable.
19862  *
19863  * @return A GHashtable containing the report rowids.
19864  */
19865 static GHashTable *
reports_hashtable()19866 reports_hashtable ()
19867 {
19868   GHashTable *reports_table;
19869   reports_table = new_resources_hashtable ();
19870 
19871   reports_add_all (reports_table);
19872 
19873   return reports_table;
19874 }
19875 
19876 /**
19877  * @brief Clear the report count cache for all reports of a user.
19878  *
19879  * @param[in]  uuid  UUID of user.
19880  */
19881 static void
reports_clear_count_cache(const gchar * uuid)19882 reports_clear_count_cache (const gchar *uuid)
19883 {
19884   gchar *quoted_uuid;
19885 
19886   quoted_uuid = sql_quote (uuid);
19887   sql ("DELETE FROM report_counts"
19888        " WHERE report_counts.user = (SELECT id FROM users"
19889        "                             WHERE uuid = '%s');",
19890        quoted_uuid);
19891   g_free (quoted_uuid);
19892 }
19893 
19894 /**
19895  * @brief Clear all report counts for all dynamic severity users.
19896  */
19897 void
reports_clear_count_cache_dynamic()19898 reports_clear_count_cache_dynamic ()
19899 {
19900   sql ("DELETE FROM report_counts"
19901        " WHERE report_counts.user IN (SELECT owner FROM settings"
19902        "                              WHERE name = 'Dynamic Severity'"
19903        "                              AND value = '1');");
19904 }
19905 
19906 /**
19907  * @brief Rebuild the report count cache for all reports and users.
19908  *
19909  * @param[in]  clear        Whether to clear the cache before rebuilding.
19910  * @param[out] changes_out  The number of processed user/report combinations.
19911  */
19912 static void
reports_build_count_cache(int clear,int * changes_out)19913 reports_build_count_cache (int clear, int* changes_out)
19914 {
19915   int changes;
19916   iterator_t reports;
19917   changes = 0;
19918 
19919   /* Clear cache of trashcan reports, we won't count them. */
19920   sql ("DELETE FROM report_counts"
19921        " WHERE (SELECT hidden = 2 FROM tasks"
19922        "        WHERE tasks.id = (SELECT task FROM reports"
19923        "                          WHERE reports.id = report_counts.report));");
19924 
19925   init_iterator (&reports,
19926                  "SELECT id FROM reports"
19927                  " WHERE (SELECT hidden = 0 FROM tasks"
19928                  "        WHERE tasks.id = task);");
19929 
19930   while (next (&reports))
19931     {
19932       report_t report = iterator_int64 (&reports, 0);
19933 
19934       report_cache_counts (report, clear, clear, NULL);
19935       changes ++;
19936     }
19937 
19938   cleanup_iterator (&reports);
19939 
19940   if (changes_out)
19941     *changes_out = changes;
19942 }
19943 
19944 /**
19945  * @brief Initializes an iterator for updating the report cache
19946  *
19947  * @param[in]  iterator       Iterator.
19948  * @param[in]  report         Report to select.
19949  * @param[in]  min_qod_limit  Limit for min_qod.
19950  * @param[in]  add_defaults   Whether to add default values.
19951  * @param[in]  users_where    Optional SQL clause to limit users.
19952  */
19953 void
init_report_counts_build_iterator(iterator_t * iterator,report_t report,int min_qod_limit,int add_defaults,const char * users_where)19954 init_report_counts_build_iterator (iterator_t *iterator, report_t report,
19955                                    int min_qod_limit, int add_defaults,
19956                                    const char *users_where)
19957 {
19958   gchar *report_id, *users_string;
19959 
19960   report_id = sql_string ("SELECT uuid FROM reports WHERE id = %llu;", report);
19961 
19962   users_string = acl_users_with_access_sql ("report", report_id, users_where);
19963   if (users_string == NULL)
19964     {
19965       init_iterator (iterator, "SELECT NULL WHERE NOT t();");
19966       g_free (report_id);
19967       return;
19968     }
19969 
19970   if (add_defaults && MIN_QOD_DEFAULT <= min_qod_limit)
19971     {
19972       init_iterator (iterator,
19973                      "SELECT * FROM"
19974                      " (WITH users_with_access (\"user\") AS %s"
19975                      "  SELECT DISTINCT min_qod, override, \"user\""
19976                      "  FROM report_counts"
19977                      "  WHERE report = %llu"
19978                      "    AND \"user\" IN (SELECT \"user\""
19979                      "                     FROM users_with_access)"
19980                      "    AND min_qod <= %d"
19981                      "  UNION SELECT 0 as min_qod, 0, \"user\""
19982                      "          FROM users_with_access"
19983                      "  UNION SELECT 0 as min_qod, 1, \"user\""
19984                      "          FROM users_with_access"
19985                      "  UNION SELECT %d as min_qod, 0, \"user\""
19986                      "          FROM users_with_access"
19987                      "  UNION SELECT %d as min_qod, 1, \"user\""
19988                      "          FROM users_with_access) AS inner_query"
19989                      " ORDER BY \"user\"",
19990                      users_string,
19991                      report,
19992                      min_qod_limit,
19993                      MIN_QOD_DEFAULT,
19994                      MIN_QOD_DEFAULT);
19995     }
19996   else if (add_defaults)
19997     {
19998       init_iterator (iterator,
19999                      "SELECT * FROM"
20000                      " (WITH users_with_access (\"user\") AS %s"
20001                      "  SELECT DISTINCT min_qod, override, \"user\""
20002                      "  FROM report_counts"
20003                      "  WHERE report = %llu"
20004                      "    AND min_qod <= %d"
20005                      "    AND \"user\" IN (SELECT \"user\""
20006                      "                     FROM users_with_access)"
20007                      "  UNION SELECT 0 as min_qod, 0, \"user\""
20008                      "          FROM users_with_access"
20009                      "  UNION SELECT 0 as min_qod, 1, \"user\""
20010                      "          FROM users_with_access) AS inner_query"
20011                      " ORDER BY \"user\"",
20012                      users_string,
20013                      report,
20014                      min_qod_limit);
20015     }
20016   else
20017     {
20018       init_iterator (iterator,
20019                      "WITH users_with_access (\"user\") AS %s"
20020                      " SELECT DISTINCT min_qod, override, \"user\""
20021                      " FROM report_counts"
20022                      " WHERE report = %llu"
20023                      "   AND min_qod <= %d"
20024                      "   AND \"user\" IN (SELECT \"user\""
20025                      "                    FROM users_with_access)"
20026                      " ORDER BY \"user\"",
20027                      users_string,
20028                      report,
20029                      min_qod_limit);
20030     }
20031   g_free (users_string);
20032   g_free (report_id);
20033 }
20034 
20035 /**
20036  * @brief Get the min_qod from a report_counts build iterator.
20037  *
20038  * @param[in]  iterator  Iterator.
20039  *
20040  * @return The min_qod.
20041  */
20042 static int
report_counts_build_iterator_min_qod(iterator_t * iterator)20043 report_counts_build_iterator_min_qod (iterator_t *iterator)
20044 {
20045   return iterator_int (iterator, 0);
20046 }
20047 
20048 /**
20049  * @brief Get the override flag from a report_counts build iterator.
20050  *
20051  * @param[in]  iterator  Iterator.
20052  *
20053  * @return Whether the report counts are using overrides.
20054  */
20055 static int
report_counts_build_iterator_override(iterator_t * iterator)20056 report_counts_build_iterator_override (iterator_t *iterator)
20057 {
20058   return iterator_int (iterator, 1);
20059 }
20060 
20061 /**
20062  * @brief Get the user from a report_counts build iterator.
20063  *
20064  * @param[in]  iterator  Iterator.
20065  *
20066  * @return The min_qod.
20067  */
20068 static user_t
report_counts_build_iterator_user(iterator_t * iterator)20069 report_counts_build_iterator_user (iterator_t *iterator)
20070 {
20071   return iterator_int64 (iterator, 2);
20072 }
20073 
20074 /**
20075  * @brief Cache report counts and clear existing caches if requested.
20076  *
20077  * @param[in]  report             Report to cache counts of.
20078  * @param[in]  clear_original     Whether to clear existing cache for
20079  *                                 original severity.
20080  * @param[in]  clear_overridden   Whether to clear existing cache for
20081  *                                 overridden severity.
20082  * @param[in]  users_where        Optional SQL clause to limit users.
20083  */
20084 static void
report_cache_counts(report_t report,int clear_original,int clear_overridden,const char * users_where)20085 report_cache_counts (report_t report, int clear_original, int clear_overridden,
20086                      const char* users_where)
20087 {
20088   iterator_t cache_iterator;
20089   int holes, infos, logs, warnings, false_positives;
20090   double severity;
20091   get_data_t *get = NULL;
20092   gchar *old_user_id;
20093 
20094   old_user_id = current_credentials.uuid;
20095   init_report_counts_build_iterator (&cache_iterator, report, INT_MAX, 1,
20096                                      users_where);
20097 
20098   while (next (&cache_iterator))
20099     {
20100       int override = report_counts_build_iterator_override (&cache_iterator);
20101       int min_qod = report_counts_build_iterator_min_qod (&cache_iterator);
20102       user_t user = report_counts_build_iterator_user (&cache_iterator);
20103 
20104       current_credentials.uuid
20105         = sql_string ("SELECT uuid FROM users WHERE id = %llu",
20106                       user);
20107       manage_session_init (current_credentials.uuid);
20108 
20109       get = report_results_get_data (1, -1, override, min_qod);
20110 
20111       if ((clear_original && override == 0) || (clear_overridden && override))
20112         {
20113           sql ("DELETE FROM report_counts"
20114                " WHERE report = %llu"
20115                "   AND \"user\" = %llu"
20116                "   AND override = %d"
20117                "   AND min_qod = %d",
20118                report, user, override, min_qod);
20119         }
20120 
20121       report_counts_id (report, &holes, &infos, &logs, &warnings,
20122                         &false_positives, &severity, get, NULL);
20123 
20124       get_data_reset (get);
20125       g_free (get);
20126       g_free (current_credentials.uuid);
20127     }
20128   cleanup_iterator (&cache_iterator);
20129   current_credentials.uuid = old_user_id;
20130   manage_session_init (current_credentials.uuid);
20131 }
20132 
20133 /**
20134  * @brief Clear report counts .
20135  *
20136  * @param[in]  report  Report.
20137  * @param[in]  clear_original     Whether to clear existing cache for
20138  *                                 original severity.
20139  * @param[in]  clear_overridden   Whether to clear existing cache for
20140  *                                 overridden severity.
20141  * @param[in]  users_where        Optional SQL clause to limit users.
20142  */
20143 static void
report_clear_count_cache(report_t report,int clear_original,int clear_overridden,const char * users_where)20144 report_clear_count_cache (report_t report,
20145                           int clear_original, int clear_overridden,
20146                           const char* users_where)
20147 {
20148   gchar *extra_where = NULL;
20149   if (users_where)
20150     {
20151       extra_where
20152         = g_strdup_printf (" AND \"user\" IN (SELECT id FROM users WHERE %s)",
20153                            users_where);
20154     }
20155 
20156   if (clear_original && clear_overridden)
20157     {
20158       sql ("DELETE FROM report_counts"
20159            " WHERE report = %llu"
20160            "%s",
20161            report,
20162            extra_where ? extra_where : "");
20163     }
20164   else if (clear_original || clear_overridden)
20165     {
20166       int override = clear_overridden ? 1 : 0;
20167       sql ("DELETE FROM report_counts"
20168            " WHERE report = %llu"
20169            "   AND override = %d"
20170            "%s",
20171            report,
20172            override,
20173            extra_where ? extra_where : "");
20174     }
20175 }
20176 
20177 /**
20178  * @brief Make a report.
20179  *
20180  * @param[in]  task    The task associated with the report.
20181  * @param[in]  uuid    The UUID of the report.
20182  * @param[in]  status  The run status of the scan associated with the report.
20183  *
20184  * @return A report descriptor for the new report.
20185  */
20186 report_t
make_report(task_t task,const char * uuid,task_status_t status)20187 make_report (task_t task, const char* uuid, task_status_t status)
20188 {
20189   sql ("INSERT into reports (uuid, owner, task, date, comment,"
20190        " scan_run_status, slave_progress)"
20191        " VALUES ('%s',"
20192        " (SELECT owner FROM tasks WHERE tasks.id = %llu),"
20193        " %llu, %i, '', %u, 0);",
20194        uuid, task, task, time (NULL), status);
20195   return sql_last_insert_id ();
20196 }
20197 
20198 /**
20199  * @brief Create the current report for a task.
20200  *
20201  * @param[in]   task       The task.
20202  * @param[out]  report_id  Report ID.
20203  * @param[in]   status     Run status of scan associated with report.
20204  *
20205  * @return 0 success, -1 global_current_report is already set, -2 failed to
20206  *         generate ID.
20207  */
20208 int
create_current_report(task_t task,char ** report_id,task_status_t status)20209 create_current_report (task_t task, char **report_id, task_status_t status)
20210 {
20211   char *id;
20212 
20213   assert (global_current_report == (report_t) 0);
20214 
20215   if (global_current_report) return -1;
20216 
20217   if (report_id == NULL) report_id = &id;
20218 
20219   /* Generate report UUID. */
20220 
20221   *report_id = gvm_uuid_make ();
20222   if (*report_id == NULL) return -2;
20223 
20224   /* Create the report. */
20225 
20226   global_current_report = make_report (task, *report_id, status);
20227 
20228   set_report_scheduled (global_current_report);
20229 
20230   return 0;
20231 }
20232 
20233 /**
20234  * @brief Free a host detail.
20235  *
20236  * @param[in]  detail  Host detail.
20237  */
20238 void
host_detail_free(host_detail_t * detail)20239 host_detail_free (host_detail_t *detail)
20240 {
20241   g_free (detail->ip);
20242   g_free (detail->name);
20243   g_free (detail->source_desc);
20244   g_free (detail->source_name);
20245   g_free (detail->source_type);
20246   g_free (detail->value);
20247 }
20248 
20249 /**
20250  * @brief Insert a host detail into a report.
20251  *
20252  * @param[in]   report      The detail's report.
20253  * @param[in]   host        The detail's host.
20254  * @param[in]   s_type      The detail's source type.
20255  * @param[in]   s_name      The detail's source name.
20256  * @param[in]   s_desc      The detail's source description.
20257  * @param[in]   name        The detail's name.
20258  * @param[in]   value       The detail's value.
20259  */
20260 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)20261 insert_report_host_detail (report_t report, const char *host,
20262                            const char *s_type, const char *s_name,
20263                            const char *s_desc, const char *name,
20264                            const char *value)
20265 {
20266   char *quoted_host, *quoted_source_name, *quoted_source_type;
20267   char *quoted_source_desc, *quoted_name, *quoted_value;
20268 
20269   quoted_host = sql_quote (host);
20270   quoted_source_type = sql_quote (s_type);
20271   quoted_source_name = sql_quote (s_name);
20272   quoted_source_desc = sql_quote (s_desc);
20273   quoted_name = sql_quote (name);
20274   quoted_value = sql_quote (value);
20275   sql ("INSERT INTO report_host_details"
20276        " (report_host, source_type, source_name, source_description,"
20277        "  name, value)"
20278        " VALUES"
20279        " ((SELECT id FROM report_hosts"
20280        "   WHERE report = %llu AND host = '%s'),"
20281        "  '%s', '%s', '%s', '%s', '%s');",
20282        report, quoted_host, quoted_source_type, quoted_source_name,
20283        quoted_source_desc, quoted_name, quoted_value);
20284 
20285   g_free (quoted_host);
20286   g_free (quoted_source_type);
20287   g_free (quoted_source_name);
20288   g_free (quoted_source_desc);
20289   g_free (quoted_name);
20290   g_free (quoted_value);
20291 }
20292 
20293 /**
20294  * @brief Maximum number of values per insert, when uploading report.
20295  */
20296 #define CREATE_REPORT_INSERT_SIZE 300
20297 
20298 /**
20299  * @brief Number of results per transaction, when uploading report.
20300  */
20301 #define CREATE_REPORT_CHUNK_SIZE 10
20302 
20303 /**
20304  * @brief Number of microseconds to sleep between insert chunks.
20305  */
20306 #define CREATE_REPORT_CHUNK_SLEEP 1000
20307 
20308 /**
20309  * @brief Create a report from an array of results.
20310  *
20311  * @param[in]   results       Array of create_report_result_t pointers.
20312  * @param[in]   task_id       UUID of container task, or NULL to create new one.
20313  * @param[in]   in_assets     Whether to create assets from the report.
20314  * @param[in]   scan_start    Scan start time text.
20315  * @param[in]   scan_end      Scan end time text.
20316  * @param[in]   host_starts   Array of create_report_result_t pointers.  Host
20317  *                            name in host, time in description.
20318  * @param[in]   host_ends     Array of create_report_result_t pointers.  Host
20319  *                            name in host, time in description.
20320  * @param[in]   details       Array of host_detail_t pointers.
20321  * @param[out]  report_id     Report ID.
20322  *
20323  * @return 0 success, 99 permission denied, -1 error, -2 failed to generate ID,
20324  *         -3 task_id is NULL, -4 failed to find task, -5 task must be
20325  *         container, -6 permission to create assets denied.
20326  */
20327 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)20328 create_report (array_t *results, const char *task_id, const char *in_assets,
20329                const char *scan_start, const char *scan_end,
20330                array_t *host_starts, array_t *host_ends, array_t *details,
20331                char **report_id)
20332 {
20333   int index, in_assets_int, count, insert_count, first, rc;
20334   create_report_result_t *result, *end, *start;
20335   report_t report;
20336   user_t owner;
20337   task_t task;
20338   pid_t pid;
20339   host_detail_t *detail;
20340   GString *insert;
20341 
20342   in_assets_int
20343     = (in_assets && strcmp (in_assets, "") && strcmp (in_assets, "0"));
20344 
20345   if (in_assets_int && acl_user_may ("create_asset") == 0)
20346     return -6;
20347 
20348   g_debug ("%s", __func__);
20349 
20350   if (acl_user_may ("create_report") == 0)
20351     return 99;
20352 
20353   if (task_id == NULL)
20354     return -3;
20355 
20356   sql_begin_immediate ();
20357 
20358   /* Find the task. */
20359 
20360   rc = 0;
20361 
20362   /* It's important that the task is not in the trash, because we
20363    * are inserting results below.  This find function will fail if
20364    * the task is in the trash. */
20365   if (find_task_with_permission (task_id, &task, "modify_task"))
20366     rc = -1;
20367   else if (task == 0)
20368     rc = -4;
20369   else if (task_target (task))
20370     rc = -5;
20371   if (rc)
20372     {
20373       sql_rollback ();
20374       return rc;
20375     }
20376 
20377   /* Generate report UUID. */
20378 
20379   *report_id = gvm_uuid_make ();
20380   if (*report_id == NULL) return -2;
20381 
20382   /* Create the report. */
20383 
20384   report = make_report (task, *report_id, TASK_STATUS_RUNNING);
20385 
20386   if (scan_start)
20387     {
20388       sql ("UPDATE reports SET start_time = %i WHERE id = %llu;",
20389            parse_iso_time (scan_start),
20390            report);
20391     }
20392 
20393   if (scan_end)
20394     {
20395       sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
20396            parse_iso_time (scan_end),
20397            report);
20398     }
20399 
20400   /* Show that the upload has started. */
20401 
20402   set_task_run_status (task, TASK_STATUS_RUNNING);
20403   sql ("UPDATE tasks SET upload_result_count = %llu WHERE id = %llu;",
20404        results->len,
20405        task);
20406   sql_commit ();
20407 
20408   /* Fork a child to import the results while the parent responds to the
20409    * client. */
20410 
20411   pid = fork ();
20412   switch (pid)
20413     {
20414       case 0:
20415         {
20416           /* Child.
20417            *
20418            * Fork again so the parent can wait on the child, to prevent
20419            * zombies. */
20420           cleanup_manage_process (FALSE);
20421           pid = fork ();
20422           switch (pid)
20423             {
20424               case 0:
20425                 /* Grandchild.  Reopen the database (required after fork) and carry on
20426                  * to import the reports, . */
20427                 reinit_manage_process ();
20428                 break;
20429               case -1:
20430                 /* Grandchild's parent when error. */
20431                 g_warning ("%s: fork: %s", __func__, strerror (errno));
20432                 exit (EXIT_FAILURE);
20433                 break;
20434               default:
20435                 /* Grandchild's parent.  Exit, to close parent's wait. */
20436                 g_debug ("%s: %i forked %i", __func__, getpid (), pid);
20437                 exit (EXIT_SUCCESS);
20438                 break;
20439             }
20440         }
20441         break;
20442       case -1:
20443         /* Parent when error. */
20444         g_warning ("%s: fork: %s", __func__, strerror (errno));
20445         global_current_report = report;
20446         set_task_interrupted (task,
20447                               "Failed to fork child to import report."
20448                               "  Setting task status to Interrupted.");
20449         global_current_report = 0;
20450         return -1;
20451         break;
20452       default:
20453         {
20454           int status;
20455 
20456           /* Parent.  Wait to prevent zombie, then return to respond to client. */
20457           g_debug ("%s: %i forked %i", __func__, getpid (), pid);
20458           while (waitpid (pid, &status, 0) < 0)
20459             {
20460               if (errno == ECHILD)
20461                 {
20462                   g_warning ("%s: Failed to get child exit status",
20463                              __func__);
20464                   return -1;
20465                 }
20466               if (errno == EINTR)
20467                 continue;
20468               g_warning ("%s: waitpid: %s",
20469                          __func__,
20470                          strerror (errno));
20471               return -1;
20472             }
20473           return 0;
20474           break;
20475         }
20476     }
20477 
20478   proctitle_set ("gvmd: Importing results");
20479 
20480   /* Add the results. */
20481 
20482   if (sql_int64 (&owner,
20483                  "SELECT owner FROM tasks WHERE tasks.id = %llu",
20484                  task))
20485     {
20486       g_warning ("%s: failed to get owner of task", __func__);
20487       return -1;
20488     }
20489 
20490   sql_begin_immediate ();
20491   g_debug ("%s: add hosts", __func__);
20492   index = 0;
20493   while ((start = (create_report_result_t*) g_ptr_array_index (host_starts,
20494                                                                index++)))
20495     if (start->host && start->description)
20496       manage_report_host_add (report, start->host,
20497                               parse_iso_time (start->description),
20498                               0);
20499 
20500   g_debug ("%s: add results", __func__);
20501   insert = g_string_new ("");
20502   index = 0;
20503   first = 1;
20504   insert_count = 0;
20505   count = 0;
20506   while ((result = (create_report_result_t*) g_ptr_array_index (results,
20507                                                                 index++)))
20508     {
20509       gchar *quoted_host, *quoted_hostname, *quoted_port, *quoted_nvt_oid;
20510       gchar *quoted_description, *quoted_scan_nvt_version, *quoted_severity;
20511       gchar *quoted_qod, *quoted_qod_type;
20512       g_debug ("%s: add results: index: %i", __func__, index);
20513 
20514       quoted_host = sql_quote (result->host ? result->host : "");
20515       quoted_hostname = sql_quote (result->hostname ? result->hostname : "");
20516       quoted_port = sql_quote (result->port ? result->port : "");
20517       quoted_nvt_oid = sql_quote (result->nvt_oid ? result->nvt_oid : "");
20518       quoted_description = sql_quote (result->description
20519                                        ? result->description
20520                                        : "");
20521       quoted_scan_nvt_version = sql_quote (result->scan_nvt_version
20522                                        ? result->scan_nvt_version
20523                                        : "");
20524       quoted_severity =  sql_quote (result->severity ? result->severity : "");
20525       if (result->qod && strcmp (result->qod, "") && strcmp (result->qod, "0"))
20526         quoted_qod = sql_quote (result->qod);
20527       else
20528         quoted_qod = g_strdup (G_STRINGIFY (QOD_DEFAULT));
20529       quoted_qod_type = sql_quote (result->qod_type ? result->qod_type : "");
20530       result_nvt_notice (quoted_nvt_oid);
20531 
20532       if (first)
20533         g_string_append (insert,
20534                          "INSERT INTO results"
20535                          " (uuid, owner, date, task, host, hostname, port,"
20536                          "  nvt, type, description,"
20537                          "  nvt_version, severity, qod, qod_type,"
20538                          "  result_nvt, report)"
20539                          " VALUES");
20540       else
20541         g_string_append (insert, ", ");
20542       first = 0;
20543       g_string_append_printf (insert,
20544                               " (make_uuid (), %llu, m_now (), %llu, '%s',"
20545                               "  '%s', '%s', '%s', '%s', '%s', '%s', '%s',"
20546                               "  '%s', '%s',"
20547                               "  (SELECT id FROM result_nvts WHERE nvt = '%s'),"
20548                               "  %llu)",
20549                               owner,
20550                               task,
20551                               quoted_host,
20552                               quoted_hostname,
20553                               quoted_port,
20554                               quoted_nvt_oid,
20555                               result->threat
20556                                ? threat_message_type (result->threat)
20557                                : "Log Message",
20558                               quoted_description,
20559                               quoted_scan_nvt_version,
20560                               quoted_severity,
20561                               quoted_qod,
20562                               quoted_qod_type,
20563                               quoted_nvt_oid,
20564                               report);
20565 
20566       /* Limit the number of results inserted at a time. */
20567       if (insert_count == CREATE_REPORT_INSERT_SIZE)
20568         {
20569           sql ("%s", insert->str);
20570           g_string_truncate (insert, 0);
20571           count++;
20572           insert_count = 0;
20573           first = 1;
20574 
20575           if (count == CREATE_REPORT_CHUNK_SIZE)
20576             {
20577               report_cache_counts (report, 1, 1, NULL);
20578               sql_commit ();
20579               gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
20580               sql_begin_immediate ();
20581               count = 0;
20582             }
20583         }
20584       insert_count++;
20585 
20586       g_free (quoted_host);
20587       g_free (quoted_hostname);
20588       g_free (quoted_port);
20589       g_free (quoted_nvt_oid);
20590       g_free (quoted_description);
20591       g_free (quoted_scan_nvt_version);
20592       g_free (quoted_severity);
20593       g_free (quoted_qod);
20594       g_free (quoted_qod_type);
20595     }
20596 
20597   if (first == 0)
20598     {
20599       sql ("%s", insert->str);
20600       report_cache_counts (report, 1, 1, NULL);
20601       sql_commit ();
20602       gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
20603       sql_begin_immediate ();
20604     }
20605 
20606   sql ("INSERT INTO result_nvt_reports (result_nvt, report)"
20607        " SELECT distinct result_nvt, %llu FROM results"
20608        " WHERE results.report = %llu;",
20609        report,
20610        report);
20611 
20612   g_debug ("%s: add host ends", __func__);
20613   index = 0;
20614   count = 0;
20615   while ((end = (create_report_result_t*) g_ptr_array_index (host_ends,
20616                                                              index++)))
20617     if (end->host)
20618       {
20619         gchar *quoted_host;
20620 
20621         quoted_host = sql_quote (end->host);
20622 
20623         if (end->description)
20624           sql ("UPDATE report_hosts SET end_time = %i"
20625                " WHERE report = %llu AND host = '%s';",
20626                parse_iso_time (end->description),
20627                report,
20628                quoted_host);
20629         else
20630           sql ("UPDATE report_hosts SET end_time = NULL"
20631                " WHERE report = %llu AND host = '%s';",
20632                report,
20633                quoted_host);
20634 
20635         g_free (quoted_host);
20636 
20637         count++;
20638         if (count == CREATE_REPORT_CHUNK_SIZE)
20639           {
20640             sql_commit ();
20641             gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
20642             sql_begin_immediate ();
20643             count = 0;
20644           }
20645       }
20646 
20647   g_debug ("%s: add host details", __func__);
20648   index = 0;
20649   first = 1;
20650   count = 0;
20651   insert_count = 0;
20652   g_string_truncate (insert, 0);
20653   while ((detail = (host_detail_t*) g_ptr_array_index (details, index++)))
20654     if (detail->ip && detail->name)
20655       {
20656         char *quoted_host, *quoted_source_name, *quoted_source_type;
20657         char *quoted_source_desc, *quoted_name, *quoted_value;
20658 
20659         quoted_host = sql_quote (detail->ip);
20660         quoted_source_type = sql_quote (detail->source_type ?: "");
20661         quoted_source_name = sql_quote (detail->source_name ?: "");
20662         quoted_source_desc = sql_quote (detail->source_desc ?: "");
20663         quoted_name = sql_quote (detail->name);
20664         quoted_value = sql_quote (detail->value ?: "");
20665 
20666         if (first)
20667           g_string_append (insert,
20668                            "INSERT INTO report_host_details"
20669                            " (report_host, source_type, source_name,"
20670                            "  source_description, name, value)"
20671                            " VALUES");
20672         else
20673           g_string_append (insert, ", ");
20674         first = 0;
20675 
20676         g_string_append_printf (insert,
20677                                 " ((SELECT id FROM report_hosts"
20678                                 "   WHERE report = %llu AND host = '%s'),"
20679                                 "  '%s', '%s', '%s', '%s', '%s')",
20680                                 report, quoted_host, quoted_source_type,
20681                                 quoted_source_name, quoted_source_desc,
20682                                 quoted_name, quoted_value);
20683 
20684         g_free (quoted_host);
20685         g_free (quoted_source_type);
20686         g_free (quoted_source_name);
20687         g_free (quoted_source_desc);
20688         g_free (quoted_name);
20689         g_free (quoted_value);
20690 
20691         /* Limit the number of details inserted at a time. */
20692         if (insert_count == CREATE_REPORT_INSERT_SIZE)
20693           {
20694             sql ("%s", insert->str);
20695             g_string_truncate (insert, 0);
20696             count++;
20697             insert_count = 0;
20698             first = 1;
20699 
20700             if (count == CREATE_REPORT_CHUNK_SIZE)
20701               {
20702                 sql_commit ();
20703                 gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
20704                 sql_begin_immediate ();
20705                 count = 0;
20706               }
20707           }
20708         insert_count++;
20709       }
20710 
20711   sql_commit ();
20712 
20713   index = 0;
20714   sql_begin_immediate ();
20715   while ((end = (create_report_result_t*) g_ptr_array_index (host_ends,
20716                                                              index++)))
20717     if (end->host)
20718       {
20719         sql_commit ();
20720         gvm_usleep (CREATE_REPORT_CHUNK_SLEEP);
20721         add_assets_from_host_in_report (report, end->host);
20722         sql_begin_immediate ();
20723       }
20724 
20725   if (first == 0)
20726     sql ("%s", insert->str);
20727 
20728   sql_commit ();
20729   g_string_free (insert, TRUE);
20730 
20731   current_scanner_task = task;
20732   global_current_report = report;
20733   set_task_run_status (task, TASK_STATUS_DONE);
20734   current_scanner_task = 0;
20735   global_current_report = 0;
20736 
20737   if (in_assets_int)
20738     {
20739       create_asset_report (*report_id, "");
20740     }
20741 
20742   exit (EXIT_SUCCESS);
20743   return 0;
20744 }
20745 
20746 /**
20747  * @brief Return the UUID of a report.
20748  *
20749  * @param[in]  report  Report.
20750  *
20751  * @return Report UUID.
20752  */
20753 char*
report_uuid(report_t report)20754 report_uuid (report_t report)
20755 {
20756   return sql_string ("SELECT uuid FROM reports WHERE id = %llu;",
20757                      report);
20758 }
20759 
20760 /**
20761  * @brief Return the task of a report.
20762  *
20763  * @param[in]   report  A report.
20764  * @param[out]  task    Task return, 0 if successfully failed to find task.
20765  *
20766  * @return FALSE on success (including if failed to find report), TRUE on error.
20767  */
20768 gboolean
report_task(report_t report,task_t * task)20769 report_task (report_t report, task_t *task)
20770 {
20771   switch (sql_int64 (task,
20772                      "SELECT task FROM reports WHERE id = %llu;",
20773                      report))
20774     {
20775       case 0:
20776         break;
20777       case 1:        /* Too few rows in result of query. */
20778         *task = 0;
20779         break;
20780       default:       /* Programming error. */
20781         assert (0);
20782       case -1:
20783         return TRUE;
20784         break;
20785     }
20786   return FALSE;
20787 }
20788 
20789 /**
20790  * @brief Get compliance counts for a report.
20791  *
20792  * @param[in]  report_id              UUID of the report.
20793  * @param[out] compliance_yes         Number of "YES" results.
20794  * @param[out] compliance_no          Number of "NO" results.
20795  * @param[out] compliance_incomplete  Number of "INCOMPLETE" results.
20796  */
20797 void
report_compliance_by_uuid(const char * report_id,int * compliance_yes,int * compliance_no,int * compliance_incomplete)20798 report_compliance_by_uuid (const char *report_id,
20799                            int *compliance_yes,
20800                            int *compliance_no,
20801                            int *compliance_incomplete)
20802 {
20803   report_t report;
20804   gchar *quoted_uuid = sql_quote (report_id);
20805   sql_int64 (&report,
20806              "SELECT id FROM reports WHERE uuid = '%s';",
20807              quoted_uuid);
20808 
20809   if (compliance_yes)
20810     {
20811       *compliance_yes
20812         = sql_int ("SELECT count(*) FROM results"
20813                    " WHERE report = %llu"
20814                    " AND description LIKE 'Compliant:%%YES%%';",
20815                    report);
20816     }
20817 
20818   if (compliance_no)
20819     {
20820       *compliance_no
20821         = sql_int ("SELECT count(*) FROM results"
20822                    " WHERE report = %llu"
20823                    " AND description LIKE 'Compliant:%%NO%%';",
20824                    report);
20825     }
20826 
20827   if (compliance_incomplete)
20828     {
20829       *compliance_incomplete
20830         = sql_int ("SELECT count(*) FROM results"
20831                    " WHERE report = %llu"
20832                    " AND description LIKE 'Compliant:%%INCOMPLETE%%';",
20833                    report);
20834     }
20835 
20836   g_free (quoted_uuid);
20837 }
20838 
20839 /**
20840  * @brief Return the source interface of a report.
20841  *
20842  * @param[in]  report  Report.
20843  *
20844  * @return Source interface.
20845  */
20846 static char*
report_source_iface(report_t report)20847 report_source_iface (report_t report)
20848 {
20849   return sql_string ("SELECT source_iface FROM reports WHERE id = %llu;",
20850                      report);
20851 }
20852 
20853 /**
20854  * @brief Add a result to a report.
20855  *
20856  * @param[in]  report  The report.
20857  * @param[in]  result  The result.
20858  */
20859 static void
report_add_result_for_buffer(report_t report,result_t result)20860 report_add_result_for_buffer (report_t report, result_t result)
20861 {
20862   double severity, ov_severity;
20863   int qod;
20864   rowid_t rowid;
20865   iterator_t cache_iterator;
20866   user_t previous_user = 0;
20867 
20868   assert (result);
20869 
20870   if (report == 0)
20871     return;
20872 
20873   if (sql_int ("SELECT NOT EXISTS (SELECT * from result_nvt_reports"
20874                "                   WHERE result_nvt = (SELECT result_nvt"
20875                "                                       FROM results"
20876                "                                       WHERE id = %llu)"
20877                "                   AND report = %llu);",
20878        result,
20879        report))
20880     sql ("INSERT INTO result_nvt_reports (result_nvt, report)"
20881          " VALUES ((SELECT result_nvt FROM results WHERE id = %llu),"
20882          "         %llu);",
20883          result,
20884          report);
20885 
20886   qod = sql_int ("SELECT qod FROM results WHERE id = %llu;",
20887                  result);
20888 
20889   severity = sql_double ("SELECT severity FROM results WHERE id = %llu;",
20890                          result);
20891   ov_severity = severity;
20892 
20893   init_report_counts_build_iterator (&cache_iterator, report, qod, 1, NULL);
20894   while (next (&cache_iterator))
20895     {
20896       int min_qod = report_counts_build_iterator_min_qod (&cache_iterator);
20897       int override = report_counts_build_iterator_override (&cache_iterator);
20898       user_t user = report_counts_build_iterator_user (&cache_iterator);
20899 
20900       if (override && user != previous_user)
20901         {
20902           char *ov_severity_str;
20903           gchar *owned_clause, *with_clause;
20904 
20905           owned_clause = acl_where_owned_for_get ("override", NULL, NULL,
20906                                                   &with_clause);
20907 
20908           ov_severity_str
20909             = sql_string ("%s"
20910                           " SELECT coalesce (overrides.new_severity, %1.1f)"
20911                           " FROM overrides, results"
20912                           " WHERE results.id = %llu"
20913                           " AND overrides.nvt = results.nvt"
20914                           " AND %s"
20915                           " AND ((overrides.end_time = 0)"
20916                           "      OR (overrides.end_time >= m_now ()))"
20917                           " AND (overrides.task ="
20918                           "      (SELECT reports.task FROM reports"
20919                           "       WHERE reports.id = %llu)"
20920                           "      OR overrides.task = 0)"
20921                           " AND (overrides.result = results.id"
20922                           "      OR overrides.result = 0)"
20923                           " AND (overrides.hosts is NULL"
20924                           "      OR overrides.hosts = ''"
20925                           "      OR hosts_contains (overrides.hosts,"
20926                           "                         results.host))"
20927                           " AND (overrides.port is NULL"
20928                           "      OR overrides.port = ''"
20929                           "      OR overrides.port = results.port)"
20930                           " AND severity_matches_ov (%1.1f,"
20931                           "                          overrides.severity)"
20932                           " ORDER BY overrides.result DESC,"
20933                           "   overrides.task DESC, overrides.port DESC,"
20934                           "   overrides.severity ASC,"
20935                           "   overrides.creation_time DESC"
20936                           " LIMIT 1",
20937                           with_clause ? with_clause : "",
20938                           severity,
20939                           result,
20940                           owned_clause,
20941                           report,
20942                           severity);
20943 
20944           g_free (with_clause);
20945           g_free (owned_clause);
20946 
20947           if (ov_severity_str == NULL
20948               || (sscanf (ov_severity_str, "%lf", &ov_severity) != 1))
20949             ov_severity = severity;
20950 
20951           free (ov_severity_str);
20952 
20953           previous_user = user;
20954         }
20955 
20956       rowid = 0;
20957       sql_int64 (&rowid,
20958                  "SELECT id FROM report_counts"
20959                  " WHERE report = %llu"
20960                  " AND \"user\" = %llu"
20961                  " AND override = %d"
20962                  " AND severity = %1.1f"
20963                  " AND min_qod = %d",
20964                  report, user, override,
20965                  override ? ov_severity : severity,
20966                  min_qod);
20967       if (rowid)
20968         sql ("UPDATE report_counts"
20969             " SET count = count + 1"
20970             " WHERE id = %llu;",
20971             rowid);
20972       else
20973         sql ("INSERT INTO report_counts"
20974              " (report, \"user\", override, min_qod, severity, count, end_time)"
20975              " VALUES"
20976              " (%llu, %llu, %d, %d, %1.1f, 1, 0);",
20977              report, user, override,
20978              override ? ov_severity : severity,
20979              min_qod);
20980 
20981     }
20982   cleanup_iterator (&cache_iterator);
20983 }
20984 
20985 /**
20986  * @brief Add a result to a report.
20987  *
20988  * @param[in]  report  The report.
20989  * @param[in]  result  The result.
20990  */
20991 void
report_add_result(report_t report,result_t result)20992 report_add_result (report_t report, result_t result)
20993 {
20994   if (report == 0 || result == 0)
20995     return;
20996 
20997   sql ("UPDATE results SET report = %llu,"
20998        "                   owner = (SELECT reports.owner"
20999        "                            FROM reports WHERE id = %llu)"
21000        " WHERE id = %llu;",
21001        report, report, result);
21002 
21003   report_add_result_for_buffer (report, result);
21004 
21005   sql ("UPDATE report_counts"
21006        " SET end_time = (SELECT coalesce(min(overrides.end_time), 0)"
21007        "                 FROM overrides, results"
21008        "                 WHERE overrides.nvt = results.nvt"
21009        "                 AND results.report = %llu"
21010        "                 AND overrides.end_time >= m_now ())"
21011        " WHERE report = %llu AND override = 1;",
21012        report, report);
21013 }
21014 
21015 /**
21016  * @brief Add results from an array to a report.
21017  *
21018  * @param[in]  report   The report to add the results to.
21019  * @param[in]  results  GArray containing the row ids of the results to add.
21020  */
21021 void
report_add_results_array(report_t report,GArray * results)21022 report_add_results_array (report_t report, GArray *results)
21023 {
21024   GString *array_sql;
21025   int index;
21026 
21027   if (report == 0 || results == NULL || results->len == 0)
21028     return;
21029 
21030   array_sql = g_string_new ("(");
21031   for (index = 0; index < results->len; index++)
21032     {
21033       result_t result;
21034       result = g_array_index (results, result_t, index);
21035 
21036       if (index)
21037         g_string_append (array_sql, ", ");
21038       g_string_append_printf (array_sql, "%llu", result);
21039     }
21040   g_string_append_c (array_sql, ')');
21041 
21042   sql ("UPDATE results SET report = %llu,"
21043        "                   owner = (SELECT reports.owner"
21044        "                            FROM reports WHERE id = %llu)"
21045        " WHERE id IN %s;",
21046        report, report, array_sql->str);
21047 
21048   for (index = 0; index < results->len; index++)
21049     {
21050       result_t result;
21051       result = g_array_index (results, result_t, index);
21052 
21053       report_add_result_for_buffer (report, result);
21054     }
21055 
21056   sql ("UPDATE report_counts"
21057        " SET end_time = (SELECT coalesce(min(overrides.end_time), 0)"
21058        "                 FROM overrides, results"
21059        "                 WHERE overrides.nvt = results.nvt"
21060        "                 AND results.report = %llu"
21061        "                 AND overrides.end_time >= m_now ())"
21062        " WHERE report = %llu AND override = 1;",
21063        report, report);
21064 
21065   g_string_free (array_sql, TRUE);
21066 }
21067 
21068 /**
21069  * @brief Filter columns for report iterator.
21070  */
21071 #define REPORT_ITERATOR_FILTER_COLUMNS                                         \
21072  { ANON_GET_ITERATOR_FILTER_COLUMNS, "task_id", "name", "date", "status",      \
21073    "task", "severity", "false_positive", "log", "low", "medium", "high",       \
21074    "hosts", "result_hosts", "fp_per_host", "log_per_host", "low_per_host",     \
21075    "medium_per_host", "high_per_host", "duration", "duration_per_host",        \
21076    NULL }
21077 
21078 /**
21079  * @brief Report iterator columns.
21080  */
21081 #define REPORT_ITERATOR_COLUMNS                                              \
21082  {                                                                           \
21083    { "id", NULL, KEYWORD_TYPE_INTEGER },                                     \
21084    { "uuid", NULL, KEYWORD_TYPE_STRING },                                    \
21085    { "iso_time (start_time)", "name", KEYWORD_TYPE_STRING },                 \
21086    { "''", NULL, KEYWORD_TYPE_STRING },                                      \
21087    { "iso_time (start_time)", NULL, KEYWORD_TYPE_STRING },                   \
21088    { "iso_time (end_time)", NULL, KEYWORD_TYPE_STRING },                     \
21089    { "start_time", "created", KEYWORD_TYPE_INTEGER },                        \
21090    { "end_time", "modified", KEYWORD_TYPE_INTEGER },                         \
21091    { "(SELECT name FROM users WHERE users.id = reports.owner)",              \
21092      "_owner",                                                               \
21093      KEYWORD_TYPE_STRING },                                                  \
21094    { "owner", NULL, KEYWORD_TYPE_INTEGER },                                  \
21095    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
21096  }
21097 
21098 /**
21099  * @brief Report iterator columns.
21100  */
21101 #define REPORT_ITERATOR_WHERE_COLUMNS                                        \
21102  {                                                                           \
21103    { "run_status_name (scan_run_status)", "status", KEYWORD_TYPE_STRING },   \
21104    {                                                                         \
21105      "(SELECT uuid FROM tasks WHERE tasks.id = task)",                       \
21106      "task_id",                                                              \
21107      KEYWORD_TYPE_STRING                                                     \
21108    },                                                                        \
21109    { "date", NULL, KEYWORD_TYPE_INTEGER },                                   \
21110    { "(SELECT name FROM tasks WHERE tasks.id = task)", "task" },             \
21111    {                                                                         \
21112      "report_severity (id, opts.override, opts.min_qod)",                    \
21113      "severity",                                                             \
21114      KEYWORD_TYPE_DOUBLE                                                     \
21115    },                                                                        \
21116    {                                                                         \
21117      "report_severity_count (id, opts.override, opts.min_qod,"               \
21118      "                       'False Positive')",                             \
21119      "false_positive",                                                       \
21120      KEYWORD_TYPE_INTEGER                                                    \
21121    },                                                                        \
21122    {                                                                         \
21123      "report_severity_count (id, opts.override, opts.min_qod, 'Log')",       \
21124      "log",                                                                  \
21125      KEYWORD_TYPE_INTEGER                                                    \
21126    },                                                                        \
21127    {                                                                         \
21128      "report_severity_count (id, opts.override, opts.min_qod, 'Low')",       \
21129      "low",                                                                  \
21130      KEYWORD_TYPE_INTEGER                                                    \
21131    },                                                                        \
21132    {                                                                         \
21133      "report_severity_count (id, opts.override, opts.min_qod, 'Medium')",    \
21134      "medium",                                                               \
21135      KEYWORD_TYPE_INTEGER                                                    \
21136    },                                                                        \
21137    {                                                                         \
21138      "report_severity_count (id, opts.override, opts.min_qod, 'High')",      \
21139      "high",                                                                 \
21140      KEYWORD_TYPE_INTEGER                                                    \
21141    },                                                                        \
21142    {                                                                         \
21143      "(SELECT name FROM users WHERE users.id = reports.owner)",              \
21144      "_owner",                                                               \
21145      KEYWORD_TYPE_STRING                                                     \
21146    },                                                                        \
21147    {                                                                         \
21148      "report_host_count (id)",                                               \
21149      "hosts",                                                                \
21150      KEYWORD_TYPE_INTEGER                                                    \
21151    },                                                                        \
21152    {                                                                         \
21153      "report_result_host_count (id, opts.min_qod)",                          \
21154      "result_hosts",                                                         \
21155      KEYWORD_TYPE_INTEGER                                                    \
21156    },                                                                        \
21157    {                                                                         \
21158      "coalesce (report_severity_count (id, opts.override, opts.min_qod,"     \
21159      "                                 'False Positive') * 1.0"              \
21160      "            / nullif (report_result_host_count (id, opts.min_qod), 0),"\
21161      "          0)",                                                         \
21162      "fp_per_host",                                                          \
21163      KEYWORD_TYPE_INTEGER                                                    \
21164    },                                                                        \
21165    {                                                                         \
21166      "coalesce (report_severity_count (id, opts.override, opts.min_qod,"     \
21167      "                                 'Log') * 1.0"                         \
21168      "            / nullif (report_result_host_count (id, opts.min_qod), 0),"\
21169      "          0)",                                                         \
21170      "log_per_host",                                                         \
21171      KEYWORD_TYPE_INTEGER                                                    \
21172    },                                                                        \
21173    {                                                                         \
21174      "coalesce (report_severity_count (id, opts.override, opts.min_qod,"     \
21175      "                                 'Low') * 1.0"                         \
21176      "            / nullif (report_result_host_count (id, opts.min_qod), 0),"\
21177      "          0)",                                                         \
21178      "low_per_host",                                                         \
21179      KEYWORD_TYPE_INTEGER                                                    \
21180    },                                                                        \
21181    {                                                                         \
21182      "coalesce (report_severity_count (id, opts.override, opts.min_qod,"     \
21183      "                                 'Medium') * 1.0"                      \
21184      "            / nullif (report_result_host_count (id, opts.min_qod), 0),"\
21185      "          0)",                                                         \
21186      "medium_per_host",                                                      \
21187      KEYWORD_TYPE_INTEGER                                                    \
21188    },                                                                        \
21189    {                                                                         \
21190      "coalesce (report_severity_count (id, opts.override, opts.min_qod,"     \
21191      "                                 'High') * 1.0"                        \
21192      "            / nullif (report_result_host_count (id, opts.min_qod), 0),"\
21193      "          0)",                                                         \
21194      "high_per_host",                                                        \
21195      KEYWORD_TYPE_INTEGER                                                    \
21196    },                                                                        \
21197    {                                                                         \
21198      "(CASE WHEN (start_time IS NULL or end_time IS NULL)"                   \
21199      " THEN NULL ELSE end_time - start_time END)",                           \
21200      "duration",                                                             \
21201      KEYWORD_TYPE_INTEGER                                                    \
21202    },                                                                        \
21203    {                                                                         \
21204      "(CASE WHEN (start_time IS NULL or end_time IS NULL"                    \
21205      "            or report_result_host_count (id, opts.min_qod) = 0)"       \
21206      " THEN NULL"                                                            \
21207      " ELSE (end_time - start_time)"                                         \
21208      "        / report_result_host_count (id, opts.min_qod) END)",           \
21209      "duration_per_host",                                                    \
21210      KEYWORD_TYPE_INTEGER                                                    \
21211    },                                                                        \
21212    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
21213  }
21214 
21215 /**
21216  * @brief Generate the extra_tables string for a report iterator.
21217  *
21218  * @param[in]  override  Whether to apply overrides.
21219  * @param[in]  min_qod   Minimum QoD of results to count.
21220  *
21221  * @return Newly allocated string with the extra_tables clause.
21222  */
21223 static gchar*
report_iterator_opts_table(int override,int min_qod)21224 report_iterator_opts_table (int override, int min_qod)
21225 {
21226   return g_strdup_printf (", (SELECT"
21227                           "   %d AS override,"
21228                           "   %d AS min_qod)"
21229                           "  AS opts",
21230                           override,
21231                           min_qod);
21232 }
21233 
21234 /**
21235  * @brief Count number of reports.
21236  *
21237  * @param[in]  get  GET params.
21238  *
21239  * @return Total number of reports in filtered set.
21240  */
21241 int
report_count(const get_data_t * get)21242 report_count (const get_data_t *get)
21243 {
21244   static const char *filter_columns[] = REPORT_ITERATOR_FILTER_COLUMNS;
21245   static column_t columns[] = REPORT_ITERATOR_COLUMNS;
21246   static column_t where_columns[] = REPORT_ITERATOR_WHERE_COLUMNS;
21247   gchar *extra_tables;
21248   int ret;
21249 
21250   extra_tables = report_iterator_opts_table (0, MIN_QOD_DEFAULT);
21251 
21252   ret = count2 ("report", get, columns, NULL, where_columns, NULL,
21253                 filter_columns, 0,
21254                 extra_tables,
21255                 get->trash
21256                  ? " AND (SELECT hidden FROM tasks"
21257                    "      WHERE tasks.id = task)"
21258                    "     = 2"
21259                  : " AND (SELECT hidden FROM tasks"
21260                    "      WHERE tasks.id = task)"
21261                    "     = 0",
21262                 NULL,
21263                 TRUE);
21264 
21265   g_free (extra_tables);
21266   return ret;
21267 }
21268 
21269 /**
21270  * @brief Initialise a report iterator, including observed reports.
21271  *
21272  * @param[in]  iterator    Iterator.
21273  * @param[in]  get         GET data.
21274  *
21275  * @return 0 success, 1 failed to find report, 2 failed to find filter,
21276  *         -1 error.
21277  */
21278 int
init_report_iterator(iterator_t * iterator,const get_data_t * get)21279 init_report_iterator (iterator_t* iterator, const get_data_t *get)
21280 {
21281   static const char *filter_columns[] = REPORT_ITERATOR_FILTER_COLUMNS;
21282   static column_t columns[] = REPORT_ITERATOR_COLUMNS;
21283   static column_t where_columns[] = REPORT_ITERATOR_WHERE_COLUMNS;
21284   char *filter;
21285   int overrides, min_qod;
21286   gchar *extra_tables;
21287   int ret;
21288 
21289   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
21290     {
21291       filter = filter_term (get->filt_id);
21292       if (filter == NULL)
21293         return 2;
21294     }
21295   else
21296     filter = NULL;
21297 
21298   overrides = filter_term_apply_overrides (filter ? filter : get->filter);
21299   min_qod = filter_term_min_qod (filter ? filter : get->filter);
21300 
21301   free (filter);
21302 
21303   extra_tables = report_iterator_opts_table (overrides, min_qod);
21304 
21305   ret = init_get_iterator2 (iterator,
21306                             "report",
21307                             get,
21308                             /* Columns. */
21309                             columns,
21310                             NULL,
21311                             /* Filterable columns not in SELECT columns. */
21312                             where_columns,
21313                             NULL,
21314                             filter_columns,
21315                             0,
21316                             extra_tables,
21317                             get->trash
21318                              ? " AND (SELECT hidden FROM tasks"
21319                                "      WHERE tasks.id = task)"
21320                                "     = 2"
21321                              : " AND (SELECT hidden FROM tasks"
21322                                "      WHERE tasks.id = task)"
21323                                "     = 0",
21324                             NULL,
21325                             TRUE,
21326                             FALSE,
21327                             NULL);
21328   g_free (extra_tables);
21329   return ret;
21330 }
21331 
21332 /**
21333  * @brief Initialise a report iterator.
21334  *
21335  * @param[in]  iterator  Iterator.
21336  * @param[in]  task      Task whose reports the iterator loops over.
21337  */
21338 void
init_report_iterator_task(iterator_t * iterator,task_t task)21339 init_report_iterator_task (iterator_t* iterator, task_t task)
21340 {
21341   assert (task);
21342   init_iterator (iterator,
21343                  "SELECT id, uuid FROM reports WHERE task = %llu;",
21344                  task);
21345 }
21346 
21347 /**
21348  * @brief Get the UUID from a report iterator.
21349  *
21350  * @param[in]  iterator  Iterator.
21351  *
21352  * @return UUID, or NULL if iteration is complete.  Freed by
21353  *         cleanup_iterator.
21354  */
21355 DEF_ACCESS (report_iterator_uuid, 1);
21356 
21357 /**
21358  * @brief Read the next report from an iterator.
21359  *
21360  * @param[in]   iterator  Task iterator.
21361  * @param[out]  report    Report.
21362  *
21363  * @return TRUE if there was a next task, else FALSE.
21364  */
21365 gboolean
next_report(iterator_t * iterator,report_t * report)21366 next_report (iterator_t* iterator, report_t* report)
21367 {
21368   if (next (iterator))
21369     {
21370       *report = iterator_int64 (iterator, 0);
21371       return TRUE;
21372     }
21373   return FALSE;
21374 }
21375 
21376 /**
21377  * @brief Return SQL WHERE for restricting a SELECT to levels.
21378  *
21379  * @param[in]  levels  String describing threat levels (message types)
21380  *                     to include in report (for example, "hmlg" for
21381  *                     High, Medium, Low and loG).  All levels if NULL.
21382  * @param[in]  new_severity_sql  SQL for new severity.
21383  *
21384  * @return WHERE clause for levels if one is required, else NULL.
21385  */
21386 static GString *
where_levels_auto(const char * levels,const char * new_severity_sql)21387 where_levels_auto (const char *levels, const char *new_severity_sql)
21388 {
21389   int count;
21390   GString *levels_sql;
21391 
21392   /* Generate SQL for constraints on message type, according to levels. */
21393 
21394   levels_sql = g_string_new ("");
21395 
21396   if (levels == NULL || strlen (levels) == 0)
21397     {
21398       g_string_append_printf (levels_sql,
21399                               " AND %s != " G_STRINGIFY (SEVERITY_ERROR),
21400                               new_severity_sql);
21401       return levels_sql;
21402     }
21403 
21404   count = 0;
21405 
21406   g_string_append_printf (levels_sql, " AND severity_in_levels (%s", new_severity_sql);
21407 
21408   if (strchr (levels, 'h'))
21409     {
21410       g_string_append (levels_sql, ", 'high'");
21411       count++;
21412     }
21413   if (strchr (levels, 'm'))
21414     {
21415       g_string_append (levels_sql, ", 'medium'");
21416       count++;
21417     }
21418   if (strchr (levels, 'l'))
21419     {
21420       g_string_append (levels_sql, ", 'low'");
21421       count++;
21422     }
21423   if (strchr (levels, 'g'))
21424     {
21425       g_string_append (levels_sql, ", 'log'");
21426       count++;
21427     }
21428   if (strchr (levels, 'f'))
21429     {
21430       g_string_append (levels_sql, ", 'false'");
21431       count++;
21432     }
21433 
21434   if (count == 0)
21435     {
21436       g_string_free (levels_sql, TRUE);
21437       return NULL;
21438     }
21439 
21440   g_string_append (levels_sql, ")");
21441 
21442   if (count == 5)
21443     {
21444       /* All levels. */
21445       g_string_free (levels_sql, TRUE);
21446       levels_sql = g_string_new ("");
21447       /* It's not possible to override from or to the error severity, so no
21448        * need to use the overridden severity here (new_severity_sql).  This
21449        * helps with the default result counting performance because the
21450        * overridden severity is complex. */
21451       g_string_append_printf (levels_sql,
21452                               " AND severity != " G_STRINGIFY (SEVERITY_ERROR));
21453     }
21454 
21455   return levels_sql;
21456 }
21457 
21458 /**
21459  * @brief Return SQL WHERE for restricting a SELECT to a minimum QoD.
21460  *
21461  * @param[in]  min_qod  Minimum value for QoD.
21462  *
21463  * @return WHERE clause if one is required, else an empty string.
21464  */
21465 static gchar*
where_qod(int min_qod)21466 where_qod (int min_qod)
21467 {
21468   gchar *qod_sql;
21469 
21470   if (min_qod <= 0)
21471     qod_sql = g_strdup ("");
21472   else
21473     qod_sql = g_strdup_printf (" AND (results.qod >= CAST (%d AS INTEGER))",
21474                                min_qod);
21475 
21476   return qod_sql;
21477 }
21478 
21479 /**
21480  * @brief Filter columns for result iterator.
21481  */
21482 #define RESULT_ITERATOR_FILTER_COLUMNS                                        \
21483   { GET_ITERATOR_FILTER_COLUMNS, "host", "location", "nvt",                   \
21484     "type", "original_type",                                                  \
21485     "description", "task", "report", "cvss_base", "nvt_version",              \
21486     "severity", "original_severity", "vulnerability", "date", "report_id",    \
21487     "solution_type", "qod", "qod_type", "task_id", "cve", "hostname",         \
21488     "path", NULL }
21489 
21490 // TODO Combine with RESULT_ITERATOR_COLUMNS.
21491 /**
21492  * @brief Result iterator filterable columns, for severity only version .
21493  */
21494 #define BASE_RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE                      \
21495     { "id", NULL, KEYWORD_TYPE_INTEGER },                                     \
21496     { "uuid", NULL, KEYWORD_TYPE_STRING },                                    \
21497     { "(SELECT name FROM nvts WHERE nvts.oid =  nvt)",                        \
21498       "name",                                                                 \
21499       KEYWORD_TYPE_STRING },                                                  \
21500     { "''", "comment", KEYWORD_TYPE_STRING },                                 \
21501     { " iso_time (date, opts.user_zone)",                                     \
21502       "creation_time",                                                        \
21503       KEYWORD_TYPE_STRING },                                                  \
21504     { " iso_time (date, opts.user_zone)",                                     \
21505       "modification_time",                                                    \
21506       KEYWORD_TYPE_STRING },                                                  \
21507     { "date", "created", KEYWORD_TYPE_INTEGER },                              \
21508     { "date", "modified", KEYWORD_TYPE_INTEGER },                             \
21509     { "(SELECT name FROM users WHERE users.id = results.owner)",              \
21510       "_owner",                                                               \
21511       KEYWORD_TYPE_STRING },                                                  \
21512     { "owner", NULL, KEYWORD_TYPE_INTEGER },                                  \
21513     /* Result specific columns. */                                            \
21514     { "host", NULL, KEYWORD_TYPE_STRING },                                    \
21515     { "port", "location", KEYWORD_TYPE_STRING },                              \
21516     { "nvt", NULL, KEYWORD_TYPE_STRING },                                     \
21517     { "severity_to_type (severity)", "original_type", KEYWORD_TYPE_STRING },  \
21518     { "'Log Message'", /* Adjusted by init_result_get_iterator_severity. */   \
21519       "type",                                                                 \
21520       KEYWORD_TYPE_STRING },                                                  \
21521     { "description", NULL, KEYWORD_TYPE_STRING },                             \
21522     { "task", NULL, KEYWORD_TYPE_INTEGER },                                   \
21523     { "report", "report_rowid", KEYWORD_TYPE_INTEGER },                       \
21524     { "(SELECT cvss_base FROM nvts WHERE nvts.oid =  nvt)",                   \
21525       "cvss_base",                                                            \
21526       KEYWORD_TYPE_DOUBLE },                                                  \
21527     { "nvt_version", NULL, KEYWORD_TYPE_STRING },                             \
21528     { "severity", "original_severity", KEYWORD_TYPE_DOUBLE },                 \
21529     { "(SELECT name FROM nvts WHERE nvts.oid =  nvt)",                        \
21530       "vulnerability",                                                        \
21531       KEYWORD_TYPE_STRING },                                                  \
21532     { "date" , NULL, KEYWORD_TYPE_INTEGER },                                  \
21533     { "(SELECT uuid FROM reports WHERE id = report)",                         \
21534       "report_id",                                                            \
21535       KEYWORD_TYPE_STRING },                                                  \
21536     { "(SELECT solution_type FROM nvts WHERE nvts.oid = nvt)",                \
21537       "solution_type",                                                        \
21538       KEYWORD_TYPE_STRING },                                                  \
21539     { "qod", NULL, KEYWORD_TYPE_INTEGER },                                    \
21540     { "qod_type", NULL, KEYWORD_TYPE_STRING },                                \
21541     { "(CASE WHEN (hostname IS NULL) OR (hostname = '')"                      \
21542       " THEN (SELECT value FROM report_host_details"                          \
21543       "       WHERE name = 'hostname'"                                        \
21544       "         AND report_host = (SELECT id FROM report_hosts"               \
21545       "                            WHERE report_hosts.host=results.host"      \
21546       "                            AND report_hosts.report = results.report)" \
21547       "       LIMIT 1)"                                                       \
21548       " ELSE hostname"                                                        \
21549       " END)",                                                                \
21550       "hostname",                                                             \
21551       KEYWORD_TYPE_STRING                                                     \
21552     },                                                                        \
21553     { "(SELECT uuid FROM tasks WHERE id = task)",                             \
21554       "task_id",                                                              \
21555       KEYWORD_TYPE_STRING },                                                  \
21556     { "(SELECT cve FROM nvts WHERE oid = nvt)", "cve", KEYWORD_TYPE_STRING }, \
21557     { "path",                                                                 \
21558       NULL,                                                                   \
21559       KEYWORD_TYPE_STRING },                                                  \
21560     { "(SELECT CASE WHEN host IS NULL"                                        \
21561       "             THEN NULL"                                                \
21562       "             ELSE (SELECT uuid FROM hosts"                             \
21563       "                   WHERE id = (SELECT host FROM host_identifiers"      \
21564       "                               WHERE source_type = 'Report Host'"      \
21565       "                               AND name = 'ip'"                        \
21566       "                               AND source_id"                          \
21567       "                                   = (SELECT uuid"                     \
21568       "                                      FROM reports"                    \
21569       "                                      WHERE id = results.report)"      \
21570       "                               AND value = results.host"               \
21571       "                               LIMIT 1))"                              \
21572       "             END)",                                                    \
21573       NULL,                                                                   \
21574       KEYWORD_TYPE_STRING },                                                  \
21575     { "(SELECT CASE"                                                          \
21576       "        WHEN EXISTS (SELECT * FROM notes"                              \
21577       "                     WHERE (result = results.id"                       \
21578       "                            OR (result = 0 AND nvt = results.nvt))"    \
21579       "                     AND (task = 0 OR task = results.task))"           \
21580       "        THEN 1"                                                        \
21581       "        ELSE 0"                                                        \
21582       "        END)",                                                         \
21583       NULL,                                                                   \
21584       KEYWORD_TYPE_INTEGER },                                                 \
21585     { "(SELECT CASE"                                                          \
21586       "        WHEN EXISTS (SELECT * FROM overrides"                          \
21587       "                     WHERE (result = results.id"                       \
21588       "                            OR (result = 0 AND nvt = results.nvt))"    \
21589       "                     AND (task = 0 OR task = results.task))"           \
21590       "        THEN 1"                                                        \
21591       "        ELSE 0"                                                        \
21592       "        END)",                                                         \
21593       NULL,                                                                   \
21594       KEYWORD_TYPE_INTEGER },                                                 \
21595     { TICKET_SQL_RESULT_MAY_HAVE_TICKETS,                                     \
21596       NULL,                                                                   \
21597       KEYWORD_TYPE_INTEGER },                                                 \
21598     { "(SELECT name FROM tasks WHERE tasks.id = task)",                       \
21599       "task",                                                                 \
21600       KEYWORD_TYPE_STRING },
21601 
21602 /**
21603  * @brief Result iterator columns.
21604  */
21605 #define RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE                           \
21606   {                                                                           \
21607     BASE_RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE                          \
21608     { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
21609   }
21610 
21611 /**
21612  * @brief Result iterator columns, when CERT db is not loaded.
21613  */
21614 #define RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE_NO_CERT                   \
21615   {                                                                           \
21616     BASE_RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE                          \
21617     { "0",                                                                    \
21618       NULL,                                                                   \
21619       KEYWORD_TYPE_INTEGER },                                                 \
21620     { "0",                                                                    \
21621       NULL,                                                                   \
21622       KEYWORD_TYPE_INTEGER },                                                 \
21623     { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
21624   }
21625 
21626 /**
21627  * @brief Result iterator columns.
21628  */
21629 #define PRE_BASE_RESULT_ITERATOR_COLUMNS(new_severity_sql)                    \
21630     { "results.id", NULL, KEYWORD_TYPE_INTEGER },                             \
21631     /* ^ 0 */                                                                 \
21632     { "results.uuid", NULL, KEYWORD_TYPE_STRING },                            \
21633     { "nvts.name",                                                            \
21634       "name",                                                                 \
21635       KEYWORD_TYPE_STRING },                                                  \
21636     { "''", "comment", KEYWORD_TYPE_STRING },                                 \
21637     { " iso_time (date, opts.user_zone)",                                     \
21638       "creation_time",                                                        \
21639       KEYWORD_TYPE_STRING },                                                  \
21640     { " iso_time (date, opts.user_zone)",                                     \
21641       "modification_time",                                                    \
21642       KEYWORD_TYPE_STRING },                                                  \
21643     { "date", "created", KEYWORD_TYPE_INTEGER },                              \
21644     { "date", "modified", KEYWORD_TYPE_INTEGER },                             \
21645     { "(SELECT name FROM users WHERE users.id = results.owner)",              \
21646       "_owner",                                                               \
21647       KEYWORD_TYPE_STRING },                                                  \
21648     { "results.owner", NULL, KEYWORD_TYPE_INTEGER },                          \
21649     /* ^ 9 */                                                                 \
21650     /* Result specific columns. */                                            \
21651     { "host", NULL, KEYWORD_TYPE_STRING },                                    \
21652     /* ^ 10 = 0 */                                                            \
21653     { "port", "location", KEYWORD_TYPE_STRING },                              \
21654     { "nvt", NULL, KEYWORD_TYPE_STRING },                                     \
21655     { "severity_to_type (results.severity)",                                  \
21656       "original_type",                                                        \
21657       KEYWORD_TYPE_STRING },                                                  \
21658     { "severity_to_type (" new_severity_sql ")",                              \
21659       "type",                                                                 \
21660       KEYWORD_TYPE_STRING },                                                  \
21661     { "description", NULL, KEYWORD_TYPE_STRING },                             \
21662     { "task", NULL, KEYWORD_TYPE_INTEGER },                                   \
21663     { "report", "report_rowid", KEYWORD_TYPE_INTEGER },                       \
21664     { "nvts.cvss_base",                                                       \
21665       "cvss_base",                                                            \
21666       KEYWORD_TYPE_DOUBLE },                                                  \
21667     { "nvt_version", NULL, KEYWORD_TYPE_STRING },                             \
21668     { "results.severity", "original_severity", KEYWORD_TYPE_DOUBLE },         \
21669     /* ^ 20 = 10 */                                                           \
21670     { new_severity_sql,                                                       \
21671       "severity",                                                             \
21672       KEYWORD_TYPE_DOUBLE },                                                  \
21673     { "nvts.name",                                                            \
21674       "vulnerability",                                                        \
21675       KEYWORD_TYPE_STRING },                                                  \
21676     { "date" , NULL, KEYWORD_TYPE_INTEGER },                                  \
21677     { "(SELECT uuid FROM reports WHERE id = report)",                         \
21678       "report_id",                                                            \
21679       KEYWORD_TYPE_STRING },                                                  \
21680     { "nvts.solution_type",                                                   \
21681       "solution_type",                                                        \
21682       KEYWORD_TYPE_STRING },                                                  \
21683     { "results.qod", "qod", KEYWORD_TYPE_INTEGER },                           \
21684     { "results.qod_type", NULL, KEYWORD_TYPE_STRING },                        \
21685     { "(CASE WHEN (hostname IS NULL) OR (hostname = '')"                      \
21686       " THEN (SELECT value FROM report_host_details"                          \
21687       "       WHERE name = 'hostname'"                                        \
21688       "         AND report_host = (SELECT id FROM report_hosts"               \
21689       "                            WHERE report_hosts.host=results.host"      \
21690       "                            AND report_hosts.report = results.report)" \
21691       "       LIMIT 1)"                                                       \
21692       " ELSE hostname"                                                        \
21693       " END)",                                                                \
21694       "hostname",                                                             \
21695       KEYWORD_TYPE_STRING                                                     \
21696     },                                                                        \
21697     { "(SELECT uuid FROM tasks WHERE id = task)",                             \
21698       "task_id",                                                              \
21699       KEYWORD_TYPE_STRING },                                                  \
21700     { "nvts.cve", "cve", KEYWORD_TYPE_STRING },                               \
21701     /* ^ 30 = 20 */                                                           \
21702     { "path",                                                                 \
21703       NULL,                                                                   \
21704       KEYWORD_TYPE_STRING },                                                  \
21705     { "(SELECT CASE WHEN host IS NULL"                                        \
21706       "             THEN NULL"                                                \
21707       "             ELSE (SELECT uuid FROM hosts"                             \
21708       "                   WHERE id = (SELECT host FROM host_identifiers"      \
21709       "                               WHERE source_type = 'Report Host'"      \
21710       "                               AND name = 'ip'"                        \
21711       "                               AND source_id"                          \
21712       "                                   = (SELECT uuid"                     \
21713       "                                      FROM reports"                    \
21714       "                                      WHERE id = results.report)"      \
21715       "                               AND value = results.host"               \
21716       "                               LIMIT 1))"                              \
21717       "             END)",                                                    \
21718       NULL,                                                                   \
21719       KEYWORD_TYPE_STRING },                                                  \
21720     { "(SELECT CASE"                                                          \
21721       "        WHEN EXISTS (SELECT * FROM notes"                              \
21722       "                     WHERE (result = results.id"                       \
21723       "                            OR (result = 0 AND nvt = results.nvt))"    \
21724       "                     AND (task = 0 OR task = results.task))"           \
21725       "        THEN 1"                                                        \
21726       "        ELSE 0"                                                        \
21727       "        END)",                                                         \
21728       NULL,                                                                   \
21729       KEYWORD_TYPE_INTEGER },                                                 \
21730     { "(SELECT CASE"                                                          \
21731       "        WHEN EXISTS (SELECT * FROM overrides"                          \
21732       "                     WHERE (result = results.id"                       \
21733       "                            OR (result = 0 AND nvt = results.nvt))"    \
21734       "                     AND (task = 0 OR task = results.task))"           \
21735       "        THEN 1"                                                        \
21736       "        ELSE 0"                                                        \
21737       "        END)",                                                         \
21738       NULL,                                                                   \
21739       KEYWORD_TYPE_INTEGER },                                                 \
21740     { TICKET_SQL_RESULT_MAY_HAVE_TICKETS,                                     \
21741       NULL,                                                                   \
21742       KEYWORD_TYPE_INTEGER },                                                 \
21743     /* ^ 35 = 25 */                                                           \
21744     { "(SELECT name FROM tasks WHERE tasks.id = task)",                       \
21745       "task",                                                                 \
21746       KEYWORD_TYPE_STRING },                                                  \
21747     { "nvts.summary",                                                         \
21748       NULL,                                                                   \
21749       KEYWORD_TYPE_STRING },                                                  \
21750     { "nvts.insight",                                                         \
21751       NULL,                                                                   \
21752       KEYWORD_TYPE_STRING },                                                  \
21753     { "nvts.affected",                                                        \
21754       NULL,                                                                   \
21755       KEYWORD_TYPE_STRING },                                                  \
21756     { "nvts.impact",                                                          \
21757       NULL,                                                                   \
21758       KEYWORD_TYPE_STRING },                                                  \
21759     /* ^ 40 = 30 */                                                           \
21760     { "nvts.solution",                                                        \
21761       NULL,                                                                   \
21762       KEYWORD_TYPE_STRING },                                                  \
21763     { "nvts.detection",                                                       \
21764       NULL,                                                                   \
21765       KEYWORD_TYPE_STRING },                                                  \
21766     { "nvts.family",                                                          \
21767       NULL,                                                                   \
21768       KEYWORD_TYPE_STRING },                                                  \
21769     { "nvts.tag",                                                             \
21770       NULL,                                                                   \
21771       KEYWORD_TYPE_STRING },                                                  \
21772 
21773 /**
21774  * @brief Result iterator columns.
21775  */
21776 #define BASE_RESULT_ITERATOR_COLUMNS                                          \
21777   PRE_BASE_RESULT_ITERATOR_COLUMNS("lateral_new_severity.new_severity")
21778 
21779 /**
21780  * @brief Result iterator columns.
21781  */
21782 #define RESULT_ITERATOR_COLUMNS                                               \
21783   {                                                                           \
21784     BASE_RESULT_ITERATOR_COLUMNS                                              \
21785     { SECINFO_SQL_RESULT_CERT_BUNDS,                                          \
21786       NULL,                                                                   \
21787       KEYWORD_TYPE_INTEGER },                                                 \
21788     { SECINFO_SQL_RESULT_DFN_CERTS,                                           \
21789       NULL,                                                                   \
21790       KEYWORD_TYPE_INTEGER },                                                 \
21791     { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
21792   }
21793 
21794 /**
21795  * @brief Result iterator columns, when CERT db is not loaded.
21796  */
21797 #define RESULT_ITERATOR_COLUMNS_NO_CERT                                       \
21798   {                                                                           \
21799     BASE_RESULT_ITERATOR_COLUMNS                                              \
21800     { "0",                                                                    \
21801       NULL,                                                                   \
21802       KEYWORD_TYPE_INTEGER },                                                 \
21803     { "0",                                                                    \
21804       NULL,                                                                   \
21805       KEYWORD_TYPE_INTEGER },                                                 \
21806     { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
21807   }
21808 
21809 /**
21810  * @brief Generate the extra_tables string for a result iterator.
21811  *
21812  * @param[in]  override  Whether to apply overrides.
21813  * @param[in]  dynamic   Whether to use dynamic severity scores.
21814  *
21815  * @return Newly allocated string with the extra_tables clause.
21816  */
21817 static gchar*
result_iterator_opts_table(int override,int dynamic)21818 result_iterator_opts_table (int override, int dynamic)
21819 {
21820   user_t user_id;
21821   gchar *user_zone, *quoted_user_zone, *ret;
21822 
21823   if (current_credentials.uuid)
21824     {
21825       user_id = sql_int64_0 ("SELECT id FROM users WHERE uuid = '%s';",
21826                              current_credentials.uuid);
21827       if (user_id > 0)
21828         user_zone = sql_string ("SELECT"
21829                                 " coalesce ((SELECT current_setting"
21830                                 "                    ('gvmd.tz_override')),"
21831                                 "           (SELECT timezone FROM users"
21832                                 "            WHERE id = %llu));",
21833                                 user_id);
21834       else
21835         user_zone = g_strdup ("UTC");
21836     }
21837   else
21838     {
21839       user_id = 0;
21840       user_zone = sql_string ("SELECT"
21841                               " coalesce ((SELECT current_setting"
21842                               "                    ('gvmd.tz_override')),"
21843                               "           'UTC');");
21844     }
21845 
21846   quoted_user_zone = sql_quote ("user_zone");
21847   g_free (user_zone);
21848 
21849   ret = g_strdup_printf
21850          (", (SELECT"
21851           "   '%s'::text AS user_zone,"
21852           "   %llu AS user_id,"
21853           "   %d AS override,"
21854           "   %d AS dynamic) AS opts",
21855           quoted_user_zone,
21856           user_id,
21857           override,
21858           dynamic);
21859 
21860   g_free (quoted_user_zone);
21861 
21862   return ret;
21863 }
21864 
21865 /**
21866  * @brief Get new severity clause.
21867  *
21868  * @param[in]  apply_overrides  Whether to apply overrides.
21869  * @param[in]  dynamic_severity Whether to use dynamic severity.
21870  *
21871  * @return Newly allocated clause.
21872  */
21873 static gchar*
new_severity_clause(int apply_overrides,int dynamic_severity)21874 new_severity_clause (int apply_overrides, int dynamic_severity)
21875 {
21876   if (apply_overrides)
21877     {
21878       if (dynamic_severity)
21879         /* Overrides, dynamic. */
21880         return g_strdup_printf ("(SELECT new_severity FROM result_new_severities_dynamic"
21881                                 " WHERE result_new_severities_dynamic.result = results.id"
21882                                 " AND result_new_severities_dynamic.user"
21883                                 "     = (SELECT id FROM users WHERE uuid = '%s')"
21884                                 " LIMIT 1)",
21885                                 current_credentials.uuid);
21886 
21887       /* Overrides, no dynamic. */
21888       return g_strdup_printf ("(SELECT new_severity FROM result_new_severities_static"
21889                               " WHERE result_new_severities_static.result = results.id"
21890                               " AND result_new_severities_static.user"
21891                               "     = (SELECT id FROM users WHERE uuid = '%s')"
21892                               " LIMIT 1)",
21893                               current_credentials.uuid);
21894     }
21895 
21896   if (dynamic_severity)
21897     /* Dynamic, no overrides. */
21898     return g_strdup ("current_severity (results.severity,"
21899                      "                  results.nvt)");
21900 
21901   /* No dynamic, no overrides. */
21902   return g_strdup ("results.severity");
21903 }
21904 
21905 /**
21906  * @brief Get extra_where string for a result iterator or count.
21907  *
21908  * @param[in]  trash            Whether to get results from trashcan.
21909  * @param[in]  report           Report to restrict returned results to.
21910  * @param[in]  host             Host to restrict returned results to.
21911  * @param[in]  apply_overrides  Whether to apply overrides.
21912  * @param[in]  dynamic_severity Whether to use dynamic severity.
21913  * @param[in]  filter           Filter string.
21914  * @param[in]  given_new_severity_sql  SQL for new severity, or NULL.
21915  *
21916  * @return     Newly allocated extra_where string.
21917  */
21918 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)21919 results_extra_where (int trash, report_t report, const gchar* host,
21920                      int apply_overrides, int dynamic_severity,
21921                      const gchar *filter, const gchar *given_new_severity_sql)
21922 {
21923   gchar *extra_where;
21924   int min_qod;
21925   gchar *levels;
21926   gchar *report_clause, *host_clause, *min_qod_clause;
21927   GString *levels_clause;
21928   gchar *new_severity_sql;
21929 
21930   // Get filter values
21931   min_qod = filter_term_min_qod (filter);
21932   levels = filter_term_value (filter, "levels");
21933   if (levels == NULL)
21934     levels = g_strdup ("hmlgdf");
21935 
21936   // Build clause fragments
21937 
21938   if (given_new_severity_sql)
21939     new_severity_sql = NULL;
21940   else
21941     new_severity_sql = new_severity_clause (apply_overrides, dynamic_severity);
21942 
21943   // Build filter clauses
21944 
21945   report_clause = report ? g_strdup_printf (" AND (report = %llu) ", report)
21946                          : NULL;
21947 
21948   if (host)
21949     {
21950       gchar *quoted_host = sql_quote (host);
21951       host_clause = g_strdup_printf (" AND (host = '%s') ", quoted_host);
21952       g_free (quoted_host);
21953     }
21954   else
21955     host_clause = NULL;
21956 
21957   min_qod_clause = where_qod (min_qod);
21958 
21959   levels_clause = where_levels_auto (levels ? levels : "hmlgdf",
21960                                      given_new_severity_sql
21961                                       ? given_new_severity_sql
21962                                       : new_severity_sql);
21963   g_free (levels);
21964   g_free (new_severity_sql);
21965 
21966   extra_where = g_strdup_printf("%s%s%s%s",
21967                                 report_clause ? report_clause : "",
21968                                 host_clause ? host_clause : "",
21969                                 levels_clause->str,
21970                                 min_qod_clause ? min_qod_clause : "");
21971 
21972   g_free (min_qod_clause);
21973   g_string_free (levels_clause, TRUE);
21974   g_free (report_clause);
21975   g_free (host_clause);
21976 
21977   return extra_where;
21978 }
21979 
21980 /**
21981  * @brief Initialise the severity-only result iterator.
21982  *
21983  * @param[in]  iterator    Iterator.
21984  * @param[in]  get         GET data.
21985  * @param[in]  report      Report to restrict returned results to.
21986  * @param[in]  host        Host to limit results to.
21987  * @param[in]  extra_order Extra text for ORDER term in SQL.
21988  *
21989  * @return 0 success, 1 failed to find result, 2 failed to find filter (filt_id),
21990  *         -1 error.
21991  */
21992 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)21993 init_result_get_iterator_severity (iterator_t* iterator, const get_data_t *get,
21994                                    report_t report, const char* host,
21995                                    const gchar *extra_order)
21996 {
21997   column_t columns[2];
21998   static column_t static_filterable_columns[]
21999     = RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE;
22000   static column_t static_filterable_columns_no_cert[]
22001     = RESULT_ITERATOR_COLUMNS_SEVERITY_FILTERABLE_NO_CERT;
22002   static const char *filter_columns[] = RESULT_ITERATOR_FILTER_COLUMNS;
22003   column_t *filterable_columns;
22004   int ret;
22005   gchar *filter;
22006   int apply_overrides, dynamic_severity;
22007   gchar *extra_tables, *extra_where, *extra_where_single, *opts, *with_clause;
22008   const gchar *lateral;
22009 
22010   assert (report);
22011 
22012   dynamic_severity = setting_dynamic_severity_int ();
22013 
22014   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
22015     {
22016       filter = filter_term (get->filt_id);
22017       if (filter == NULL)
22018         return 2;
22019     }
22020   else
22021     filter = NULL;
22022 
22023   apply_overrides
22024     = filter_term_apply_overrides (filter ? filter : get->filter);
22025 
22026   if (manage_cert_loaded ())
22027     filterable_columns = column_array_copy (static_filterable_columns);
22028   else
22029     filterable_columns = column_array_copy (static_filterable_columns_no_cert);
22030   column_array_set
22031    (filterable_columns,
22032     "type",
22033     apply_overrides
22034      ? (dynamic_severity
22035         /* Overrides, dynamic. */
22036         ? g_strdup_printf ("severity_to_type"
22037                            " ((SELECT new_severity FROM result_new_severities_dynamic"
22038                            "   WHERE result_new_severities_dynamic.result = results.id"
22039                            "   AND result_new_severities_dynamic.user = opts.user_id"
22040                            "   LIMIT 1))")
22041         /* Overrides, no dynamic. */
22042         : g_strdup_printf ("severity_to_type"
22043                            " ((SELECT new_severity FROM result_new_severities_static"
22044                            "   WHERE result_new_severities_static.result = results.id"
22045                            "   AND result_new_severities_static.user = opts.user_id"
22046                            "   LIMIT 1))"))
22047      : (dynamic_severity
22048          /* Dynamic, no overrides. */
22049          ? g_strdup ("severity_to_type (current_severity (results.severity,"
22050                      "                                    results.nvt))")
22051          /* No dynamic, no overrides. */
22052          : g_strdup ("severity_to_type (results.severity)")));
22053 
22054   if (dynamic_severity)
22055     {
22056       if (apply_overrides)
22057         lateral
22058           = "coalesce ((SELECT new_severity FROM valid_overrides"
22059             "           WHERE valid_overrides.result_nvt"
22060             "                 = results.result_nvt"
22061             "           AND (valid_overrides.result = 0"
22062             "                OR valid_overrides.result"
22063             "                   = results.id)"
22064             "           AND (valid_overrides.hosts is NULL"
22065             "                OR valid_overrides.hosts = ''"
22066             "                OR hosts_contains"
22067             "                    (valid_overrides.hosts,"
22068             "                     results.host))"
22069             "           AND (valid_overrides.port is NULL"
22070             "                OR valid_overrides.port = ''"
22071             "                OR valid_overrides.port"
22072             "                   = results.port)"
22073             "           AND severity_matches_ov"
22074             "                (coalesce"
22075             "                  ((CASE WHEN results.severity"
22076             "                              > " G_STRINGIFY
22077                                                              (SEVERITY_LOG)
22078             "                    THEN CAST (nvts.cvss_base"
22079             "                               AS double precision)"
22080             "                    ELSE results.severity"
22081             "                    END),"
22082             "                   results.severity),"
22083             "                 valid_overrides.severity)"
22084             "           LIMIT 1),"
22085             "          coalesce ((CASE WHEN results.severity"
22086             "                               > " G_STRINGIFY
22087                                                               (SEVERITY_LOG)
22088             "                     THEN CAST (nvts.cvss_base"
22089             "                                AS double precision)"
22090             "                     ELSE results.severity"
22091             "                     END),"
22092             "                    results.severity))";
22093       else
22094         lateral
22095           = "coalesce ((CASE WHEN results.severity"
22096             "                     > " G_STRINGIFY (SEVERITY_LOG)
22097             "                THEN CAST (nvts.cvss_base"
22098             "                           AS double precision)"
22099             "                ELSE results.severity"
22100             "                END),"
22101             "          results.severity)";
22102     }
22103   else
22104     {
22105       if (apply_overrides)
22106         lateral
22107           = "coalesce ((SELECT new_severity FROM valid_overrides"
22108             "           WHERE valid_overrides.result_nvt"
22109             "                 = results.result_nvt"
22110             "           AND (valid_overrides.result = 0"
22111             "                OR valid_overrides.result"
22112             "                   = results.id)"
22113             "           AND (valid_overrides.hosts is NULL"
22114             "                OR valid_overrides.hosts = ''"
22115             "                OR hosts_contains"
22116             "                    (valid_overrides.hosts,"
22117             "                     results.host))"
22118             "           AND (valid_overrides.port is NULL"
22119             "                OR valid_overrides.port = ''"
22120             "                OR valid_overrides.port"
22121             "                   = results.port)"
22122             "           AND severity_matches_ov"
22123             "                (results.severity,"
22124             "                 valid_overrides.severity)"
22125             "           LIMIT 1),"
22126             "          results.severity)";
22127       else
22128         lateral
22129           /* coalesce because results.severity gives syntax error. */
22130           = "coalesce (results.severity, results.severity)";
22131     }
22132 
22133   columns[0].select = "lateral_severity";
22134   columns[0].filter = "severity";
22135   columns[0].type = KEYWORD_TYPE_DOUBLE;
22136 
22137   columns[1].select = NULL;
22138   columns[1].filter = NULL;
22139   columns[1].type = KEYWORD_TYPE_UNKNOWN;
22140 
22141   opts = result_iterator_opts_table (apply_overrides,
22142                                      dynamic_severity);
22143   if (dynamic_severity)
22144     extra_tables = g_strdup_printf (" LEFT OUTER JOIN nvts"
22145                                     " ON results.nvt = nvts.oid,"
22146                                     " LATERAL %s AS lateral_severity%s",
22147                                     lateral, opts);
22148   else
22149     extra_tables = g_strdup_printf (", LATERAL %s AS lateral_severity%s",
22150                                     lateral, opts);
22151   g_free (opts);
22152 
22153   extra_where = results_extra_where (get->trash, report, host,
22154                                      apply_overrides, dynamic_severity,
22155                                      filter ? filter : get->filter,
22156                                      "lateral_severity");
22157 
22158   extra_where_single = results_extra_where (get->trash, report, host,
22159                                             apply_overrides,
22160                                             dynamic_severity,
22161                                             "min_qod=0",
22162                                             "lateral_severity");
22163 
22164   free (filter);
22165 
22166   if (apply_overrides)
22167     {
22168       gchar *owned_clause, *overrides_with;
22169       char *user_id;
22170 
22171       user_id = sql_string ("SELECT id FROM users WHERE uuid = '%s';",
22172                             current_credentials.uuid);
22173       // Do not get ACL with_clause as it will be added by
22174       // init_get_iterator2_with.
22175       owned_clause = acl_where_owned_for_get ("override", user_id,
22176                                               "valid_overrides_",
22177                                               &overrides_with);
22178       free (user_id);
22179 
22180       with_clause = g_strdup_printf
22181                       (" %s,"
22182                        " valid_overrides"
22183                        " AS (SELECT result_nvt, hosts, new_severity, port,"
22184                        "            severity, result"
22185                        "     FROM overrides"
22186                        "     WHERE %s"
22187                        /*    Only use if override's NVT is in report. */
22188                        "     AND EXISTS (SELECT * FROM result_nvt_reports"
22189                        "                 WHERE report = %llu"
22190                        "                 AND result_nvt"
22191                        "                     = overrides.result_nvt)"
22192                        "     AND (task = 0"
22193                        "          OR task = (SELECT reports.task"
22194                        "                     FROM reports"
22195                        "                     WHERE reports.id = %llu))"
22196                        "     AND ((end_time = 0) OR (end_time >= m_now ()))"
22197                        "     ORDER BY result DESC, task DESC, port DESC,"
22198                        "              severity ASC, creation_time DESC)"
22199                        " ",
22200                        overrides_with + strlen ("WITH "),
22201                        owned_clause,
22202                        report,
22203                        report);
22204       g_free (overrides_with);
22205       g_free (owned_clause);
22206     }
22207   else
22208     with_clause = NULL;
22209 
22210   table_order_if_sort_not_specified = 1;
22211   ret = init_get_iterator2_with (iterator,
22212                                  "result",
22213                                  get,
22214                                  /* SELECT columns. */
22215                                  columns,
22216                                  NULL,
22217                                  /* Filterable columns not in SELECT columns. */
22218                                  filterable_columns,
22219                                  NULL,
22220                                  filter_columns,
22221                                  0,
22222                                  extra_tables,
22223                                  extra_where,
22224                                  extra_where_single,
22225                                  TRUE,
22226                                  report ? TRUE : FALSE,
22227                                  extra_order,
22228                                  with_clause,
22229                                  1,
22230                                  1);
22231   table_order_if_sort_not_specified = 0;
22232   column_array_free (filterable_columns);
22233   g_free (with_clause);
22234   g_free (extra_tables);
22235   g_free (extra_where);
22236   g_free (extra_where_single);
22237   return ret;
22238 }
22239 
22240 /**
22241  * @brief SQL for getting current severity.
22242  */
22243 #define CURRENT_SEVERITY_SQL                                            \
22244   "coalesce ((CASE WHEN results.severity > " G_STRINGIFY (SEVERITY_LOG) \
22245   "           THEN CAST (nvts.cvss_base AS double precision)"           \
22246   "           ELSE results.severity"                                    \
22247   "           END),"                                                    \
22248   "          results.severity)"
22249 
22250 /**
22251  * @brief Get LATERAL clause for result iterator.
22252  *
22253  * @param[in]  apply_overrides   Whether to apply overrides.
22254  * @param[in]  dynamic_severity  Whether to use dynamic severity.
22255  *
22256  * @return SQL clause for FROM.
22257  */
22258 static const gchar *
result_iterator_lateral(int apply_overrides,int dynamic_severity)22259 result_iterator_lateral (int apply_overrides, int dynamic_severity)
22260 {
22261   if (apply_overrides && dynamic_severity)
22262     /* Overrides, dynamic. */
22263     return "(WITH curr AS (SELECT " CURRENT_SEVERITY_SQL " AS curr_severity)"
22264            " SELECT coalesce ((SELECT ov_new_severity FROM result_overrides"
22265            "                   WHERE result = results.id"
22266            "                   AND result_overrides.user = opts.user_id"
22267            "                   AND severity_matches_ov"
22268            "                        ((SELECT curr_severity FROM curr LIMIT 1),"
22269            "                         ov_old_severity)"
22270            "                   LIMIT 1),"
22271            "                  (SELECT curr_severity FROM curr LIMIT 1))"
22272            " AS new_severity)";
22273   if (apply_overrides)
22274     /* Overrides, no dynamic. */
22275     return "(SELECT new_severity"
22276            " FROM result_new_severities_static"
22277            " WHERE result_new_severities_static.result = results.id"
22278            " AND result_new_severities_static.user = opts.user_id"
22279            " LIMIT 1)";
22280   if (dynamic_severity)
22281     /* No overrides, dynamic. */
22282     return "(SELECT " CURRENT_SEVERITY_SQL " AS new_severity)";
22283   /* No overrides, no dynamic.
22284    *
22285    * SELECT because results.severity gives syntax error. */
22286   return "(SELECT results.severity AS new_severity)";
22287 }
22288 
22289 /**
22290  * @brief Initialise a result iterator.
22291  *
22292  * @param[in]  iterator    Iterator.
22293  * @param[in]  get         GET data.
22294  * @param[in]  report      Report to restrict returned results to.
22295  * @param[in]  host        Host to limit results to.
22296  * @param[in]  extra_order Extra text for ORDER term in SQL.
22297  *
22298  * @return 0 success, 1 failed to find result, 2 failed to find filter (filt_id),
22299  *         -1 error.
22300  */
22301 int
init_result_get_iterator(iterator_t * iterator,const get_data_t * get,report_t report,const char * host,const gchar * extra_order)22302 init_result_get_iterator (iterator_t* iterator, const get_data_t *get,
22303                           report_t report, const char* host,
22304                           const gchar *extra_order)
22305 {
22306   static const char *filter_columns[] = RESULT_ITERATOR_FILTER_COLUMNS;
22307   static column_t columns[] = RESULT_ITERATOR_COLUMNS;
22308   static column_t columns_no_cert[] = RESULT_ITERATOR_COLUMNS_NO_CERT;
22309   int ret;
22310   gchar *filter, *extra_tables, *extra_where, *extra_where_single, *opts_tables;
22311   int apply_overrides, dynamic_severity;
22312   column_t *actual_columns;
22313 
22314   g_debug ("%s", __func__);
22315 
22316   if (report == -1)
22317     {
22318       init_iterator (iterator, "SELECT NULL WHERE false;");
22319       return 0;
22320     }
22321 
22322   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
22323     {
22324       filter = filter_term (get->filt_id);
22325       if (filter == NULL)
22326         return 2;
22327     }
22328   else
22329     filter = NULL;
22330 
22331   apply_overrides
22332     = filter_term_apply_overrides (filter ? filter : get->filter);
22333   dynamic_severity = setting_dynamic_severity_int ();
22334 
22335   if (manage_cert_loaded ())
22336     actual_columns = columns;
22337   else
22338     actual_columns = columns_no_cert;
22339 
22340   opts_tables = result_iterator_opts_table (apply_overrides, dynamic_severity);
22341   extra_tables = g_strdup_printf (" LEFT OUTER JOIN nvts"
22342                                   " ON results.nvt = nvts.oid %s,"
22343                                   " LATERAL %s AS lateral_new_severity",
22344                                   opts_tables,
22345                                   result_iterator_lateral (apply_overrides,
22346                                                            dynamic_severity));
22347   g_free (opts_tables);
22348 
22349   extra_where = results_extra_where (get->trash, report, host,
22350                                      apply_overrides, dynamic_severity,
22351                                      filter ? filter : get->filter,
22352                                      NULL);
22353 
22354   extra_where_single = results_extra_where (get->trash, report, host,
22355                                             apply_overrides,
22356                                             dynamic_severity,
22357                                             "min_qod=0",
22358                                             NULL);
22359 
22360   free (filter);
22361 
22362   ret = init_get_iterator2 (iterator,
22363                             "result",
22364                             get,
22365                             /* SELECT columns. */
22366                             actual_columns,
22367                             NULL,
22368                             /* Filterable columns not in SELECT columns. */
22369                             NULL,
22370                             NULL,
22371                             filter_columns,
22372                             0,
22373                             extra_tables,
22374                             extra_where,
22375                             extra_where_single,
22376                             TRUE,
22377                             report ? TRUE : FALSE,
22378                             extra_order);
22379   g_free (extra_tables);
22380   g_free (extra_where);
22381   g_free (extra_where_single);
22382 
22383   g_debug ("%s: done", __func__);
22384 
22385   return ret;
22386 }
22387 
22388 /**
22389  * @brief Count the number of results.
22390  *
22391  * @param[in]  get     GET params.
22392  * @param[in]  report  Report to limit results to.
22393  * @param[in]  host    Host to limit results to.
22394  *
22395  * @return Total number of results in filtered set.
22396  */
22397 int
result_count(const get_data_t * get,report_t report,const char * host)22398 result_count (const get_data_t *get, report_t report, const char* host)
22399 {
22400   static const char *filter_columns[] = RESULT_ITERATOR_FILTER_COLUMNS;
22401   static column_t columns[] = RESULT_ITERATOR_COLUMNS;
22402   static column_t columns_no_cert[] = RESULT_ITERATOR_COLUMNS_NO_CERT;
22403   int ret;
22404   gchar *filter, *extra_tables, *extra_where, *opts_tables;
22405   int apply_overrides, dynamic_severity;
22406 
22407   if (report == -1)
22408     return 0;
22409 
22410   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
22411     {
22412       filter = filter_term (get->filt_id);
22413       if (filter == NULL)
22414         return 2;
22415     }
22416   else
22417     filter = NULL;
22418 
22419   apply_overrides
22420     = filter_term_apply_overrides (filter ? filter : get->filter);
22421   dynamic_severity = setting_dynamic_severity_int ();
22422 
22423   opts_tables = result_iterator_opts_table (apply_overrides, dynamic_severity);
22424   extra_tables = g_strdup_printf (" LEFT OUTER JOIN nvts"
22425                                   " ON results.nvt = nvts.oid %s,"
22426                                   " LATERAL %s AS lateral_new_severity",
22427                                   opts_tables,
22428                                   result_iterator_lateral (apply_overrides,
22429                                                            dynamic_severity));
22430   g_free (opts_tables);
22431 
22432   extra_where = results_extra_where (get->trash, report, host,
22433                                      apply_overrides, dynamic_severity,
22434                                      filter ? filter : get->filter,
22435                                      NULL);
22436 
22437   ret = count ("result", get,
22438                 manage_cert_loaded () ? columns : columns_no_cert,
22439                 manage_cert_loaded () ? columns : columns_no_cert,
22440                 filter_columns, 0,
22441                 extra_tables,
22442                 extra_where,
22443                 TRUE);
22444   g_free (extra_tables);
22445   g_free (extra_where);
22446   return ret;
22447 }
22448 
22449 /**
22450  * @brief Get the result from a result iterator.
22451  *
22452  * @param[in]  iterator  Iterator.
22453  *
22454  * @return The result.
22455  */
22456 result_t
result_iterator_result(iterator_t * iterator)22457 result_iterator_result (iterator_t* iterator)
22458 {
22459   if (iterator->done) return 0;
22460   return (result_t) iterator_int64 (iterator, 0);
22461 }
22462 
22463 /**
22464  * @brief Get the host from a result iterator.
22465  *
22466  * @param[in]  iterator  Iterator.
22467  *
22468  * @return The host of the result.  Caller must only use before calling
22469  *         cleanup_iterator.
22470  */
22471 DEF_ACCESS (result_iterator_host, GET_ITERATOR_COLUMN_COUNT);
22472 
22473 /**
22474  * @brief Get the port from a result iterator.
22475  *
22476  * @param[in]  iterator  Iterator.
22477  *
22478  * @return The port of the result.  Caller must only use before calling
22479  *         cleanup_iterator.
22480  */
22481 DEF_ACCESS (result_iterator_port, GET_ITERATOR_COLUMN_COUNT + 1);
22482 
22483 /**
22484  * @brief Get the NVT OID from a result iterator.
22485  *
22486  * @param[in]  iterator  Iterator.
22487  *
22488  * @return The NVT OID of the result.  Caller must only use before calling
22489  *         cleanup_iterator.
22490  */
22491 DEF_ACCESS (result_iterator_nvt_oid, GET_ITERATOR_COLUMN_COUNT + 2);
22492 
22493 /**
22494  * @brief Get the original type from a result iterator.
22495  *
22496  * This is the column 'type'.
22497  *
22498  * @param[in]  iterator  Iterator.
22499  *
22500  * @return The original type of the result.  Caller must only use before calling
22501  *         cleanup_iterator.
22502  */
22503 static
22504 DEF_ACCESS (result_iterator_original_type, GET_ITERATOR_COLUMN_COUNT + 3);
22505 
22506 /**
22507  * @brief Get the type from a result iterator.
22508  *
22509  * This is the overridden type.
22510  *
22511  * @param[in]  iterator  Iterator.
22512  *
22513  * @return The type of the result.  Caller must only use before calling
22514  *         cleanup_iterator.
22515  */
22516 static const char*
result_iterator_type(iterator_t * iterator)22517 result_iterator_type (iterator_t *iterator)
22518 {
22519   if (iterator->done) return NULL;
22520   /* new_type */
22521   return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
22522 }
22523 
22524 /**
22525  * @brief Get the descr from a result iterator.
22526  *
22527  * @param[in]  iterator  Iterator.
22528  *
22529  * @return The descr of the result.  Caller must only use before calling
22530  *         cleanup_iterator.
22531  */
22532 DEF_ACCESS (result_iterator_descr, GET_ITERATOR_COLUMN_COUNT + 5);
22533 
22534 /**
22535  * @brief Get the task from a result iterator.
22536  *
22537  * @param[in]  iterator  Iterator.
22538  *
22539  * @return The task associated with the result, or 0 on error.
22540  */
22541 task_t
result_iterator_task(iterator_t * iterator)22542 result_iterator_task (iterator_t* iterator)
22543 {
22544   if (iterator->done) return 0;
22545   return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
22546 }
22547 
22548 /**
22549  * @brief Get the report from a result iterator.
22550  *
22551  * @param[in]  iterator  Iterator.
22552  *
22553  * @return The report associated with the result, or 0 on error.
22554  */
22555 report_t
result_iterator_report(iterator_t * iterator)22556 result_iterator_report (iterator_t* iterator)
22557 {
22558   if (iterator->done) return 0;
22559   return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
22560 }
22561 
22562 /**
22563  * @brief Get the NVT CVSS base value from a result iterator.
22564  *
22565  * @param[in]  iterator  Iterator.
22566  *
22567  * @return The CVSS base of the NVT that produced the result, or NULL on error.
22568  */
22569 DEF_ACCESS (result_iterator_nvt_cvss_base, GET_ITERATOR_COLUMN_COUNT + 8);
22570 
22571 /**
22572  * @brief Get the NVT version used during the scan from a result iterator.
22573  *
22574  * @param[in]  iterator  Iterator.
22575  *
22576  * @return The version of NVT used by the scan that produced the result.
22577  *         Caller must only use before calling cleanup_iterator.
22578  */
22579 const char*
result_iterator_scan_nvt_version(iterator_t * iterator)22580 result_iterator_scan_nvt_version (iterator_t *iterator)
22581 {
22582   const char* ret;
22583 
22584   if (iterator->done)
22585     return NULL;
22586 
22587   /* nvt_version */
22588   ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
22589   return ret ? ret : "";
22590 }
22591 
22592 /**
22593  * @brief Get the original severity from a result iterator.
22594  *
22595  * This is the original severity without overrides.
22596  *
22597  * @param[in]  iterator  Iterator.
22598  *
22599  * @return The original severity of the result.  Caller must only use before
22600  *         calling cleanup_iterator.
22601  */
22602 const char*
result_iterator_original_severity(iterator_t * iterator)22603 result_iterator_original_severity (iterator_t *iterator)
22604 {
22605   const char* ret;
22606 
22607   if (iterator->done)
22608     return NULL;
22609 
22610   /* severity */
22611   ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 10);
22612   return ret ? ret : "";
22613 }
22614 
22615 /**
22616  * @brief Get the original severity/threat level from a result iterator.
22617  *
22618  * This is the original level without overrides.
22619  *
22620  * @param[in]  iterator  Iterator.
22621  *
22622  * @return The original threat level of the result.  Caller must only use before
22623  *         calling cleanup_iterator.
22624  */
22625 const char*
result_iterator_original_level(iterator_t * iterator)22626 result_iterator_original_level (iterator_t *iterator)
22627 {
22628   double severity;
22629   const char* ret;
22630 
22631   if (iterator->done)
22632     return NULL;
22633 
22634   if (iterator_null (iterator, GET_ITERATOR_COLUMN_COUNT + 10))
22635     return NULL;
22636 
22637   /* severity */
22638   severity = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 10);
22639 
22640   ret = severity_to_level (severity, 0);
22641   return ret ? ret : "";
22642 }
22643 
22644 /**
22645  * @brief Get the severity from a result iterator.
22646  *
22647  * This is the the overridden severity.
22648  *
22649  * @param[in]  iterator  Iterator.
22650  *
22651  * @return The severity of the result.  Caller must only use before calling
22652  *         cleanup_iterator.
22653  */
22654 const char*
result_iterator_severity(iterator_t * iterator)22655 result_iterator_severity (iterator_t *iterator)
22656 {
22657   const char* ret;
22658 
22659   if (iterator->done)
22660     return NULL;
22661 
22662   /* new_severity */
22663   ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
22664   return ret ? ret : "";
22665 }
22666 
22667 /**
22668  * @brief Get the severity from a result iterator as double.
22669  *
22670  * This is the the overridden severity.
22671  *
22672  * @param[in]  iterator  Iterator.
22673  *
22674  * @return The severity of the result.  Caller must only use before calling
22675  *         cleanup_iterator.
22676  */
22677 double
result_iterator_severity_double(iterator_t * iterator)22678 result_iterator_severity_double (iterator_t *iterator)
22679 {
22680   if (iterator->done)
22681     return 0.0;
22682 
22683   return iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
22684 }
22685 
22686 /**
22687  * @brief Get the severity/threat level from a result iterator.
22688  *
22689  * This is the the overridden level.
22690  *
22691  * @param[in]  iterator  Iterator.
22692  *
22693  * @return The threat level of the result.  Caller must only use before
22694  *         calling cleanup_iterator.
22695  */
22696 const char*
result_iterator_level(iterator_t * iterator)22697 result_iterator_level (iterator_t *iterator)
22698 {
22699   double severity;
22700   const char* ret;
22701 
22702   if (iterator->done)
22703     return "";
22704 
22705   /* new_severity */
22706   if (iterator_null (iterator, GET_ITERATOR_COLUMN_COUNT + 11))
22707     return "";
22708 
22709   severity = iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
22710 
22711   ret = severity_to_level (severity, 0);
22712   return ret ? ret : "";
22713 }
22714 
22715 /**
22716  * @brief Get the solution type from a result iterator.
22717  *
22718  * @param[in]  iterator  Iterator.
22719  *
22720  * @return The solution type of the result.  Caller must only use before calling
22721  *         cleanup_iterator.
22722  */
22723 DEF_ACCESS (result_iterator_solution_type, GET_ITERATOR_COLUMN_COUNT + 15);
22724 
22725 /**
22726  * @brief Get the qod from a result iterator.
22727  *
22728  * @param[in]  iterator  Iterator.
22729  *
22730  * @return The qod of the result.  Caller must only use before calling
22731  *         cleanup_iterator.
22732  */
22733 DEF_ACCESS (result_iterator_qod, GET_ITERATOR_COLUMN_COUNT + 16);
22734 
22735 /**
22736  * @brief Get the qod_type from a result iterator.
22737  *
22738  * @param[in]  iterator  Iterator.
22739  *
22740  * @return The qod type of the result.  Caller must only use before calling
22741  *         cleanup_iterator.
22742  */
22743 DEF_ACCESS (result_iterator_qod_type, GET_ITERATOR_COLUMN_COUNT + 17);
22744 
22745 /**
22746  * @brief Get the host from a result iterator.
22747  *
22748  * @param[in]  iterator  Iterator.
22749  *
22750  * @return The host of the result.  Caller must only use before calling
22751  *         cleanup_iterator.
22752  */
22753 DEF_ACCESS (result_iterator_hostname, GET_ITERATOR_COLUMN_COUNT + 18);
22754 
22755 /**
22756  * @brief Get the path from a result iterator.
22757  *
22758  * @param[in]  iterator  Iterator.
22759  *
22760  * @return The path of the result.  Caller must only use before
22761  *         calling cleanup_iterator.
22762  */
22763 DEF_ACCESS (result_iterator_path, GET_ITERATOR_COLUMN_COUNT + 21);
22764 
22765 /**
22766  * @brief Get the asset host ID from a result iterator.
22767  *
22768  * @param[in]  iterator  Iterator.
22769  *
22770  * @return The ID of the asset host.  Caller must only use before
22771  *         calling cleanup_iterator.
22772  */
22773 DEF_ACCESS (result_iterator_asset_host_id, GET_ITERATOR_COLUMN_COUNT + 22);
22774 
22775 /**
22776  * @brief Get whether notes may exist from a result iterator.
22777  *
22778  * @param[in]  iterator  Iterator.
22779  *
22780  * @return 1 if notes may exist, else 0.
22781  */
22782 int
result_iterator_may_have_notes(iterator_t * iterator)22783 result_iterator_may_have_notes (iterator_t* iterator)
22784 {
22785   if (iterator->done) return 0;
22786   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 23);
22787 }
22788 
22789 /**
22790  * @brief Get whether overrides may exist from a result iterator.
22791  *
22792  * @param[in]  iterator  Iterator.
22793  *
22794  * @return 1 if overrides may exist, else 0.
22795  */
22796 int
result_iterator_may_have_overrides(iterator_t * iterator)22797 result_iterator_may_have_overrides (iterator_t* iterator)
22798 {
22799   if (iterator->done) return 0;
22800   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 24);
22801 }
22802 
22803 /**
22804  * @brief Get whether tickets may exist from a result iterator.
22805  *
22806  * @param[in]  iterator  Iterator.
22807  *
22808  * @return 1 if notes may exist, else 0.
22809  */
22810 int
result_iterator_may_have_tickets(iterator_t * iterator)22811 result_iterator_may_have_tickets (iterator_t* iterator)
22812 {
22813   if (iterator->done) return 0;
22814   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 25);
22815 }
22816 
22817 /**
22818  * @brief Get the NVT summary from a result iterator.
22819  *
22820  * @param[in]  iterator  Iterator.
22821  *
22822  * @return The summary of the NVT that produced the result, or NULL on error.
22823  */
22824 DEF_ACCESS (result_iterator_nvt_summary, GET_ITERATOR_COLUMN_COUNT + 27);
22825 
22826 /**
22827  * @brief Get the NVT insight from a result iterator.
22828  *
22829  * @param[in]  iterator  Iterator.
22830  *
22831  * @return The insight of the NVT that produced the result, or NULL on error.
22832  */
22833 DEF_ACCESS (result_iterator_nvt_insight, GET_ITERATOR_COLUMN_COUNT + 28);
22834 
22835 /**
22836  * @brief Get the NVT affected from a result iterator.
22837  *
22838  * @param[in]  iterator  Iterator.
22839  *
22840  * @return The affected of the NVT that produced the result, or NULL on error.
22841  */
22842 DEF_ACCESS (result_iterator_nvt_affected, GET_ITERATOR_COLUMN_COUNT + 29);
22843 
22844 /**
22845  * @brief Get the NVT impact from a result iterator.
22846  *
22847  * @param[in]  iterator  Iterator.
22848  *
22849  * @return Impact text of the NVT that produced the result, or NULL on error.
22850  */
22851 DEF_ACCESS (result_iterator_nvt_impact, GET_ITERATOR_COLUMN_COUNT + 30);
22852 
22853 /**
22854  * @brief Get the NVT solution from a result iterator.
22855  *
22856  * @param[in]  iterator  Iterator.
22857  *
22858  * @return The solution of the NVT that produced the result, or NULL on error.
22859  */
22860 DEF_ACCESS (result_iterator_nvt_solution, GET_ITERATOR_COLUMN_COUNT + 31);
22861 
22862 /**
22863  * @brief Get the NVT detection from a result iterator.
22864  *
22865  * @param[in]  iterator  Iterator.
22866  *
22867  * @return The detection of the NVT that produced the result, or NULL on error.
22868  */
22869 DEF_ACCESS (result_iterator_nvt_detection, GET_ITERATOR_COLUMN_COUNT + 32);
22870 
22871 /**
22872  * @brief Get the NVT family from a result iterator.
22873  *
22874  * @param[in]  iterator  Iterator.
22875  *
22876  * @return The family of the NVT that produced the result, or NULL on error.
22877  */
22878 DEF_ACCESS (result_iterator_nvt_family, GET_ITERATOR_COLUMN_COUNT + 33);
22879 
22880 /**
22881  * @brief Get the NVT tags from a result iterator.
22882  *
22883  * @param[in]  iterator  Iterator.
22884  *
22885  * @return The tags of the NVT that produced the result, or NULL on error.
22886  */
22887 DEF_ACCESS (result_iterator_nvt_tag, GET_ITERATOR_COLUMN_COUNT + 34);
22888 
22889 /**
22890  * @brief Get CERT-BUNDs from a result iterator.
22891  *
22892  * @param[in]  iterator  Iterator.
22893  *
22894  * @return CERT-BUND names if any, else NULL.
22895  */
22896 gchar **
result_iterator_cert_bunds(iterator_t * iterator)22897 result_iterator_cert_bunds (iterator_t* iterator)
22898 {
22899   if (iterator->done) return 0;
22900   return iterator_array (iterator, GET_ITERATOR_COLUMN_COUNT + 35);
22901 }
22902 
22903 /**
22904  * @brief Get DFN-CERTs from a result iterator.
22905  *
22906  * @param[in]  iterator  Iterator.
22907  *
22908  * @return DFN-CERT names if any, else NULL.
22909  */
22910 gchar **
result_iterator_dfn_certs(iterator_t * iterator)22911 result_iterator_dfn_certs (iterator_t* iterator)
22912 {
22913   if (iterator->done) return 0;
22914   return iterator_array (iterator, GET_ITERATOR_COLUMN_COUNT + 36);
22915 }
22916 
22917 /**
22918  * @brief Get the NVT name from a result iterator.
22919  *
22920  * @param[in]  iterator  Iterator.
22921  *
22922  * @return The name of the NVT that produced the result, or NULL on error.
22923  */
22924 const char*
result_iterator_nvt_name(iterator_t * iterator)22925 result_iterator_nvt_name (iterator_t *iterator)
22926 {
22927   return get_iterator_name (iterator);
22928 }
22929 
22930 /**
22931  * @brief Get the NVT solution_type from a result iterator.
22932  *
22933  * @param[in]  iterator  Iterator.
22934  *
22935  * @return The solution_type of the NVT that produced the result,
22936  *         or NULL on error.
22937  */
22938 const char*
result_iterator_nvt_solution_type(iterator_t * iterator)22939 result_iterator_nvt_solution_type (iterator_t *iterator)
22940 {
22941   return result_iterator_solution_type (iterator);
22942 }
22943 
22944 /**
22945  * @brief Get the NVT solution_method from a result iterator.
22946  *
22947  * @param[in]  iterator  Iterator.
22948  *
22949  * @return The solution_method of the NVT that produced the result,
22950  *         or NULL on error.
22951  */
22952 const char*
result_iterator_nvt_solution_method(iterator_t * iterator)22953 result_iterator_nvt_solution_method (iterator_t *iterator)
22954 {
22955   /* When we used a cache this was never added to the cache. */
22956   return NULL;
22957 }
22958 
22959 /**
22960  * @brief Append an NVT's references to an XML string buffer.
22961  *
22962  * @param[in]  xml       The buffer where to append to.
22963  * @param[in]  oid       The oid of the nvti object from where to collect the refs.
22964  * @param[in]  first     Marker for first element.
22965  */
22966 void
xml_append_nvt_refs(GString * xml,const char * oid,int * first)22967 xml_append_nvt_refs (GString *xml, const char *oid, int *first)
22968 {
22969   nvti_t *nvti = lookup_nvti (oid);
22970   int i;
22971 
22972   if (!nvti)
22973     return;
22974 
22975   for (i = 0; i < nvti_vtref_len (nvti); i++)
22976     {
22977       vtref_t *ref;
22978 
22979       if (first && *first)
22980         {
22981           xml_string_append (xml, "<refs>");
22982           *first = 0;
22983         }
22984 
22985       ref = nvti_vtref (nvti, i);
22986       xml_string_append (xml, "<ref type=\"%s\" id=\"%s\"/>", vtref_type (ref), vtref_id (ref));
22987     }
22988 }
22989 
22990 /**
22991  * @brief Check if the result_nvts are assigned to result
22992  *
22993  * @return 0 success, -1 error
22994  */
22995 int
cleanup_result_nvts()22996 cleanup_result_nvts ()
22997 {
22998   iterator_t affected_iter;
22999   GArray *affected;
23000   int index;
23001 
23002   g_debug ("%s: Cleaning up results with wrong nvt ids", __func__);
23003   sql ("UPDATE results"
23004        " SET nvt = (SELECT oid FROM nvts WHERE name = nvt),"
23005        "     result_nvt = NULL"
23006        " WHERE nvt IN (SELECT name FROM nvts WHERE name != oid);");
23007 
23008   g_debug ("%s: Cleaning up result_nvts entries with wrong nvt ids",
23009            __func__);
23010   sql ("DELETE FROM result_nvts"
23011        " WHERE nvt IN (SELECT name FROM nvts WHERE name != oid);");
23012 
23013   g_debug ("%s: Creating missing result_nvts entries", __func__);
23014   sql ("INSERT INTO result_nvts (nvt)"
23015        " SELECT DISTINCT nvt FROM results ON CONFLICT (nvt) DO NOTHING;");
23016 
23017   // Get affected reports with overrides
23018   affected = g_array_new (TRUE, TRUE, sizeof (report_t));
23019   init_iterator (&affected_iter,
23020                  "SELECT DISTINCT report FROM results"
23021                  " WHERE (result_nvt IS NULL"
23022                  "        OR report NOT IN"
23023                  "           (SELECT report FROM result_nvt_reports"
23024                  "             WHERE result_nvt IS NOT NULL))"
23025                  "   AND nvt IN (SELECT nvt FROM overrides);");
23026   while (next (&affected_iter))
23027     {
23028       report_t report;
23029       report = iterator_int64 (&affected_iter, 0);
23030       g_array_append_val (affected, report);
23031     }
23032   cleanup_iterator(&affected_iter);
23033 
23034   g_debug ("%s: Adding missing result_nvt values to results", __func__);
23035   sql ("UPDATE results"
23036        " SET result_nvt"
23037        "       = (SELECT id FROM result_nvts"
23038        "           WHERE result_nvts.nvt = results.nvt)"
23039        " WHERE result_nvt IS NULL");
23040 
23041   g_debug ("%s: Cleaning up NULL result_nvt_reports entries", __func__);
23042   sql ("DELETE FROM result_nvt_reports WHERE result_nvt IS NULL;");
23043 
23044   g_debug ("%s: Adding missing result_nvt_reports entries", __func__);
23045   sql ("INSERT INTO result_nvt_reports (report, result_nvt)"
23046        " SELECT DISTINCT report, result_nvts.id FROM results"
23047        "   JOIN result_nvts ON result_nvts.nvt = results.nvt"
23048        "  WHERE report NOT IN (SELECT report FROM result_nvt_reports"
23049        "                       WHERE result_nvt IS NOT NULL)");
23050 
23051   // Re-cache affected reports with overrides
23052   for (index = 0; index < affected->len; index++)
23053     {
23054       report_t report;
23055       report = g_array_index (affected, report_t, index);
23056       g_debug ("%s: Updating cache of affected report %llu",
23057                __func__, report);
23058       report_cache_counts (report, 0, 1, NULL);
23059     }
23060   g_array_free (affected, TRUE);
23061 
23062   return 0;
23063 }
23064 
23065 /**
23066  * @brief Initialise a host iterator.
23067  *
23068  * @param[in]  iterator  Iterator.
23069  * @param[in]  report    Report whose hosts the iterator loops over.
23070  * @param[in]  host      Single host to iterate over.  All hosts if NULL.
23071  * @param[in]  report_host  Single report host to iterate over.  All if 0.
23072  */
23073 void
init_report_host_iterator(iterator_t * iterator,report_t report,const char * host,report_host_t report_host)23074 init_report_host_iterator (iterator_t* iterator, report_t report, const char *host,
23075                            report_host_t report_host)
23076 {
23077   if (report)
23078     {
23079       if (report_host)
23080         init_iterator (iterator,
23081                        "SELECT id, host, iso_time (start_time),"
23082                        " iso_time (end_time), current_port, max_port, report,"
23083                        " (SELECT uuid FROM reports WHERE id = report),"
23084                        " (SELECT uuid FROM hosts"
23085                        "  WHERE id = (SELECT host FROM host_identifiers"
23086                        "              WHERE source_type = 'Report Host'"
23087                        "              AND name = 'ip'"
23088                        "              AND source_id = (SELECT uuid"
23089                        "                               FROM reports"
23090                        "                               WHERE id = report)"
23091                        "              AND value = report_hosts.host"
23092                        "              LIMIT 1))"
23093                        " FROM report_hosts WHERE id = %llu"
23094                        " AND report = %llu"
23095                        "%s%s%s"
23096                        " ORDER BY order_inet (host);",
23097                        report_host,
23098                        report,
23099                        host ? " AND host = '" : "",
23100                        host ? host : "",
23101                        host ? "'" : "");
23102       else
23103         init_iterator (iterator,
23104                        "SELECT id, host, iso_time (start_time),"
23105                        " iso_time (end_time), current_port, max_port, report,"
23106                        " (SELECT uuid FROM reports WHERE id = report),"
23107                        " (SELECT uuid FROM hosts"
23108                        "  WHERE id = (SELECT host FROM host_identifiers"
23109                        "              WHERE source_type = 'Report Host'"
23110                        "              AND name = 'ip'"
23111                        "              AND source_id = (SELECT uuid"
23112                        "                               FROM reports"
23113                        "                               WHERE id = report)"
23114                        "              AND value = report_hosts.host"
23115                        "              LIMIT 1))"
23116                        " FROM report_hosts WHERE report = %llu"
23117                        "%s%s%s"
23118                        " ORDER BY order_inet (host);",
23119                        report,
23120                        host ? " AND host = '" : "",
23121                        host ? host : "",
23122                        host ? "'" : "");
23123     }
23124   else
23125     {
23126       if (report_host)
23127         init_iterator (iterator,
23128                        "SELECT id, host, iso_time (start_time),"
23129                        " iso_time (end_time), current_port, max_port, report,"
23130                        " (SELECT uuid FROM reports WHERE id = report),"
23131                        " ''"
23132                        " FROM report_hosts WHERE id = %llu"
23133                        "%s%s%s"
23134                        " ORDER BY order_inet (host);",
23135                        report_host,
23136                        host ? " AND host = '" : "",
23137                        host ? host : "",
23138                        host ? "'" : "");
23139       else
23140         init_iterator (iterator,
23141                        "SELECT id, host, iso_time (start_time),"
23142                        " iso_time (end_time), current_port, max_port, report,"
23143                        " (SELECT uuid FROM reports WHERE id = report),"
23144                        " ''"
23145                        " FROM report_hosts"
23146                        "%s%s%s"
23147                        " ORDER BY order_inet (host);",
23148                        host ? " WHERE host = '" : "",
23149                        host ? host : "",
23150                        host ? "'" : "");
23151     }
23152 }
23153 
23154 
23155 /**
23156  * @brief Get the report host from a host iterator.
23157  *
23158  * @param[in]  iterator  Iterator.
23159  *
23160  * @return Report host.
23161  */
23162 static report_host_t
host_iterator_report_host(iterator_t * iterator)23163 host_iterator_report_host (iterator_t* iterator)
23164 {
23165   if (iterator->done) return 0;
23166   return (report_host_t) iterator_int64 (iterator, 0);
23167 }
23168 
23169 /**
23170  * @brief Get the host from a host iterator.
23171  *
23172  * @param[in]  iterator  Iterator.
23173  *
23174  * @return The host of the host.  Caller must use only before calling
23175  *         cleanup_iterator.
23176  */
23177 DEF_ACCESS (host_iterator_host, 1);
23178 
23179 /**
23180  * @brief Get the start time from a host iterator.
23181  *
23182  * @param[in]  iterator  Iterator.
23183  *
23184  * @return The start time of the host.  Caller must use only before calling
23185  *         cleanup_iterator.
23186  */
23187 DEF_ACCESS (host_iterator_start_time, 2);
23188 
23189 /**
23190  * @brief Get the end time from a host iterator.
23191  *
23192  * @param[in]  iterator  Iterator.
23193  *
23194  * @return The end time of the host.  Caller must use only before calling
23195  *         cleanup_iterator.
23196  */
23197 DEF_ACCESS (host_iterator_end_time, 3);
23198 
23199 /**
23200  * @brief Get the current port from a host iterator.
23201  *
23202  * @param[in]  iterator  Iterator.
23203  *
23204  * @return Current port.
23205  */
23206 int
host_iterator_current_port(iterator_t * iterator)23207 host_iterator_current_port (iterator_t* iterator)
23208 {
23209   int ret;
23210   if (iterator->done) return -1;
23211   ret = iterator_int (iterator, 4);
23212   return ret;
23213 }
23214 
23215 /**
23216  * @brief Get the max port from a host iterator.
23217  *
23218  * @param[in]  iterator  Iterator.
23219  *
23220  * @return Current port.
23221  */
23222 int
host_iterator_max_port(iterator_t * iterator)23223 host_iterator_max_port (iterator_t* iterator)
23224 {
23225   int ret;
23226   if (iterator->done) return -1;
23227   ret = iterator_int (iterator, 5);
23228   return ret;
23229 }
23230 
23231 /**
23232  * @brief Get the asset UUID from a host iterator.
23233  *
23234  * @param[in]  iterator  Iterator.
23235  *
23236  * @return The UUID of the assset associate with the host.  Caller must use
23237  *         only before calling cleanup_iterator.
23238  */
23239 static
23240 DEF_ACCESS (host_iterator_asset_uuid, 8);
23241 
23242 /**
23243  * @brief Initialise a report errors iterator.
23244  *
23245  * @param[in]  iterator  Iterator.
23246  * @param[in]  report   The report.
23247  */
23248 void
init_report_errors_iterator(iterator_t * iterator,report_t report)23249 init_report_errors_iterator (iterator_t* iterator, report_t report)
23250 {
23251   if (report)
23252     init_iterator (iterator,
23253                    "SELECT results.host, results.port, results.nvt,"
23254                    " results.description,"
23255                    " coalesce((SELECT name FROM nvts"
23256                    "           WHERE nvts.oid = results.nvt), ''),"
23257                    " coalesce((SELECT cvss_base FROM nvts"
23258                    "           WHERE nvts.oid = results.nvt), ''),"
23259                    " results.nvt_version, results.severity,"
23260                    " results.id"
23261                    " FROM results"
23262                    " WHERE results.type = 'Error Message'"
23263                    "  AND results.report = %llu",
23264                    report);
23265 }
23266 
23267 /**
23268  * @brief Get the host from a report error messages iterator.
23269  *
23270  * @param[in]  iterator  Iterator.
23271  *
23272  * @return The host of the report error message.  Caller must use only before
23273  *         calling cleanup_iterator.
23274  */
23275 static
23276 DEF_ACCESS (report_errors_iterator_host, 0);
23277 
23278 /**
23279  * @brief Get the port from a report error messages iterator.
23280  *
23281  * @param[in]  iterator  Iterator.
23282  *
23283  * @return The port of the report error message.  Caller must use only before
23284  *         calling cleanup_iterator.
23285  */
23286 static
23287 DEF_ACCESS (report_errors_iterator_port, 1);
23288 
23289 /**
23290  * @brief Get the nvt oid from a report error messages iterator.
23291  *
23292  * @param[in]  iterator  Iterator.
23293  *
23294  * @return The nvt of the report error message.  Caller must use only before
23295  *         calling cleanup_iterator.
23296  */
23297 static
23298 DEF_ACCESS (report_errors_iterator_nvt_oid, 2);
23299 
23300 /**
23301  * @brief Get the description from a report error messages iterator.
23302  *
23303  * @param[in]  iterator  Iterator.
23304  *
23305  * @return The description of the report error message.  Caller must use only
23306  * before calling cleanup_iterator.
23307  */
23308 static
23309 DEF_ACCESS (report_errors_iterator_desc, 3);
23310 
23311 /**
23312  * @brief Get the nvt name from a report error messages iterator.
23313  *
23314  * @param[in]  iterator  Iterator.
23315  *
23316  * @return The nvt of the report error message.  Caller must use only before
23317  *         calling cleanup_iterator.
23318  */
23319 static
23320 DEF_ACCESS (report_errors_iterator_nvt_name, 4);
23321 
23322 /**
23323  * @brief Get the nvt cvss base from a report error messages iterator.
23324  *
23325  * @param[in]  iterator  Iterator.
23326  *
23327  * @return The nvt cvss base of the report error message.  Caller must use only
23328  *         before calling cleanup_iterator.
23329  */
23330 static
23331 DEF_ACCESS (report_errors_iterator_nvt_cvss, 5);
23332 
23333 /**
23334  * @brief Get the nvt cvss base from a report error messages iterator.
23335  *
23336  * @param[in]  iterator  Iterator.
23337  *
23338  * @return The nvt version at scan time of the report error message.
23339  *         Caller must use only before calling cleanup_iterator.
23340  */
23341 static
23342 DEF_ACCESS (report_errors_iterator_scan_nvt_version, 6);
23343 
23344 /**
23345  * @brief Get the nvt cvss base from a report error messages iterator.
23346  *
23347  * @param[in]  iterator  Iterator.
23348  *
23349  * @return The severity at scan time of the report error message.
23350  *         Caller must use only before calling cleanup_iterator.
23351  */
23352 static
23353 DEF_ACCESS (report_errors_iterator_severity, 7);
23354 
23355 /**
23356  * @brief Get the result from a report error messages iterator.
23357  *
23358  * @param[in]  iterator  Iterator.
23359  *
23360  * @return Result.
23361  */
23362 static result_t
report_errors_iterator_result(iterator_t * iterator)23363 report_errors_iterator_result (iterator_t* iterator)
23364 {
23365   if (iterator->done) return 0;
23366   return iterator_int64 (iterator, 8);
23367 }
23368 
23369 /**
23370  * @brief Initialise a report host details iterator.
23371  *
23372  * @param[in]  iterator     Iterator.
23373  * @param[in]  report_host  Report host whose details the iterator loops over.
23374  *                          All report_hosts if NULL.
23375  */
23376 static void
init_report_host_details_iterator(iterator_t * iterator,report_host_t report_host)23377 init_report_host_details_iterator (iterator_t* iterator,
23378                                    report_host_t report_host)
23379 {
23380   /* The 'detected_at' and 'detected_by' entries are filtered out of the final
23381    * reports as they are only used internally for product detection. */
23382   init_iterator (iterator,
23383                  "SELECT id, name, value, source_type, source_name,"
23384                  "       source_description, NULL"
23385                  " FROM report_host_details WHERE report_host = %llu"
23386                  " AND NOT name IN ('detected_at', 'detected_by')"
23387                  " AND NOT name LIKE 'detected_by@%%'"
23388                  " UNION SELECT 0, 'Closed CVE', cve, 'openvasmd', oid,"
23389                  "              nvts.name, cvss_base"
23390                  "       FROM nvts, report_host_details"
23391                  "       WHERE cve != ''"
23392                  "       AND family IN (" LSC_FAMILY_LIST ")"
23393                  "       AND nvts.oid = report_host_details.source_name"
23394                  "       AND report_host = %llu"
23395                  "       AND report_host_details.name = 'EXIT_CODE'"
23396                  "       AND report_host_details.value = 'EXIT_NOTVULN';",
23397                  report_host,
23398                  report_host);
23399 }
23400 
23401 /**
23402  * @brief Get the name from a report host details iterator.
23403  *
23404  * @param[in]  iterator  Iterator.
23405  *
23406  * @return The name of the report host detail.  Caller must use only before
23407  *         calling cleanup_iterator.
23408  */
23409 static
23410 DEF_ACCESS (report_host_details_iterator_name, 1);
23411 
23412 /**
23413  * @brief Get the value from a report host details iterator.
23414  *
23415  * @param[in]  iterator  Iterator.
23416  *
23417  * @return The value of the report host detail.  Caller must use only before
23418  *         calling cleanup_iterator.
23419  */
23420 static
23421 DEF_ACCESS (report_host_details_iterator_value, 2);
23422 
23423 /**
23424  * @brief Get the source type from a report host details iterator.
23425  *
23426  * @param[in]  iterator  Iterator.
23427  *
23428  * @return The source type of the report host detail.  Caller must use only
23429  *         before calling cleanup_iterator.
23430  */
23431 static
23432 DEF_ACCESS (report_host_details_iterator_source_type, 3);
23433 
23434 /**
23435  * @brief Get the source name from a report host details iterator.
23436  *
23437  * @param[in]  iterator  Iterator.
23438  *
23439  * @return The source name of the report host detail.  Caller must use only
23440  *         before calling cleanup_iterator.
23441  */
23442 static
23443 DEF_ACCESS (report_host_details_iterator_source_name, 4);
23444 
23445 /**
23446  * @brief Get the source description from a report host details iterator.
23447  *
23448  * @param[in]  iterator  Iterator.
23449  *
23450  * @return The source description of the report host detail.  Caller must use
23451  *         only before calling cleanup_iterator.
23452  */
23453 static
23454 DEF_ACCESS (report_host_details_iterator_source_desc, 5);
23455 
23456 /**
23457  * @brief Get the extra info from a report host details iterator.
23458  *
23459  * @param[in]  iterator  Iterator.
23460  *
23461  * @return Extra info of the report host detail.  Caller must use
23462  *         only before calling cleanup_iterator.
23463  */
23464 static
23465 DEF_ACCESS (report_host_details_iterator_extra, 6);
23466 
23467 /**
23468  * @brief Set the end time of a task.
23469  *
23470  * @param[in]  task  Task.
23471  * @param[in]  time  New time.  Freed before return.  If NULL, clear end time.
23472  */
23473 void
set_task_end_time(task_t task,char * time)23474 set_task_end_time (task_t task, char* time)
23475 {
23476   if (time)
23477     {
23478       sql ("UPDATE tasks SET end_time = %i WHERE id = %llu;",
23479            parse_iso_time (time),
23480            task);
23481       free (time);
23482     }
23483   else
23484     sql ("UPDATE tasks SET end_time = NULL WHERE id = %llu;",
23485          task);
23486 }
23487 
23488 /**
23489  * @brief Set the end time of a task.
23490  *
23491  * @param[in]  task  Task.
23492  * @param[in]  time  New time.  Freed before return.  If NULL, clear end time.
23493  */
23494 void
set_task_end_time_epoch(task_t task,time_t time)23495 set_task_end_time_epoch (task_t task, time_t time)
23496 {
23497   if (time)
23498     sql ("UPDATE tasks SET end_time = %i WHERE id = %llu;", time, task);
23499   else
23500     sql ("UPDATE tasks SET end_time = NULL WHERE id = %llu;", task);
23501 }
23502 
23503 /**
23504  * @brief Get the start time of a scan.
23505  *
23506  * @param[in]  report  The report associated with the scan.
23507  *
23508  * @return Start time of scan, in a newly allocated string.
23509  */
23510 static char*
scan_start_time(report_t report)23511 scan_start_time (report_t report)
23512 {
23513   char *time = sql_string ("SELECT iso_time (start_time)"
23514                            " FROM reports WHERE id = %llu;",
23515                            report);
23516   return time ? time : g_strdup ("");
23517 }
23518 
23519 /**
23520  * @brief Get the start time of a scan, in seconds since the epoch.
23521  *
23522  * @param[in]  report  The report associated with the scan.
23523  *
23524  * @return Start time of scan, in seconds.
23525  */
23526 int
scan_start_time_epoch(report_t report)23527 scan_start_time_epoch (report_t report)
23528 {
23529   return sql_int ("SELECT start_time FROM reports WHERE id = %llu;",
23530                   report);
23531 }
23532 
23533 /**
23534  * @brief Get the start time of a scan.
23535  *
23536  * @param[in]  uuid  The report associated with the scan.
23537  *
23538  * @return Start time of scan, in a newly allocated string.
23539  */
23540 char*
scan_start_time_uuid(const char * uuid)23541 scan_start_time_uuid (const char *uuid)
23542 {
23543   char *time, *quoted_uuid;
23544   quoted_uuid = sql_quote (uuid);
23545   time = sql_string ("SELECT iso_time (start_time)"
23546                      " FROM reports WHERE uuid = '%s';",
23547                      quoted_uuid);
23548   return time ? time : g_strdup ("");
23549 }
23550 
23551 /**
23552  * @brief Set the start time of a scan.
23553  *
23554  * @param[in]  report     The report associated with the scan.
23555  * @param[in]  timestamp  Start time. Epoch format.
23556  */
23557 void
set_scan_start_time_epoch(report_t report,time_t timestamp)23558 set_scan_start_time_epoch (report_t report, time_t timestamp)
23559 {
23560   sql ("UPDATE reports SET start_time = %i WHERE id = %llu;",
23561        timestamp, report);
23562 }
23563 
23564 /**
23565  * @brief Set the start time of a scan.
23566  *
23567  * @param[in]  report     The report associated with the scan.
23568  * @param[in]  timestamp  Start time.  In UTC ctime format.
23569  */
23570 void
set_scan_start_time_ctime(report_t report,const char * timestamp)23571 set_scan_start_time_ctime (report_t report, const char* timestamp)
23572 {
23573   sql ("UPDATE reports SET start_time = %i WHERE id = %llu;",
23574        parse_utc_ctime (timestamp),
23575        report);
23576 }
23577 
23578 /**
23579  * @brief Get the end time of a scan.
23580  *
23581  * @param[in]  report  The report associated with the scan.
23582  *
23583  * @return End time of scan, in a newly allocated string.
23584  */
23585 static char*
scan_end_time(report_t report)23586 scan_end_time (report_t report)
23587 {
23588   char *time = sql_string ("SELECT iso_time (end_time)"
23589                            " FROM reports WHERE id = %llu;",
23590                            report);
23591   return time ? time : g_strdup ("");
23592 }
23593 
23594 /**
23595  * @brief Get the end time of a scan.
23596  *
23597  * @param[in]  uuid  The report associated with the scan.
23598  *
23599  * @return End time of scan, in a newly allocated string.
23600  */
23601 char*
scan_end_time_uuid(const char * uuid)23602 scan_end_time_uuid (const char *uuid)
23603 {
23604   char *time, *quoted_uuid;
23605   quoted_uuid = sql_quote (uuid);
23606   time = sql_string ("SELECT iso_time (end_time)"
23607                      " FROM reports WHERE uuid = '%s';",
23608                      quoted_uuid);
23609   return time ? time : g_strdup ("");
23610 }
23611 
23612 /**
23613  * @brief Set the end time of a scan.
23614  *
23615  * @param[in]  report     The report associated with the scan.
23616  * @param[in]  timestamp  End time. Epoch format.
23617  */
23618 void
set_scan_end_time_epoch(report_t report,time_t timestamp)23619 set_scan_end_time_epoch (report_t report, time_t timestamp)
23620 {
23621   if (timestamp)
23622     sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
23623          timestamp, report);
23624 }
23625 
23626 /**
23627  * @brief Set the end time of a scan.
23628  *
23629  * @param[in]  report     The report associated with the scan.
23630  * @param[in]  timestamp  End time.  ISO format.  If NULL, clear end time.
23631  */
23632 void
set_scan_end_time(report_t report,const char * timestamp)23633 set_scan_end_time (report_t report, const char* timestamp)
23634 {
23635   if (timestamp)
23636     sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
23637          parse_iso_time (timestamp), report);
23638   else
23639     sql ("UPDATE reports SET end_time = NULL WHERE id = %llu;",
23640          report);
23641 }
23642 
23643 /**
23644  * @brief Set the end time of a scan.
23645  *
23646  * @param[in]  report     The report associated with the scan.
23647  * @param[in]  timestamp  End time.  In UTC ctime format.  If NULL, clear end
23648  *                        time.
23649  */
23650 void
set_scan_end_time_ctime(report_t report,const char * timestamp)23651 set_scan_end_time_ctime (report_t report, const char* timestamp)
23652 {
23653   if (timestamp)
23654     sql ("UPDATE reports SET end_time = %i WHERE id = %llu;",
23655          parse_utc_ctime (timestamp), report);
23656   else
23657     sql ("UPDATE reports SET end_time = NULL WHERE id = %llu;",
23658          report);
23659 }
23660 
23661 /**
23662  * @brief Get the end time of a scanned host.
23663  *
23664  * @param[in]  report     Report associated with the scan.
23665  * @param[in]  host       Host.
23666  *
23667  * @return End time.
23668  */
23669 int
scan_host_end_time(report_t report,const char * host)23670 scan_host_end_time (report_t report, const char* host)
23671 {
23672   gchar *quoted_host;
23673   int ret;
23674 
23675   quoted_host = sql_quote (host);
23676   ret = sql_int ("SELECT end_time FROM report_hosts"
23677                  " WHERE report = %llu AND host = '%s';",
23678                  report, quoted_host);
23679   g_free (quoted_host);
23680   return ret;
23681 }
23682 
23683 /**
23684  * @brief Set the end time of a scanned host.
23685  *
23686  * @param[in]  report     Report associated with the scan.
23687  * @param[in]  host       Host.
23688  * @param[in]  timestamp  End time.  ISO format.
23689  */
23690 void
set_scan_host_end_time(report_t report,const char * host,const char * timestamp)23691 set_scan_host_end_time (report_t report, const char* host,
23692                         const char* timestamp)
23693 {
23694   gchar *quoted_host;
23695   quoted_host = sql_quote (host);
23696   if (sql_int ("SELECT COUNT(*) FROM report_hosts"
23697                " WHERE report = %llu AND host = '%s';",
23698                report, quoted_host))
23699     sql ("UPDATE report_hosts SET end_time = %i"
23700          " WHERE report = %llu AND host = '%s';",
23701          parse_iso_time (timestamp), report, quoted_host);
23702   else
23703     manage_report_host_add (report, host, 0, parse_iso_time (timestamp));
23704   g_free (quoted_host);
23705 }
23706 
23707 /**
23708  * @brief Set the end time of a scanned host.
23709  *
23710  * @param[in]  report     Report associated with the scan.
23711  * @param[in]  host       Host.
23712  * @param[in]  timestamp  End time.  In UTC ctime format.
23713  */
23714 void
set_scan_host_end_time_ctime(report_t report,const char * host,const char * timestamp)23715 set_scan_host_end_time_ctime (report_t report, const char* host,
23716                             const char* timestamp)
23717 {
23718   gchar *quoted_host;
23719   quoted_host = sql_quote (host);
23720   if (sql_int ("SELECT COUNT(*) FROM report_hosts"
23721                " WHERE report = %llu AND host = '%s';",
23722                report, quoted_host))
23723     sql ("UPDATE report_hosts SET end_time = %i"
23724          " WHERE report = %llu AND host = '%s';",
23725          parse_utc_ctime (timestamp), report, quoted_host);
23726   else
23727     manage_report_host_add (report, host, 0, parse_utc_ctime (timestamp));
23728   g_free (quoted_host);
23729 }
23730 
23731 /**
23732  * @brief Set the start time of a scanned host.
23733  *
23734  * @param[in]  report     Report associated with the scan.
23735  * @param[in]  host       Host.
23736  * @param[in]  timestamp  Start time.  In UTC ctime format.
23737  */
23738 void
set_scan_host_start_time_ctime(report_t report,const char * host,const char * timestamp)23739 set_scan_host_start_time_ctime (report_t report, const char* host,
23740                               const char* timestamp)
23741 {
23742   gchar *quoted_host;
23743   quoted_host = sql_quote (host);
23744   if (sql_int ("SELECT COUNT(*) FROM report_hosts"
23745                " WHERE report = %llu AND host = '%s';",
23746                report, quoted_host))
23747     sql ("UPDATE report_hosts SET start_time = %i"
23748          " WHERE report = %llu AND host = '%s';",
23749          parse_utc_ctime (timestamp), report, quoted_host);
23750   else
23751     manage_report_host_add (report, host, parse_utc_ctime (timestamp), 0);
23752   g_free (quoted_host);
23753 }
23754 
23755 /**
23756  * @brief Get the timestamp of a report.
23757  *
23758  * @todo Lacks permission check.  Caller contexts all have permission
23759  *       checks before calling this so it's safe.  Rework callers so
23760  *       they pass report_t instead of UUID string.
23761  *
23762  * @param[in]   report_id    UUID of report.
23763  * @param[out]  timestamp    Timestamp on success.  Caller must free.
23764  *
23765  * @return 0 on success, -1 on error.
23766  */
23767 int
report_timestamp(const char * report_id,gchar ** timestamp)23768 report_timestamp (const char* report_id, gchar** timestamp)
23769 {
23770   const char* stamp;
23771   time_t time = sql_int ("SELECT date FROM reports where uuid = '%s';",
23772                          report_id);
23773   stamp = iso_time (&time);
23774   if (stamp == NULL) return -1;
23775   *timestamp = g_strdup (stamp);
23776   return 0;
23777 }
23778 
23779 /**
23780  * @brief Return the run status of the scan associated with a report.
23781  *
23782  * @param[in]   report  Report.
23783  * @param[out]  status  Scan run status.
23784  *
23785  * @return 0 on success, -1 on error.
23786  */
23787 static int
report_scan_run_status(report_t report,task_status_t * status)23788 report_scan_run_status (report_t report, task_status_t* status)
23789 {
23790   *status = sql_int ("SELECT scan_run_status FROM reports"
23791                      " WHERE reports.id = %llu;",
23792                      report);
23793   return 0;
23794 }
23795 
23796 /**
23797  * @brief Return the run status of the scan associated with a report.
23798  *
23799  * @param[in]   report  Report.
23800  * @param[out]  status  Scan run status.
23801  *
23802  * @return 0 on success, -1 on error.
23803  */
23804 int
set_report_scan_run_status(report_t report,task_status_t status)23805 set_report_scan_run_status (report_t report, task_status_t status)
23806 {
23807   sql ("UPDATE reports SET scan_run_status = %u WHERE id = %llu;",
23808        status,
23809        report);
23810   if (setting_auto_cache_rebuild_int ())
23811     report_cache_counts (report, 0, 0, NULL);
23812   return 0;
23813 }
23814 
23815 /**
23816  * @brief Get the result severity counts for a report.
23817  *
23818  * @param[in]  report     Report.
23819  * @param[in]  host       Host to which to limit the count.  NULL to allow all.
23820  * @param[in]  get        Report "get" data to retrieve filter info from.
23821  * @param[out] severity_data           The severity data struct to store counts in.
23822  * @param[out] filtered_severity_data  The severity data struct to store counts in.
23823  */
23824 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)23825 report_severity_data (report_t report, const char *host,
23826                       const get_data_t* get,
23827                       severity_data_t* severity_data,
23828                       severity_data_t* filtered_severity_data)
23829 {
23830   iterator_t results;
23831 
23832   gchar *filter;
23833   int apply_overrides;
23834 
23835   if (report == 0)
23836     return;
23837 
23838   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
23839     {
23840       filter = filter_term (get->filt_id);
23841     }
23842   else
23843     filter = NULL;
23844 
23845   apply_overrides
23846     = filter_term_apply_overrides (filter ? filter : get->filter);
23847 
23848   if (severity_data)
23849     {
23850       get_data_t *get_all;
23851 
23852       get_all = report_results_get_data (1, -1, apply_overrides, 0);
23853       ignore_max_rows_per_page = 1;
23854       init_result_get_iterator_severity (&results, get_all, report, host, NULL);
23855       ignore_max_rows_per_page = 0;
23856       while (next (&results))
23857         {
23858           double severity;
23859 
23860           if (results.done)
23861             severity = 0.0;
23862           else
23863             severity = iterator_double (&results, 0);
23864 
23865           severity_data_add (severity_data, severity);
23866         }
23867       cleanup_iterator (&results);
23868       get_data_reset (get_all);
23869       free (get_all);
23870     }
23871 
23872   if (filtered_severity_data)
23873     {
23874       get_data_t get_filtered;
23875 
23876       memset (&get_filtered, 0, sizeof (get_data_t));
23877       get_filtered.filt_id = get->filt_id;
23878       get_filtered.filter = get->filter;
23879       get_filtered.type = get->type;
23880       get_filtered.ignore_pagination = 1;
23881 
23882       ignore_max_rows_per_page = 1;
23883       init_result_get_iterator_severity (&results, &get_filtered, report, host,
23884                                          NULL);
23885       ignore_max_rows_per_page = 0;
23886       while (next (&results))
23887         {
23888           double severity;
23889 
23890           if (results.done)
23891             severity = 0.0;
23892           else
23893             severity = iterator_double (&results, 0);
23894 
23895           severity_data_add (filtered_severity_data, severity);
23896         }
23897       cleanup_iterator (&results);
23898     }
23899 }
23900 
23901 /**
23902  * @brief Get the message counts for a report given the UUID.
23903  *
23904  * @todo Lacks permission check.  Caller contexts all have permission
23905  *       checks before calling this so it's safe.  Rework callers to
23906  *       use report_counts_id instead.
23907  *
23908  * @param[in]   report_id    ID of report.
23909  * @param[out]  holes        Number of hole messages.
23910  * @param[out]  infos        Number of info messages.
23911  * @param[out]  logs         Number of log messages.
23912  * @param[out]  warnings     Number of warning messages.
23913  * @param[out]  false_positives  Number of false positives.
23914  * @param[out]  severity     Maximum severity score.
23915  * @param[in]   override     Whether to override the threat.
23916  * @param[in]   min_qod      Min QOD.
23917  *
23918  * @return 0 on success, -1 on error.
23919  */
23920 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)23921 report_counts (const char* report_id, int* holes, int* infos,
23922                int* logs, int* warnings, int* false_positives, double* severity,
23923                int override, int min_qod)
23924 {
23925   report_t report;
23926   int ret;
23927   get_data_t *get;
23928   // TODO Wrap in transaction.
23929   if (find_report_with_permission (report_id, &report, "get_reports"))
23930     return -1;
23931   // TODO Check if report was found.
23932 
23933   get = report_results_get_data (1, -1, override, min_qod);
23934   ret = report_counts_id (report, holes, infos, logs, warnings,
23935                           false_positives, severity, get, NULL);
23936   get_data_reset (get);
23937   free (get);
23938   return ret;
23939 }
23940 
23941 /**
23942  * @brief Test if a counts cache exists for a report and the current user.
23943  * @param[in]           report    The report to check.
23944  * @param[in]           override  Whether to check for overridden results.
23945  * @param[in]           min_qod   Minimum QoD of results to count.
23946  *
23947  * @return 1 if cache exists, 0 otherwise.
23948  */
23949 static int
report_counts_cache_exists(report_t report,int override,int min_qod)23950 report_counts_cache_exists (report_t report, int override, int min_qod)
23951 {
23952   return sql_int ("SELECT EXISTS (SELECT * FROM report_counts"
23953                   " WHERE report = %llu"
23954                   "   AND override = %d"
23955                   "   AND \"user\" = (SELECT id FROM users"
23956                   "                   WHERE users.uuid = '%s')"
23957                   "   AND min_qod = %d"
23958                   "   AND (end_time = 0 OR end_time >= m_now ()));",
23959                   report, override, current_credentials.uuid, min_qod);
23960 }
23961 
23962 /**
23963  * @brief Get cached result counts for a report and the current user.
23964  *
23965  * @param[in]           report    The report to get counts from.
23966  * @param[in]           override  Whether to get overridden results.
23967  * @param[in]           min_qod   Minimum QoD of results to count.
23968  * @param[out]          data      The severity_data_t to save counts in.
23969  */
23970 static void
report_counts_from_cache(report_t report,int override,int min_qod,severity_data_t * data)23971 report_counts_from_cache (report_t report, int override, int min_qod,
23972                           severity_data_t* data)
23973 {
23974   iterator_t iterator;
23975   init_iterator (&iterator,
23976                  "SELECT severity, count FROM report_counts"
23977                  " WHERE report = %llu"
23978                  "   AND override = %i"
23979                  "   AND \"user\" = (SELECT id FROM users"
23980                  "                   WHERE users.uuid = '%s')"
23981                  "   AND min_qod = %d"
23982                  "   AND (end_time = 0 OR end_time >= m_now ());",
23983                  report, override, current_credentials.uuid, min_qod);
23984   while (next (&iterator))
23985     {
23986       severity_data_add_count (data,
23987                                iterator_double (&iterator, 0),
23988                                iterator_int (&iterator, 1));
23989     }
23990   cleanup_iterator (&iterator);
23991 }
23992 
23993 /**
23994  * @brief Cache the message counts for a report.
23995  *
23996  * @param[in]   report    Report.
23997  * @param[in]   override  Whether overrides were applied to the results.
23998  * @param[in]   min_qod   The minimum QoD of the results.
23999  * @param[in]   data      Severity data struct containing the message counts.
24000  *
24001  * @return      0 if successful, 1 gave up, -1 error (see sql_giveup).
24002  */
24003 static int
cache_report_counts(report_t report,int override,int min_qod,severity_data_t * data)24004 cache_report_counts (report_t report, int override, int min_qod,
24005                      severity_data_t* data)
24006 {
24007   int i, ret;
24008   double severity;
24009   int end_time;
24010 
24011   /* Try cache results. */
24012 
24013   ret = sql_giveup ("DELETE FROM report_counts"
24014                     " WHERE report = %llu"
24015                     "   AND override = %i"
24016                     "   AND min_qod = %i"
24017                     "   AND \"user\" = (SELECT id FROM users"
24018                     "                   WHERE users.uuid = '%s');",
24019                     report, override, min_qod, current_credentials.uuid);
24020   if (ret)
24021     {
24022       return ret;
24023     }
24024 
24025   if (data->total == 0)
24026     {
24027       /* Create dummy entry for empty reports */
24028       ret = sql_giveup ("INSERT INTO report_counts"
24029                         " (report, \"user\", override, min_qod, severity,"
24030                         "  count, end_time)"
24031                         " VALUES (%llu,"
24032                         "         (SELECT id FROM users"
24033                         "          WHERE users.uuid = '%s'),"
24034                         "         %d, %d, " G_STRINGIFY (SEVERITY_MISSING) ","
24035                         "         0, 0);",
24036                         report, current_credentials.uuid, override, min_qod);
24037       if (ret)
24038         {
24039           return ret;
24040         }
24041     }
24042   else
24043     {
24044       GString *insert;
24045       int first;
24046 
24047       i = 0;
24048       if (override)
24049         end_time = sql_int ("SELECT coalesce(min(end_time), 0)"
24050                             " FROM overrides, results"
24051                             " WHERE overrides.nvt = results.nvt"
24052                             " AND results.report = %llu"
24053                             " AND overrides.end_time >= m_now ();",
24054                             report);
24055       else
24056         end_time = 0;
24057 
24058       severity = severity_data_value (i);
24059       insert = g_string_new ("INSERT INTO report_counts"
24060                              " (report, \"user\", override, min_qod,"
24061                              "  severity, count, end_time)"
24062                              " VALUES");
24063       first = 1;
24064       while (severity <= (data->max + (1.0
24065                                        / SEVERITY_SUBDIVISIONS
24066                                        / SEVERITY_SUBDIVISIONS))
24067              && severity != SEVERITY_MISSING)
24068         {
24069           if (data->counts[i] > 0)
24070             {
24071               g_string_append_printf (insert,
24072                                       "%s (%llu,"
24073                                       "    (SELECT id FROM users"
24074                                       "     WHERE users.uuid = '%s'),"
24075                                       "    %d, %d, %1.1f, %d, %d)",
24076                                       first == 1 ? "" : ",",
24077                                       report, current_credentials.uuid,
24078                                       override, min_qod, severity,
24079                                       data->counts[i], end_time);
24080               first = 0;
24081             }
24082           i++;
24083           severity = severity_data_value (i);
24084         }
24085 
24086       if (i)
24087         {
24088           g_string_append_printf (insert, ";");
24089           ret = sql_giveup (insert->str);
24090           if (ret)
24091             {
24092               g_string_free (insert, TRUE);
24093               return ret;
24094             }
24095         }
24096       g_string_free (insert, TRUE);
24097     }
24098   return 0;
24099 }
24100 
24101 /**
24102  * @brief Get the message counts for a report.
24103  *
24104  * @param[in]   report    Report.
24105  * @param[out]  holes     Number of hole messages.
24106  * @param[out]  infos     Number of info messages.
24107  * @param[out]  logs      Number of log messages.
24108  * @param[out]  warnings  Number of warning messages.
24109  * @param[out]  false_positives    Number of false positive messages.
24110  * @param[out]  severity  Maximum severity of the report.
24111  * @param[in]   get       Get data.
24112  * @param[in]   host      Host to which to limit the count.
24113  * @param[out]  filtered_holes     Number of hole messages after filtering.
24114  * @param[out]  filtered_infos     Number of info messages after filtering.
24115  * @param[out]  filtered_logs      Number of log messages after filtering.
24116  * @param[out]  filtered_warnings  Number of warning messages after filtering.
24117  * @param[out]  filtered_false_positives  Number of false positive messages after
24118  *                                        filtering.
24119  * @param[out]  filtered_severity  Maximum severity after filtering.
24120  *
24121  * @return 0 on success, -1 on error.
24122  */
24123 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)24124 report_counts_id_full (report_t report, int* holes, int* infos,
24125                        int* logs, int* warnings, int* false_positives,
24126                        double* severity,
24127                        const get_data_t* get, const char* host,
24128                        int* filtered_holes,
24129                        int* filtered_infos, int* filtered_logs,
24130                        int* filtered_warnings, int* filtered_false_positives,
24131                        double* filtered_severity)
24132 {
24133   const char *filter;
24134   keyword_t **point;
24135   array_t *split;
24136   int filter_cacheable, unfiltered_requested, filtered_requested, cache_exists;
24137   int override, min_qod_int;
24138   severity_data_t severity_data, filtered_severity_data;
24139 
24140   unfiltered_requested = (holes || warnings || infos || logs || false_positives
24141                           || severity);
24142   filtered_requested = (filtered_holes || filtered_warnings || filtered_infos
24143                         || filtered_logs || filtered_false_positives
24144                         || filtered_severity);
24145 
24146   if (current_credentials.uuid == NULL
24147       || strcmp (current_credentials.uuid, "") == 0)
24148     g_warning ("%s: called by NULL or dummy user", __func__);
24149 
24150   if (get->filt_id && strlen (get->filt_id)
24151       && strcmp (get->filt_id, FILT_ID_NONE))
24152     {
24153       filter = filter_term (get->filt_id);
24154       if (filter == NULL)
24155         {
24156           return -1;
24157         }
24158     }
24159   else
24160     {
24161       filter = get->filter;
24162     }
24163 
24164   filter_cacheable = TRUE;
24165   override = 0;
24166   min_qod_int = MIN_QOD_DEFAULT;
24167 
24168   if (filter == NULL)
24169     filter = "";
24170 
24171   split = split_filter (filter);
24172   point = (keyword_t**) split->pdata;
24173   while (*point)
24174     {
24175       keyword_t *keyword;
24176 
24177       keyword = *point;
24178       if (keyword->column == NULL)
24179         {
24180           filter_cacheable = FALSE;
24181         }
24182       else if (strcasecmp (keyword->column, "first") == 0
24183                || strcasecmp (keyword->column, "rows") == 0
24184                || strcasecmp (keyword->column, "sort") == 0
24185                || strcasecmp (keyword->column, "sort-reverse") == 0
24186                || strcasecmp (keyword->column, "notes") == 0
24187                || strcasecmp (keyword->column, "overrides") == 0)
24188         {
24189           // ignore
24190         }
24191       else if (strcasecmp (keyword->column, "apply_overrides") == 0)
24192         {
24193           if (keyword->string
24194               && strcmp (keyword->string, "")
24195               && strcmp (keyword->string, "0"))
24196             override = 1;
24197         }
24198       else if (strcasecmp (keyword->column, "min_qod") == 0)
24199         {
24200           if (keyword->string == NULL
24201               || sscanf (keyword->string, "%d", &min_qod_int) != 1)
24202             min_qod_int = MIN_QOD_DEFAULT;
24203         }
24204       else
24205         {
24206           filter_cacheable = FALSE;
24207         }
24208       point++;
24209     }
24210   filter_free (split);
24211 
24212   cache_exists = filter_cacheable
24213                  && report_counts_cache_exists (report, override, min_qod_int);
24214   init_severity_data (&severity_data);
24215   init_severity_data (&filtered_severity_data);
24216 
24217   if (cache_exists && filter_cacheable)
24218     {
24219       /* Get unfiltered counts from cache. */
24220       if (unfiltered_requested)
24221         report_counts_from_cache (report, override, min_qod_int,
24222                                   &severity_data);
24223       if (filtered_requested)
24224         report_counts_from_cache (report, override, min_qod_int,
24225                                   &filtered_severity_data);
24226     }
24227   else
24228     {
24229       /* Recalculate. */
24230       report_severity_data (report, host, get,
24231                             unfiltered_requested
24232                               ? &severity_data : NULL,
24233                             filtered_requested
24234                               ? &filtered_severity_data : NULL);
24235     }
24236 
24237   severity_data_level_counts (&severity_data,
24238                               NULL, false_positives,
24239                               logs, infos, warnings, holes);
24240   severity_data_level_counts (&filtered_severity_data,
24241                               NULL, filtered_false_positives,
24242                               filtered_logs, filtered_infos,
24243                               filtered_warnings, filtered_holes);
24244 
24245   if (severity)
24246     *severity = severity_data.max;
24247   if (filtered_severity && filtered_requested)
24248     *filtered_severity = filtered_severity_data.max;
24249 
24250   if (filter_cacheable && !cache_exists)
24251     {
24252       if (unfiltered_requested)
24253         cache_report_counts (report, override, 0, &severity_data);
24254       if (filtered_requested)
24255         cache_report_counts (report, override, min_qod_int,
24256                              &filtered_severity_data);
24257     }
24258 
24259   cleanup_severity_data (&severity_data);
24260   cleanup_severity_data (&filtered_severity_data);
24261 
24262   return 0;
24263 }
24264 
24265 /**
24266  * @brief Get only the filtered message counts for a report.
24267  *
24268  * @param[in]   report    Report.
24269  * @param[out]  holes     Number of hole messages.
24270  * @param[out]  infos     Number of info messages.
24271  * @param[out]  logs      Number of log messages.
24272  * @param[out]  warnings  Number of warning messages.
24273  * @param[out]  false_positives  Number of false positive messages.
24274  * @param[out]  severity  Maximum severity score.
24275  * @param[in]   get       Get data.
24276  * @param[in]   host      Host to which to limit the count.  NULL to allow all.
24277  *
24278  * @return 0 on success, -1 on error.
24279  */
24280 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)24281 report_counts_id (report_t report, int* holes, int* infos,
24282                   int* logs, int* warnings, int* false_positives,
24283                   double* severity, const get_data_t *get, const char *host)
24284 {
24285   int ret;
24286   ret = report_counts_id_full (report, NULL, NULL, NULL, NULL, NULL, NULL,
24287                                get, host, holes, infos, logs, warnings,
24288                                false_positives, severity);
24289   return ret;
24290 }
24291 
24292 /**
24293  * @brief Get the maximum severity of a report.
24294  *
24295  * @param[in]  report     Report.
24296  * @param[in]  overrides  Whether to apply overrides.
24297  * @param[in]  min_qod    Minimum QoD of results to count.
24298  *
24299  * @return Severity score of the report.
24300  */
24301 double
report_severity(report_t report,int overrides,int min_qod)24302 report_severity (report_t report, int overrides, int min_qod)
24303 {
24304   double severity;
24305   iterator_t iterator;
24306 
24307   init_iterator (&iterator,
24308                  "SELECT max(severity)"
24309                  " FROM report_counts"
24310                  " WHERE report = %llu"
24311                  " AND override = %d"
24312                  " AND \"user\" = (SELECT id FROM users WHERE uuid = '%s')"
24313                  " AND min_qod = %d"
24314                  " AND (end_time = 0 or end_time >= m_now ());",
24315                  report, overrides, current_credentials.uuid, min_qod);
24316   if (next (&iterator)
24317       && (iterator_null (&iterator, 0) == 0))
24318     {
24319       g_debug ("%s: max(severity)=%s", __func__,
24320                iterator_string (&iterator, 0));
24321       severity = iterator_double (&iterator, 0);
24322     }
24323   else
24324     {
24325       g_debug ("%s: could not get max from cache", __func__);
24326       get_data_t *get = report_results_get_data (1, -1, overrides, min_qod);
24327       report_counts_id (report, NULL, NULL, NULL, NULL,
24328                         NULL, &severity, get, NULL);
24329       get_data_reset (get);
24330       free (get);
24331     }
24332   cleanup_iterator (&iterator);
24333   return severity;
24334 }
24335 
24336 /**
24337  * @brief Delete a report.
24338  *
24339  * It's up to the caller to provide the transaction.
24340  *
24341  * @param[in]  report  Report.
24342  *
24343  * @return 0 success, 2 report is in use, -1 error.
24344  */
24345 int
delete_report_internal(report_t report)24346 delete_report_internal (report_t report)
24347 {
24348   task_t task;
24349 
24350   if (sql_int ("SELECT count(*) FROM reports WHERE id = %llu"
24351                " AND (scan_run_status = %u OR scan_run_status = %u"
24352                " OR scan_run_status = %u OR scan_run_status = %u"
24353                " OR scan_run_status = %u);",
24354                report,
24355                TASK_STATUS_RUNNING,
24356                TASK_STATUS_QUEUED,
24357                TASK_STATUS_REQUESTED,
24358                TASK_STATUS_DELETE_REQUESTED,
24359                TASK_STATUS_DELETE_ULTIMATE_REQUESTED,
24360                TASK_STATUS_STOP_REQUESTED,
24361                TASK_STATUS_STOP_WAITING))
24362     return 2;
24363 
24364   /* This needs to have exclusive access to reports because otherwise at this
24365    * point another process (like a RESUME_TASK handler) could store the report
24366    * ID and then start trying to access that report after we've deleted it. */
24367 
24368   if (report_task (report, &task))
24369     return -1;
24370 
24371   /* Remove the report data. */
24372 
24373   sql ("DELETE FROM report_host_details WHERE report_host IN"
24374        " (SELECT id FROM report_hosts WHERE report = %llu);",
24375        report);
24376   sql ("DELETE FROM report_hosts WHERE report = %llu;", report);
24377 
24378   sql ("DELETE FROM tag_resources"
24379        " WHERE resource_type = 'result'"
24380        "   AND resource IN"
24381        "         (SELECT id FROM results WHERE report = %llu);",
24382        report);
24383   sql ("DELETE FROM tag_resources_trash"
24384        " WHERE resource_type = 'result'"
24385        "   AND resource IN"
24386        "         (SELECT id FROM results WHERE report = %llu);",
24387        report);
24388   sql ("DELETE FROM results WHERE report = %llu;", report);
24389   sql ("DELETE FROM results_trash WHERE report = %llu;", report);
24390 
24391   sql ("DELETE FROM tag_resources"
24392        " WHERE resource_type = 'report'"
24393        "   AND resource = %llu;",
24394        report);
24395   sql ("DELETE FROM tag_resources_trash"
24396        " WHERE resource_type = 'report'"
24397        "   AND resource = %llu;",
24398        report);
24399   sql ("DELETE FROM report_counts WHERE report = %llu;", report);
24400   sql ("DELETE FROM result_nvt_reports WHERE report = %llu;", report);
24401   sql ("DELETE FROM reports WHERE id = %llu;", report);
24402 
24403   /* Adjust permissions. */
24404 
24405   permissions_set_orphans ("report", report, LOCATION_TABLE);
24406   tags_remove_resource ("report", report, LOCATION_TABLE);
24407   tickets_remove_report (report);
24408 
24409   /* Update the task state. */
24410 
24411   switch (sql_int64 (&report,
24412                      "SELECT max (id) FROM reports WHERE task = %llu",
24413                      task))
24414     {
24415       case 0:
24416         if (report)
24417           {
24418             task_status_t status;
24419             if (report_scan_run_status (report, &status))
24420               return -1;
24421             sql ("UPDATE tasks SET run_status = %u WHERE id = %llu;",
24422                  status,
24423                  task);
24424           }
24425         else
24426           sql ("UPDATE tasks SET run_status = %u WHERE id = %llu;",
24427                TASK_STATUS_NEW,
24428                task);
24429         break;
24430       case 1:        /* Too few rows in result of query. */
24431         break;
24432       default:       /* Programming error. */
24433         assert (0);
24434       case -1:
24435         return -1;
24436         break;
24437     }
24438 
24439   return 0;
24440 }
24441 
24442 /**
24443  * @brief Delete a report.
24444  *
24445  * @param[in]  report_id  UUID of report.
24446  * @param[in]  dummy      Dummy arg to match other delete functions.
24447  *
24448  * @return 0 success, 2 failed to find report, 99 permission denied, -1 error.
24449  */
24450 int
delete_report(const char * report_id,int dummy)24451 delete_report (const char *report_id, int dummy)
24452 {
24453   report_t report;
24454   int ret;
24455 
24456   sql_begin_immediate ();
24457 
24458   /* This prevents other processes (in particular a RESUME_TASK) from getting
24459    * a reference to the report ID, and then using that reference to try access
24460    * the deleted report.
24461    *
24462    * If the report is running already then delete_report_internal will
24463    * ROLLBACK. */
24464   sql ("LOCK table reports IN ACCESS EXCLUSIVE MODE;");
24465 
24466   if (acl_user_may ("delete_report") == 0)
24467     {
24468       sql_rollback ();
24469       return 99;
24470     }
24471 
24472   report = 0;
24473   if (find_report_with_permission (report_id, &report, "delete_report"))
24474     {
24475       sql_rollback ();
24476       return -1;
24477     }
24478 
24479   if (report == 0)
24480     {
24481       if (find_trash_report_with_permission (report_id, &report, "delete_report"))
24482         {
24483           sql_rollback ();
24484           return -1;
24485         }
24486       if (report == 0)
24487         {
24488           sql_rollback ();
24489           return 2;
24490         }
24491     }
24492 
24493   ret = delete_report_internal (report);
24494   if (ret)
24495     {
24496       sql_rollback ();
24497       return ret;
24498     }
24499 
24500   sql_commit ();
24501 
24502   return 0;
24503 }
24504 
24505 /**
24506  * @brief Return the slave progress of a report.
24507  *
24508  * @param[in]  report  Report.
24509  *
24510  * @return Number of reports.
24511  */
24512 static int
report_slave_progress(report_t report)24513 report_slave_progress (report_t report)
24514 {
24515   return sql_int ("SELECT slave_progress FROM reports WHERE id = %llu;",
24516                   report);
24517 }
24518 
24519 /**
24520  * @brief Set slave progress of a report.
24521  *
24522  * @param[in]  report    The report.
24523  * @param[in]  progress  The new progress value.
24524  *
24525  * @return 0 success.
24526  */
24527 int
set_report_slave_progress(report_t report,int progress)24528 set_report_slave_progress (report_t report, int progress)
24529 {
24530   sql ("UPDATE reports SET slave_progress = %i WHERE id = %llu;",
24531        progress,
24532        report);
24533   return 0;
24534 }
24535 
24536 /**
24537  * @brief Prepare a partial report for restarting the scan from the beginning.
24538  *
24539  * @param[in]  report  The report.
24540  */
24541 void
trim_report(report_t report)24542 trim_report (report_t report)
24543 {
24544   /* Remove results for all hosts. */
24545 
24546   sql ("DELETE FROM results WHERE id IN"
24547        " (SELECT results.id FROM results"
24548        "  WHERE results.report = %llu);",
24549        report);
24550 
24551   /* Remove all hosts and host details. */
24552 
24553   sql ("DELETE FROM report_host_details WHERE report_host IN"
24554        " (SELECT id FROM report_hosts WHERE report = %llu);",
24555        report);
24556   sql ("DELETE FROM report_hosts"
24557        " WHERE report = %llu;",
24558        report);
24559 
24560   /* Clear and rebuild counts cache */
24561   if (setting_auto_cache_rebuild_int ())
24562     report_cache_counts (report, 1, 1, NULL);
24563   else
24564     report_clear_count_cache (report, 1, 1, NULL);
24565 }
24566 
24567 /**
24568  * @brief Prepare a partial report for resumption of the scan.
24569  *
24570  * @param[in]  report  The report.
24571  */
24572 void
trim_partial_report(report_t report)24573 trim_partial_report (report_t report)
24574 {
24575   /* Remove results for partial hosts. */
24576 
24577   sql ("DELETE FROM results WHERE id IN"
24578        " (SELECT results.id FROM results, report_hosts"
24579        "  WHERE results.report = %llu"
24580        "  AND report_hosts.report = %llu"
24581        "  AND results.host = report_hosts.host"
24582        "  AND report_hosts.end_time = 0);",
24583        report,
24584        report);
24585 
24586   /* Remove partial hosts and host details. */
24587 
24588   sql ("DELETE FROM report_host_details WHERE report_host IN"
24589        " (SELECT report_hosts.id FROM report_hosts"
24590        "  WHERE report_hosts.report = %llu"
24591        "  AND report_hosts.end_time = 0);",
24592        report);
24593 
24594   sql ("DELETE FROM report_hosts"
24595        " WHERE report = %llu"
24596        " AND end_time is NULL;",
24597        report);
24598 
24599   /* Clear and rebuild counts cache */
24600   if (setting_auto_cache_rebuild_int ())
24601     report_cache_counts (report, 1, 1, NULL);
24602   else
24603     report_clear_count_cache (report, 1, 1, NULL);
24604 }
24605 
24606 /**
24607  * @brief Compares two textual port representations, sorting descending
24608  * @brief by severity
24609  *
24610  * @param[in]  arg_one  First threat level.
24611  * @param[in]  arg_two  Second threat level.
24612  *
24613  * @return 1, 0 or -1 if first given severity is less than, equal to or greater
24614  *         than second.
24615  */
24616 static gint
compare_severity_desc(gconstpointer arg_one,gconstpointer arg_two)24617 compare_severity_desc (gconstpointer arg_one, gconstpointer arg_two)
24618 {
24619   double one_severity, two_severity;
24620   gchar *one = *((gchar**) arg_one);
24621   gchar *two = *((gchar**) arg_two);
24622   gint host;
24623 
24624   one += strlen (one) + 1;
24625   two += strlen (two) + 1;
24626   one_severity = g_strtod (one, NULL);
24627   two_severity = g_strtod (two, NULL);
24628 
24629   one += strlen (one) + 1;
24630   two += strlen (two) + 1;
24631   host = strcmp (one, two);
24632   if (host == 0)
24633     {
24634       if (one_severity > two_severity)
24635         return -1;
24636       else if (one_severity < two_severity)
24637         return 1;
24638       else
24639         {
24640           one = *((gchar**) arg_one);
24641           two = *((gchar**) arg_two);
24642           return strcmp (two, one);
24643         }
24644     }
24645   return host;
24646 }
24647 
24648 /**
24649  * @brief Compares two textual port representations, sorting descending
24650  * @brief by severity
24651  *
24652  * @param[in]  arg_one  First port.
24653  * @param[in]  arg_two  Second port.
24654  *
24655  * @return -1, 0 or 1 if first given severity is less than, equal to or greater
24656  *         than second.
24657  */
24658 static gint
compare_severity_asc(gconstpointer arg_one,gconstpointer arg_two)24659 compare_severity_asc (gconstpointer arg_one, gconstpointer arg_two)
24660 {
24661   double one_severity, two_severity;
24662   gchar *one = *((gchar**) arg_one);
24663   gchar *two = *((gchar**) arg_two);
24664   gint host;
24665 
24666   one += strlen (one) + 1;
24667   two += strlen (two) + 1;
24668   one_severity = g_strtod (one, NULL);
24669   two_severity = g_strtod (two, NULL);
24670 
24671   one += strlen (one) + 1;
24672   two += strlen (two) + 1;
24673   host = strcmp (one, two);
24674   if (host == 0)
24675     {
24676       if (one_severity < two_severity)
24677         return -1;
24678       else if (one_severity > two_severity)
24679         return 1;
24680       else
24681         {
24682           one = *((gchar**) arg_one);
24683           two = *((gchar**) arg_two);
24684           return strcmp (one, two);
24685         }
24686     }
24687   return host;
24688 }
24689 
24690 /**
24691  * @brief Some result info, for sorting.
24692  */
24693 struct result_buffer
24694 {
24695   gchar *host;                  ///< Host.
24696   gchar *port;                  ///< Port.
24697   gchar *severity;              ///< Severity.
24698   double severity_double;       ///< Severity.
24699 };
24700 
24701 /**
24702  * @brief Buffer host type.
24703  */
24704 typedef struct result_buffer result_buffer_t;
24705 
24706 /**
24707  * @brief Create a result buffer.
24708  *
24709  * @param[in]  host      Host.
24710  * @param[in]  port      Port.
24711  * @param[in]  severity  Severity.
24712  * @param[in]  severity_double  Severity.
24713  *
24714  * @return Freshly allocated result buffer.
24715  */
24716 static result_buffer_t*
result_buffer_new(const gchar * host,const gchar * port,const gchar * severity,double severity_double)24717 result_buffer_new (const gchar *host, const gchar *port, const gchar *severity,
24718                    double severity_double)
24719 {
24720   result_buffer_t *result_buffer;
24721   result_buffer = g_malloc (sizeof (result_buffer_t));
24722   result_buffer->host = g_strdup (host);
24723   result_buffer->port = g_strdup (port);
24724   result_buffer->severity = g_strdup (severity);
24725   result_buffer->severity_double = severity_double;
24726   return result_buffer;
24727 }
24728 
24729 /**
24730  * @brief Free a result buffer.
24731  *
24732  * @param[in]  result_buffer  Result buffer.
24733  */
24734 static void
result_buffer_free(result_buffer_t * result_buffer)24735 result_buffer_free (result_buffer_t *result_buffer)
24736 {
24737   g_free (result_buffer->host);
24738   g_free (result_buffer->port);
24739   g_free (result_buffer->severity);
24740   g_free (result_buffer);
24741 }
24742 
24743 /**
24744  * @brief Compares two buffered results, sorting by host, port then severity.
24745  *
24746  * @param[in]  arg_one  First result.
24747  * @param[in]  arg_two  Second result.
24748  *
24749  * @return -1, 0 or 1 if first given result is less than, equal to or greater
24750  *         than second.
24751  */
24752 static gint
compare_port_severity(gconstpointer arg_one,gconstpointer arg_two)24753 compare_port_severity (gconstpointer arg_one, gconstpointer arg_two)
24754 {
24755   int host;
24756   result_buffer_t *one, *two;
24757 
24758   one = *((result_buffer_t**) arg_one);
24759   two = *((result_buffer_t**) arg_two);
24760 
24761   host = strcmp (one->host, two->host);
24762   if (host == 0)
24763     {
24764       double severity_cmp;
24765       int port;
24766 
24767       port = strcmp (one->port, two->port);
24768       if (port != 0)
24769         return port;
24770 
24771       severity_cmp = two->severity_double - one->severity_double;
24772       if (severity_cmp > 0)
24773         return 1;
24774       else if (severity_cmp < 0)
24775         return -1;
24776       else
24777         return 0;
24778     }
24779   return host;
24780 }
24781 
24782 /** @todo Defined in gmp.c! */
24783 void buffer_results_xml (GString *, iterator_t *, task_t, int, int, int,
24784                          int, int, int, int, const char *, iterator_t *,
24785                          int, int, int);
24786 
24787 /**
24788  * @brief Comparison returns.
24789  */
24790 typedef enum
24791 {
24792   COMPARE_RESULTS_CHANGED,
24793   COMPARE_RESULTS_ERROR,
24794   COMPARE_RESULTS_GONE,
24795   COMPARE_RESULTS_NEW,
24796   COMPARE_RESULTS_SAME
24797 } compare_results_t;
24798 
24799 /**
24800  * @brief Return the sort order of two results.
24801  *
24802  * @param[in]  results        Iterator containing first result.
24803  * @param[in]  delta_results  Iterator containing second result.
24804  * @param[in]  sort_order     Whether to sort ascending or descending.
24805  * @param[in]  sort_field     Field to sort on, or NULL for "type".
24806  *
24807  * @return < 0 if first comes before second, 0 if equal, > 0 if first comes
24808  *         after second.
24809  */
24810 static compare_results_t
result_cmp(iterator_t * results,iterator_t * delta_results,int sort_order,const char * sort_field)24811 result_cmp (iterator_t *results, iterator_t *delta_results, int sort_order,
24812             const char* sort_field)
24813 {
24814   const char *host, *delta_host, *port, *delta_port;
24815   const char *nvt, *delta_nvt, *name, *delta_name, *descr, *delta_descr;
24816   int ret;
24817   double severity, delta_severity;
24818 
24819   if (sort_field == NULL)
24820     sort_field = "type";
24821 
24822   g_debug ("   delta: %s: sort_order: %i", __func__, sort_order);
24823   g_debug ("   delta: %s: sort_field: %s", __func__, sort_field);
24824 
24825   host = result_iterator_host (results);
24826   delta_host = result_iterator_host (delta_results);
24827 
24828   port = result_iterator_port (results);
24829   delta_port = result_iterator_port (delta_results);
24830 
24831   severity = result_iterator_severity_double (results);
24832   delta_severity = result_iterator_severity_double (delta_results);
24833 
24834   nvt = result_iterator_nvt_oid (results);
24835   delta_nvt = result_iterator_nvt_oid (delta_results);
24836 
24837   name = result_iterator_nvt_name (results);
24838   delta_name = result_iterator_nvt_name (delta_results);
24839 
24840   descr = result_iterator_descr (results);
24841   delta_descr = result_iterator_descr (delta_results);
24842 
24843   /* For delta reports to work correctly, the order must be the same as in
24844    * init_delta_iterators, except that description should not be checked
24845    * unless it is the sort_field.
24846    *
24847    * If description is not the sort_field it is checked after the result_cmp
24848    * in compare_results. */
24849 
24850   /* Check sort_field first, also using sort_order (0 is descending). */
24851   if (strcmp (sort_field, "host") == 0)
24852     {
24853       ret = collate_ip (NULL,
24854                         strlen (host), host, strlen (delta_host), delta_host);
24855       if (sort_order == 0)
24856         ret = -ret;
24857       g_debug ("   delta: %s: host (%s): %s VS %s (%i)",
24858                __func__, sort_order ? "desc" : "asc",
24859                host, delta_host, ret);
24860       if (ret)
24861         return ret;
24862     }
24863   else if (strcmp (sort_field, "port") == 0
24864            || strcmp (sort_field, "location") == 0)
24865     {
24866       ret = strcmp (port, delta_port);
24867       if (sort_order == 0)
24868         ret = -ret;
24869       g_debug ("   delta: %s: port (%s): %s VS %s (%i)",
24870                __func__, sort_order ? "desc" : "asc",
24871                port, delta_port, ret);
24872       if (ret)
24873         return ret;
24874     }
24875   else if (strcmp (sort_field, "severity") == 0)
24876     {
24877       if (severity > delta_severity)
24878         ret = sort_order ? 1 : -1;
24879       else if (severity < delta_severity)
24880         ret = sort_order ? -1 : 1;
24881       else
24882         ret = 0;
24883       g_debug ("   delta: %s: severity (%s): %f VS %f (%i)",
24884                __func__, sort_order ? "desc" : "asc",
24885                severity, delta_severity, ret);
24886       if (ret)
24887         return ret;
24888     }
24889   /* NVT OID, not name/vulnerability. */
24890   else if (strcmp (sort_field, "nvt") == 0)
24891     {
24892       ret = strcmp (nvt, delta_nvt);
24893       if (sort_order)
24894         ret = -ret;
24895       g_debug ("   delta: %s: nvt (%s): %s VS %s (%i)",
24896                __func__, sort_order ? "desc" : "asc",
24897                nvt, delta_nvt, ret);
24898       if (ret)
24899         return ret;
24900     }
24901   else if (strcmp (sort_field, "description") == 0)
24902     {
24903       ret = strcmp (descr, delta_descr);
24904       if (sort_order == 0)
24905         ret = -ret;
24906       g_debug ("   delta: %s: description (%s): %s VS %s (%i)",
24907                __func__, sort_order ? "desc" : "asc",
24908                descr, delta_descr, ret);
24909       if (ret)
24910         return ret;
24911     }
24912   else if (strcmp (sort_field, "type") == 0)
24913     {
24914       const char *type, *delta_type;
24915 
24916       type = result_iterator_type (results);
24917       delta_type = result_iterator_type (delta_results);
24918 
24919       ret = strcmp (type, delta_type);
24920       if (sort_order == 0)
24921         ret = -ret;
24922       g_debug ("   delta: %s: type (%s): %s VS %s (%i)",
24923                __func__, sort_order ? "desc" : "asc",
24924                descr, delta_descr, ret);
24925       if (ret)
24926         return ret;
24927     }
24928   else if (strcmp (sort_field, "original_type") == 0)
24929     {
24930       const char *type, *delta_type;
24931 
24932       type = result_iterator_original_type (results);
24933       delta_type = result_iterator_original_type (delta_results);
24934 
24935       ret = strcmp (type, delta_type);
24936       if (sort_order == 0)
24937         ret = -ret;
24938       g_debug ("   delta: %s: original_type (%s): %s VS %s (%i)",
24939                __func__, sort_order ? "desc" : "asc",
24940                descr, delta_descr, ret);
24941       if (ret)
24942         return ret;
24943     }
24944   else
24945     {
24946       /* Default to "vulnerability" (a.k.a "name") for unknown sort fields.
24947        *
24948        * Also done in print_report_xml_start, so this is just a safety check. */
24949       ret = strcmp (name ? name : "", delta_name ? delta_name : "");
24950       if (sort_order == 0)
24951         ret = -ret;
24952       if (ret)
24953         return ret;
24954     }
24955 
24956   /* Check remaining fields */
24957   if (strcmp (sort_field, "host"))
24958     {
24959       ret = collate_ip (NULL,
24960                         strlen (host), host, strlen (delta_host), delta_host);
24961       g_debug ("   delta: %s: host: %s VS %s (%i)",
24962                __func__, host, delta_host, ret);
24963       if (ret)
24964         return ret;
24965     }
24966   if (strcmp (sort_field, "port")
24967       && strcmp (sort_field, "location"))
24968     {
24969       ret = strcmp (port, delta_port);
24970       g_debug ("   delta: %s: port: %s VS %s (%i)",
24971                __func__, port, delta_port, ret);
24972       if (ret)
24973         return ret;
24974     }
24975   if (strcmp (sort_field, "severity"))
24976     {
24977       if (severity > delta_severity)
24978         ret = 1;
24979       else if (severity < delta_severity)
24980         ret = -1;
24981       else
24982         ret = 0;
24983       g_debug ("   delta: %s: severity: %f VS %f (%i)",
24984                __func__, severity, delta_severity, ret);
24985       if (ret)
24986         return ret;
24987     }
24988   if (strcmp (sort_field, "nvt"))
24989     {
24990       ret = strcmp (nvt, delta_nvt);
24991       g_debug ("   delta: %s: nvt: %s VS %s (%i)",
24992                __func__, nvt, delta_nvt, ret);
24993       if (ret)
24994         return ret;
24995     }
24996 
24997   return 0;
24998 }
24999 
25000 /**
25001  * @brief Test if two strings are equal, ignoring whitespace.
25002  *
25003  * @param[in]  one  First string.
25004  * @param[in]  two  Second string.
25005  *
25006  * @return 1 if equal, else 0.
25007  */
25008 static int
streq_ignore_ws(const gchar * one,const gchar * two)25009 streq_ignore_ws (const gchar *one, const gchar *two)
25010 {
25011   if (one == NULL)
25012     return two == NULL;
25013   if (two == NULL)
25014     return 0;
25015 
25016   while (1)
25017     {
25018       /* Skip space in both. */
25019       while (isspace (*one))
25020         one++;
25021       while (isspace (*two))
25022         two++;
25023 
25024       /* Check for end. */
25025       if (*one == '\0')
25026         break;
25027       if (*two == '\0')
25028         return 0;
25029 
25030       /* Compare. */
25031       if (*one != *two)
25032         return 0;
25033 
25034       /* Next. */
25035       one++;
25036       two++;
25037     }
25038   if (*two)
25039     return 0;
25040   return 1;
25041 }
25042 
25043 /**
25044  * @brief Compare two results.
25045  *
25046  * @param[in]  results        Iterator containing first result.
25047  * @param[in]  delta_results  Iterator containing second result.
25048  * @param[in]  sort_order     Whether to sort ascending or descending.
25049  * @param[in]  sort_field     Field to sort on, or NULL for "type".
25050  *
25051  * @return Result of comparison.
25052  */
25053 static compare_results_t
compare_results(iterator_t * results,iterator_t * delta_results,int sort_order,const char * sort_field)25054 compare_results (iterator_t *results, iterator_t *delta_results, int sort_order,
25055                  const char* sort_field)
25056 {
25057   int ret;
25058   const char *descr, *delta_descr;
25059 
25060   g_debug ("   delta: %s", __func__);
25061 
25062   ret = result_cmp (results, delta_results, sort_order, sort_field);
25063   if (ret > 0)
25064     /* The delta result sorts first, so it is new. */
25065     return COMPARE_RESULTS_NEW;
25066   if (ret < 0)
25067     /* The 'results' result sorts first, so it has gone. */
25068     return COMPARE_RESULTS_GONE;
25069 
25070   descr = result_iterator_descr (results);
25071   delta_descr = result_iterator_descr (delta_results);
25072 
25073   g_debug ("   delta: %s: descr: %s VS %s (%i)",
25074           __func__,
25075           descr ? descr : "NULL",
25076           delta_descr ? delta_descr : "NULL",
25077           (descr && delta_descr) ? strcmp (descr, delta_descr) : 0);
25078 
25079   /* This comparison ignores whitespace to match the diff output created by
25080    * strdiff in gmp.c.  The down side of this is that the comparison may be
25081    * affected by the locale.
25082    *
25083    * An alternate would be to use the strdiff result as the comparison, but
25084    * strdiff is only called for the results on the page (and not for the
25085    * rest of the results, which must also be compared for the counts).
25086    * Using strdiff for all results could also be slow, because it's running
25087    * the diff command. */
25088   if (descr && delta_descr && (streq_ignore_ws (descr, delta_descr) == 0))
25089     return COMPARE_RESULTS_CHANGED;
25090 
25091   return COMPARE_RESULTS_SAME;
25092 }
25093 
25094 /**
25095  * @brief Compare two results, optionally writing associated XML to a buffer.
25096  *
25097  * This is called with buffer NULL to compare results after the page limit
25098  * (filter keyword "max") is reached.  These results need to be compared to be
25099  * included in the counts.
25100  *
25101  * @param[in]  buffer         Buffer.  NULL to skip writing to buffer.
25102  * @param[in]  results        Iterator containing first result.
25103  * @param[in]  delta_results  Iterator containing second result.
25104  * @param[in]  task           Task associated with report.
25105  * @param[in]  notes              Whether to include notes.
25106  * @param[in]  notes_details      If notes, Whether to include details.
25107  * @param[in]  overrides          Whether to include overrides.
25108  * @param[in]  overrides_details  If overrides, Whether to include details.
25109  * @param[in]  sort_order     Whether to sort ascending or descending.
25110  * @param[in]  sort_field     Field to sort on, or NULL for "type".
25111  * @param[in]  changed        Whether to include changed results.
25112  * @param[in]  gone           Whether to include gone results.
25113  * @param[in]  new            Whether to include new results.
25114  * @param[in]  same           Whether to include same results.
25115  * @param[in]  max_results    Value to decrement if result is buffered.
25116  * @param[in]  first_result   Skip result and decrement if positive.
25117  * @param[in]  used           0 if used, 1 if skipped.
25118  * @param[in]  would_use      0 if would use (first_result aside), 1 if skipped.
25119  *
25120  * @return Result of comparison.
25121  */
25122 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)25123 compare_and_buffer_results (GString *buffer, iterator_t *results,
25124                             iterator_t *delta_results, task_t task, int notes,
25125                             int notes_details, int overrides,
25126                             int overrides_details, int sort_order,
25127                             const char* sort_field, int changed, int gone,
25128                             int new, int same, int *max_results,
25129                             int *first_result, int *used, int *would_use)
25130 {
25131   compare_results_t state;
25132   state = compare_results (results, delta_results, sort_order, sort_field);
25133   *used = 0;
25134   *would_use = 0;
25135   switch (state)
25136     {
25137       case COMPARE_RESULTS_CHANGED:
25138         if (changed)
25139           {
25140             *would_use = 1;
25141             if (*first_result)
25142               {
25143                 g_debug ("   delta: skip");
25144                 (*first_result)--;
25145                 break;
25146               }
25147             *used = 1;
25148             (*max_results)--;
25149             if (buffer)
25150               buffer_results_xml (buffer,
25151                                   results,
25152                                   task,
25153                                   notes,
25154                                   notes_details,
25155                                   overrides,
25156                                   overrides_details,
25157                                   0,
25158                                   0,
25159                                   0,
25160                                   "changed",
25161                                   delta_results,
25162                                   /* This is the only case that uses 1. */
25163                                   1,  /* Whether result is "changed". */
25164                                   -1,
25165                                   0);
25166           }
25167         break;
25168 
25169       case COMPARE_RESULTS_GONE:
25170         if (gone)
25171           {
25172             *would_use = 1;
25173             if (*first_result)
25174               {
25175                 g_debug ("   delta: skip");
25176                 (*first_result)--;
25177                 break;
25178               }
25179             *used = 1;
25180             (*max_results)--;
25181             if (buffer)
25182               buffer_results_xml (buffer,
25183                                   results,
25184                                   task,
25185                                   notes,
25186                                   notes_details,
25187                                   overrides,
25188                                   overrides_details,
25189                                   0,
25190                                   0,
25191                                   0,
25192                                   "gone",
25193                                   delta_results,
25194                                   0,
25195                                   -1,
25196                                   0);
25197           }
25198         break;
25199 
25200       case COMPARE_RESULTS_NEW:
25201         if (new)
25202           {
25203             *would_use = 1;
25204             if (*first_result)
25205               {
25206                 g_debug ("   delta: skip");
25207                 (*first_result)--;
25208                 break;
25209               }
25210             *used = 1;
25211             (*max_results)--;
25212             if (buffer)
25213               buffer_results_xml (buffer,
25214                                   delta_results,
25215                                   task,
25216                                   notes,
25217                                   notes_details,
25218                                   overrides,
25219                                   overrides_details,
25220                                   0,
25221                                   0,
25222                                   0,
25223                                   "new",
25224                                   delta_results,
25225                                   0,
25226                                   -1,
25227                                   0);
25228           }
25229         break;
25230 
25231       case COMPARE_RESULTS_SAME:
25232         if (same)
25233           {
25234             *would_use = 1;
25235             if (*first_result)
25236               {
25237                 g_debug ("   delta: skip");
25238                 (*first_result)--;
25239                 break;
25240               }
25241             *used = 1;
25242             (*max_results)--;
25243             if (buffer)
25244               buffer_results_xml (buffer,
25245                                   results,
25246                                   task,
25247                                   notes,
25248                                   notes_details,
25249                                   overrides,
25250                                   overrides_details,
25251                                   0,
25252                                   0,
25253                                   0,
25254                                   "same",
25255                                   delta_results,
25256                                   0,
25257                                   -1,
25258                                   0);
25259           }
25260         break;
25261 
25262       default:
25263         return COMPARE_RESULTS_ERROR;
25264     }
25265 
25266   return state;
25267 }
25268 
25269 /**
25270  * @brief Write XML to a file or close stream and return.
25271  *
25272  * @param[in]   stream  Stream to write to.
25273  * @param[in]   xml     XML.
25274  */
25275 #define PRINT_XML(stream, xml)                                               \
25276   do                                                                         \
25277     {                                                                        \
25278       if (fprintf (stream, "%s", xml) < 0)                                   \
25279         {                                                                    \
25280           fclose (stream);                                                   \
25281           return -1;                                                         \
25282         }                                                                    \
25283     }                                                                        \
25284   while (0)
25285 
25286 /**
25287  * @brief Add a port to a port tree.
25288  *
25289  * @param[in]  ports    The tree.
25290  * @param[in]  results  Result iterator on result whose port to add.
25291  */
25292 static void
add_port(GTree * ports,iterator_t * results)25293 add_port (GTree *ports, iterator_t *results)
25294 {
25295   const char *port, *host;
25296   double *old_severity, *severity;
25297   GTree *host_ports;
25298 
25299   /* Ensure there's an inner tree for the host. */
25300 
25301   host = result_iterator_host (results);
25302   host_ports = g_tree_lookup (ports, host);
25303   if (host_ports == NULL)
25304     {
25305       host_ports = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, g_free,
25306                                     g_free);
25307       g_tree_insert (ports, g_strdup (host), host_ports);
25308     }
25309 
25310   /* Ensure the highest threat is recorded for the port in the inner tree. */
25311 
25312   port = result_iterator_port (results);
25313   severity = g_malloc (sizeof (double));
25314   *severity = result_iterator_severity_double (results);
25315 
25316   old_severity = g_tree_lookup (host_ports, port);
25317   g_debug ("   delta: %s: adding %s severity %1.1f on host %s", __func__,
25318           port, *severity, host);
25319   if (old_severity == NULL)
25320     g_tree_insert (host_ports, g_strdup (port), severity);
25321   else if (severity > old_severity)
25322     {
25323       *old_severity = *severity;
25324       g_free (severity);
25325     }
25326   else
25327     {
25328       g_free (severity);
25329     }
25330 }
25331 
25332 /**
25333  * @brief Print delta host ports.
25334  *
25335  * @param[in]  key     Port.
25336  * @param[in]  value   Threat.
25337  * @param[in]  data    Host and stream.
25338  *
25339  * @return Always FALSE.
25340  */
25341 static gboolean
print_host_port(gpointer key,gpointer value,gpointer data)25342 print_host_port (gpointer key, gpointer value, gpointer data)
25343 {
25344   gpointer *host_and_stream;
25345   host_and_stream = (gpointer*) data;
25346   g_debug ("   delta: %s: host %s port %s", __func__,
25347           (gchar*) host_and_stream[0], (gchar*) key);
25348   fprintf ((FILE*) host_and_stream[1],
25349            "<port>"
25350            "<host>%s</host>"
25351            "%s"
25352            "<severity>%1.1f</severity>"
25353            "<threat>%s</threat>"
25354            "</port>",
25355            (gchar*) host_and_stream[0],
25356            (gchar*) key,
25357            *((double*) value),
25358            severity_to_level (*((double*) value), 0));
25359   return FALSE;
25360 }
25361 
25362 /**
25363  * @brief Print delta ports.
25364  *
25365  * @param[in]  key     Host.
25366  * @param[in]  value   Port tree.
25367  * @param[in]  stream  Stream.
25368  *
25369  * @return Always FALSE.
25370  */
25371 static gboolean
print_host_ports(gpointer key,gpointer value,gpointer stream)25372 print_host_ports (gpointer key, gpointer value, gpointer stream)
25373 {
25374   gpointer host_and_stream[2];
25375   host_and_stream[0] = key;
25376   host_and_stream[1] = stream;
25377   g_debug ("   delta: %s: host %s", __func__, (gchar*) key);
25378   g_tree_foreach ((GTree*) value, print_host_port, host_and_stream);
25379   return FALSE;
25380 }
25381 
25382 /**
25383  * @brief Add port to ports array.
25384  *
25385  * @param[in]  key     Port.
25386  * @param[in]  value   Threat.
25387  * @param[in]  ports   Ports array.
25388  *
25389  * @return Always FALSE.
25390  */
25391 static gboolean
array_add_port(gpointer key,gpointer value,gpointer ports)25392 array_add_port (gpointer key, gpointer value, gpointer ports)
25393 {
25394   gpointer *port_threat;
25395   port_threat = g_malloc (2 * sizeof (gpointer));
25396   port_threat[0] = key;
25397   port_threat[1] = value;
25398   array_add ((array_t*) ports, port_threat);
25399   return FALSE;
25400 }
25401 
25402 /**
25403  * @brief Print delta ports, in descending order.
25404  *
25405  * @param[in]  key     Host.
25406  * @param[in]  value   Port tree.
25407  * @param[in]  stream  Stream.
25408  *
25409  * @return Always FALSE.
25410  */
25411 static gboolean
print_host_ports_desc(gpointer key,gpointer value,gpointer stream)25412 print_host_ports_desc (gpointer key, gpointer value, gpointer stream)
25413 {
25414   guint index;
25415   array_t *ports;
25416 
25417   g_debug ("   delta: %s: host %s", __func__, (gchar*) key);
25418 
25419   /* Convert tree to array. */
25420 
25421   ports = make_array ();
25422   g_tree_foreach ((GTree*) value, array_add_port, ports);
25423 
25424   /* Print the array backwards. */
25425 
25426   index = ports->len;
25427   while (index--)
25428     {
25429       gpointer *port_threat;
25430       port_threat = g_ptr_array_index (ports, index);
25431       fprintf ((FILE*) stream,
25432                "<port>"
25433                "<host>%s</host>"
25434                "%s"
25435                "<severity>%1.1f</severity>"
25436                "<threat>%s</threat>"
25437                "</port>",
25438                (gchar*) key,
25439                (gchar*) port_threat[0],
25440                *((double*) port_threat[1]),
25441                severity_to_level (*((double*) port_threat[1]), 0));
25442     }
25443 
25444   array_free (ports);
25445 
25446   return FALSE;
25447 }
25448 
25449 /**
25450  * @brief Compare port severities, ascending.
25451  *
25452  * @param[in]  one  First.
25453  * @param[in]  two  Second.
25454  *
25455  * @return 1 one greater, -1 two greater, 0 equal.
25456  */
25457 static gint
compare_ports_severity(gconstpointer one,gconstpointer two)25458 compare_ports_severity (gconstpointer one, gconstpointer two)
25459 {
25460   gpointer *port_threat_one, *port_threat_two;
25461   port_threat_one = *((gpointer**) one);
25462   port_threat_two = *((gpointer**) two);
25463   if (*((double*) port_threat_one[1]) > *((double*) port_threat_two[1]))
25464     return 1;
25465   else if (*((double*) port_threat_one[1]) < *((double*) port_threat_two[1]))
25466     return -1;
25467   else
25468     return 0;
25469 }
25470 
25471 /**
25472  * @brief Compare port severities, descending.
25473  *
25474  * @param[in]  one  First.
25475  * @param[in]  two  Second.
25476  *
25477  * @return 1 one less, -1 two less, 0 equal.
25478  */
25479 static gint
compare_ports_severity_desc(gconstpointer one,gconstpointer two)25480 compare_ports_severity_desc (gconstpointer one, gconstpointer two)
25481 {
25482   gpointer *port_threat_one, *port_threat_two;
25483   port_threat_one = *((gpointer**) one);
25484   port_threat_two = *((gpointer**) two);
25485   if (*((double*) port_threat_one[1]) < *((double*) port_threat_two[1]))
25486     return 1;
25487   else if (*((double*) port_threat_one[1]) > *((double*) port_threat_two[1]))
25488     return -1;
25489   else
25490     return 0;
25491 }
25492 
25493 /**
25494  * @brief Print delta ports, ordering by severity.
25495  *
25496  * @param[in]  key        Host.
25497  * @param[in]  value      Port tree.
25498  * @param[in]  stream     Stream.
25499  * @param[in]  ascending  Ascending or descending.
25500  *
25501  * @return Always FALSE.
25502  */
25503 static gboolean
print_host_ports_by_severity(gpointer key,gpointer value,gpointer stream,int ascending)25504 print_host_ports_by_severity (gpointer key, gpointer value, gpointer stream,
25505                               int ascending)
25506 {
25507   guint index, len;
25508   array_t *ports;
25509 
25510   g_debug ("   delta: %s: host %s", __func__, (gchar*) key);
25511 
25512   /* Convert tree to array. */
25513 
25514   ports = make_array ();
25515   g_tree_foreach ((GTree*) value, array_add_port, ports);
25516 
25517   /* Sort the array. */
25518 
25519   if (ascending)
25520     g_ptr_array_sort (ports, compare_ports_severity);
25521   else
25522     g_ptr_array_sort (ports, compare_ports_severity_desc);
25523 
25524   /* Print the sorted array. */
25525 
25526   index = 0;
25527   len = ports->len;
25528   while (index < len)
25529     {
25530       gpointer *port_threat;
25531       port_threat = g_ptr_array_index (ports, index);
25532       fprintf ((FILE*) stream,
25533                "<port>"
25534                "<host>%s</host>"
25535                "%s"
25536                "<severity>%1.1f</severity>"
25537                "<threat>%s</threat>"
25538                "</port>",
25539                (gchar*) key,
25540                (gchar*) port_threat[0],
25541                *((double*) port_threat[1]),
25542                severity_to_level (*((double*) port_threat[1]), 0));
25543       index++;
25544     }
25545 
25546   array_free (ports);
25547 
25548   return FALSE;
25549 }
25550 
25551 /**
25552  * @brief Print delta ports, ordering by severity descending.
25553  *
25554  * @param[in]  key     Host.
25555  * @param[in]  value   Port tree.
25556  * @param[in]  stream  Stream.
25557  *
25558  * @return Always FALSE.
25559  */
25560 static gboolean
print_host_ports_by_severity_desc(gpointer key,gpointer value,gpointer stream)25561 print_host_ports_by_severity_desc (gpointer key, gpointer value,
25562                                    gpointer stream)
25563 {
25564   return print_host_ports_by_severity (key, value, stream, 0);
25565 }
25566 
25567 /**
25568  * @brief Print delta ports, ordering by severity ascending.
25569  *
25570  * @param[in]  key     Host.
25571  * @param[in]  value   Port tree.
25572  * @param[in]  stream  Stream.
25573  *
25574  * @return Always FALSE.
25575  */
25576 static gboolean
print_host_ports_by_severity_asc(gpointer key,gpointer value,gpointer stream)25577 print_host_ports_by_severity_asc (gpointer key, gpointer value,
25578                                   gpointer stream)
25579 {
25580   return print_host_ports_by_severity (key, value, stream, 1);
25581 }
25582 
25583 /**
25584  * @brief Free delta host ports.
25585  *
25586  * @param[in]  host_ports  Ports.
25587  * @param[in]  dummy       Dummy.
25588  *
25589  * @return Always FALSE.
25590  */
25591 static gboolean
free_host_ports(GTree * host_ports,gpointer dummy)25592 free_host_ports (GTree *host_ports, gpointer dummy)
25593 {
25594   g_tree_destroy (host_ports);
25595   return FALSE;
25596 }
25597 
25598 /**
25599  * @brief Get N'th last report_host given a host.
25600  *
25601  * The last report_host is at position 1, the second last at position 2, and
25602  * so on.
25603  *
25604  * @param[in]  host         Host.
25605  * @param[in]  report_host  Report host.
25606  * @param[in]  position     Position from end.
25607  *
25608  * @return N'th last report_host.
25609  */
25610 gboolean
host_nthlast_report_host(const char * host,report_host_t * report_host,int position)25611 host_nthlast_report_host (const char *host, report_host_t *report_host,
25612                           int position)
25613 {
25614   gchar *quoted_host;
25615 
25616   assert (current_credentials.uuid);
25617 
25618   if (position == 0)
25619     position = 1;
25620 
25621   quoted_host = sql_quote (host);
25622   switch (sql_int64 (report_host,
25623                      "SELECT id FROM report_hosts WHERE host = '%s'"
25624                      " AND user_owns ('task',"
25625                      "                (SELECT reports.task FROM reports"
25626                      "                 WHERE reports.id"
25627                      "                       = report_hosts.report))"
25628                      " AND (SELECT tasks.hidden FROM tasks, reports"
25629                      "      WHERE reports.task = tasks.id"
25630                      "      AND reports.id = report_hosts.report)"
25631                      "     = 0"
25632                      " AND (SELECT value FROM task_preferences, tasks,"
25633                      "                        reports"
25634                      "      WHERE reports.task = tasks.id"
25635                      "      AND reports.id = report_hosts.report"
25636                      "      AND task_preferences.task = tasks.id"
25637                      "      AND task_preferences.name = 'in_assets')"
25638                      "     = 'yes'"
25639                      " AND report_hosts.end_time > 0"
25640                      " AND NOT EXISTS (SELECT * FROM report_host_details"
25641                      "                 WHERE report_host = report_hosts.id"
25642                      "                 AND name = 'CVE Scan')"
25643                      " ORDER BY id DESC LIMIT 1 OFFSET %i;",
25644                      quoted_host,
25645                      position - 1))
25646     {
25647       case 0:
25648         break;
25649       case 1:        /* Too few rows in result of query. */
25650         *report_host = 0;
25651         break;
25652       default:       /* Programming error. */
25653         assert (0);
25654       case -1:
25655         return TRUE;
25656         break;
25657     }
25658 
25659   g_free (quoted_host);
25660   return FALSE;
25661 }
25662 
25663 /**
25664  * @brief Count a report's total number of hosts.
25665  *
25666  * @param[in]  report  Report.
25667  *
25668  * @return Host count.
25669  */
25670 int
report_host_count(report_t report)25671 report_host_count (report_t report)
25672 {
25673   return sql_int ("SELECT count (DISTINCT id) FROM report_hosts"
25674                   " WHERE report = %llu;",
25675                   report);
25676 }
25677 
25678 /**
25679  * @brief Count a report's total number of hosts with results.
25680  *
25681  * @param[in]   report         Report.
25682  * @param[in]   min_qod        Minimum QoD of results to count.
25683  *
25684  * @return The number of hosts with results
25685  */
25686 int
report_result_host_count(report_t report,int min_qod)25687 report_result_host_count (report_t report, int min_qod)
25688 {
25689   return sql_int ("SELECT count (DISTINCT id) FROM report_hosts"
25690                   " WHERE report_hosts.report = %llu"
25691                   "   AND EXISTS (SELECT * FROM results"
25692                   "               WHERE results.host = report_hosts.host"
25693                   "                 AND results.qod >= %d)",
25694                   report,
25695                   min_qod);
25696 }
25697 
25698 /**
25699  * @brief Count a report's total number of tcp/ip ports.
25700  *
25701  * Ignores port entries in "general/..." form.
25702  *
25703  * @param[in]  report  Report.
25704  *
25705  * @return Ports count.
25706  */
25707 static int
report_port_count(report_t report)25708 report_port_count (report_t report)
25709 {
25710   return sql_int ("SELECT count (DISTINCT port) FROM results"
25711                   " WHERE report = %llu AND port != ''"
25712                   "  AND port NOT %s 'general/%%';",
25713                   report,
25714                   sql_ilike_op ());
25715 }
25716 
25717 /**
25718  * @brief Count a report's total number of closed cves.
25719  *
25720  * @param[in]  report  Report.
25721  *
25722  * @return Closed CVE count.
25723  */
25724 static int
report_closed_cve_count(report_t report)25725 report_closed_cve_count (report_t report)
25726 {
25727   return sql_int (" SELECT count(id) FROM nvts"
25728                   " WHERE cve != ''"
25729                   " AND family IN (" LSC_FAMILY_LIST ")"
25730                   " AND oid IN"
25731                   " (SELECT source_name FROM report_host_details"
25732                   "  WHERE report_host IN "
25733                   "   (SELECT id FROM report_hosts WHERE report = %llu)"
25734                   "  AND name = 'EXIT_CODE'"
25735                   "  AND value = 'EXIT_NOTVULN');",
25736                   report);
25737 }
25738 
25739 /**
25740  * @brief Count a report's total number of vulnerabilities.
25741  *
25742  * @param[in]  report  Report.
25743  *
25744  * @return Vulnerabilities count.
25745  */
25746 static int
report_vuln_count(report_t report)25747 report_vuln_count (report_t report)
25748 {
25749   return sql_int ("SELECT count (DISTINCT nvt) FROM results"
25750                   " WHERE report = %llu"
25751                   " AND severity != " G_STRINGIFY (SEVERITY_ERROR) ";",
25752                   report);
25753 }
25754 
25755 /**
25756  * @brief Count a report's total number of detected Operating Systems.
25757  *
25758  * @param[in]  report  Report.
25759  *
25760  * @return OS count.
25761  */
25762 static int
report_os_count(report_t report)25763 report_os_count (report_t report)
25764 {
25765   return sql_int ("SELECT count (DISTINCT value) FROM report_host_details"
25766                   " WHERE report_host IN"
25767                   "  (SELECT id from report_hosts WHERE report = %llu)"
25768                   "  AND name = 'best_os_cpe';",
25769                   report);
25770 }
25771 
25772 /**
25773  * @brief Count a report's total number of detected Apps.
25774  *
25775  * @param[in]  report  Report.
25776  *
25777  * @return App count.
25778  */
25779 static int
report_app_count(report_t report)25780 report_app_count (report_t report)
25781 {
25782   return sql_int ("SELECT count (DISTINCT value) FROM report_host_details"
25783                   " WHERE report_host IN"
25784                   "  (SELECT id from report_hosts WHERE report = %llu)"
25785                   "  AND name = 'App';",
25786                   report);
25787 }
25788 
25789 /**
25790  * @brief Count a report's total number of found SSL Certificates.
25791  *
25792  * @param[in]  report  Report.
25793  *
25794  * @return SSL Certificates count.
25795  */
25796 static int
report_ssl_cert_count(report_t report)25797 report_ssl_cert_count (report_t report)
25798 {
25799   return sql_int ("SELECT count (DISTINCT id) FROM report_host_details"
25800                   " WHERE report_host IN"
25801                   "  (SELECT id from report_hosts WHERE report = %llu)"
25802                   "  AND name = 'SSLInfo';",
25803                   report);
25804 }
25805 
25806 /**
25807  * @brief Count a report's total number of error messages.
25808  *
25809  * @param[in]  report  Report.
25810  *
25811  * @return Error Messages count.
25812  */
25813 static int
report_error_count(report_t report)25814 report_error_count (report_t report)
25815 {
25816   return sql_int ("SELECT count (id) FROM results"
25817                   " WHERE report = %llu and type = 'Error Message';",
25818                   report);
25819 }
25820 
25821 /**
25822  * @brief Get a list string of finished hosts in a report.
25823  *
25824  * @param[in]  report  The report to get the finished hosts from.
25825  *
25826  * @return String containing finished hosts as comma separated list.
25827  */
25828 char *
report_finished_hosts_str(report_t report)25829 report_finished_hosts_str (report_t report)
25830 {
25831   char *ret;
25832 
25833   ret = sql_string ("SELECT string_agg (host, ',' ORDER BY host)"
25834                     " FROM report_hosts"
25835                     " WHERE report = %llu"
25836                     "   AND end_time != 0;",
25837                     report);
25838 
25839   return ret;
25840 }
25841 
25842 /**
25843  * @brief Write report host detail to file stream.
25844  *
25845  * On error close stream.
25846  *
25847  * @param[in]   stream    Stream to write to.
25848  * @param[in]   details   Report host details iterator.
25849  * @param[in]   lean      Whether to return reduced info.
25850  *
25851  * @return 0 success, -1 error.
25852  */
25853 static int
print_report_host_detail(FILE * stream,iterator_t * details,int lean)25854 print_report_host_detail (FILE *stream, iterator_t *details, int lean)
25855 {
25856   const char *name, *value;
25857 
25858   name = report_host_details_iterator_name (details);
25859   value = report_host_details_iterator_value (details);
25860 
25861   if (lean)
25862     {
25863       /* Skip certain host details. */
25864 
25865       if (strcmp (name, "EXIT_CODE") == 0
25866           && strcmp (value, "EXIT_NOTVULN") == 0)
25867         return 0;
25868 
25869       if (strcmp (name, "scanned_with_scanner") == 0)
25870         return 0;
25871 
25872       if (strcmp (name, "scanned_with_feedtype") == 0)
25873         return 0;
25874 
25875       if (strcmp (name, "scanned_with_feedversion") == 0)
25876         return 0;
25877 
25878       if (strcmp (name, "OS") == 0)
25879         return 0;
25880 
25881       if (strcmp (name, "traceroute") == 0)
25882         return 0;
25883     }
25884 
25885   PRINT (stream,
25886         "<detail>"
25887         "<name>%s</name>"
25888         "<value>%s</value>"
25889         "<source>",
25890         name,
25891         value);
25892 
25893   if (lean == 0)
25894     PRINT (stream,
25895            "<type>%s</type>",
25896            report_host_details_iterator_source_type (details));
25897 
25898   PRINT (stream,
25899         "<name>%s</name>",
25900         report_host_details_iterator_source_name (details));
25901 
25902   if (report_host_details_iterator_source_desc (details)
25903       && strlen (report_host_details_iterator_source_desc (details)))
25904     PRINT (stream,
25905            "<description>%s</description>",
25906            report_host_details_iterator_source_desc (details));
25907   else if (lean == 0)
25908     PRINT (stream,
25909            "<description></description>");
25910 
25911   PRINT (stream,
25912         "</source>");
25913 
25914   if (report_host_details_iterator_extra (details)
25915       && strlen (report_host_details_iterator_extra (details)))
25916     PRINT (stream,
25917            "<extra>%s</extra>",
25918            report_host_details_iterator_extra (details));
25919   else if (lean == 0)
25920     PRINT (stream,
25921            "<extra></extra>");
25922 
25923   PRINT (stream,
25924         "</detail>");
25925 
25926   return 0;
25927 }
25928 
25929 /**
25930  * @brief Print the XML for a report's host details to a file stream.
25931  * @param[in]  report_host  The report host.
25932  * @param[in]  stream       File stream to write to.
25933  * @param[in]  lean         Report host details iterator.
25934  *
25935  * @return 0 on success, -1 error.
25936  */
25937 static int
print_report_host_details_xml(report_host_t report_host,FILE * stream,int lean)25938 print_report_host_details_xml (report_host_t report_host, FILE *stream,
25939                                int lean)
25940 {
25941   iterator_t details;
25942 
25943   init_report_host_details_iterator
25944    (&details, report_host);
25945   while (next (&details))
25946     if (print_report_host_detail (stream, &details, lean))
25947       return -1;
25948   cleanup_iterator (&details);
25949 
25950   return 0;
25951 }
25952 
25953 /**
25954  * @brief Write report error message to file stream.
25955  *
25956  * @param[in]   stream      Stream to write to.
25957  * @param[in]   errors      Pointer to report error messages iterator.
25958  * @param[in]   asset_id    Asset ID.
25959  */
25960 #define PRINT_REPORT_ERROR(stream, errors, asset_id)                       \
25961   do                                                                       \
25962     {                                                                      \
25963       PRINT (stream,                                                       \
25964              "<error>"                                                     \
25965              "<host>"                                                      \
25966              "%s"                                                          \
25967              "<asset asset_id=\"%s\"/>"                                    \
25968              "</host>"                                                     \
25969              "<port>%s</port>"                                             \
25970              "<description>%s</description>"                               \
25971              "<nvt oid=\"%s\">"                                            \
25972              "<type>nvt</type>"                                            \
25973              "<name>%s</name>"                                             \
25974              "<cvss_base>%s</cvss_base>"                                   \
25975              "</nvt>"                                                      \
25976              "<scan_nvt_version>%s</scan_nvt_version>"                     \
25977              "<severity>%s</severity>"                                     \
25978              "</error>",                                                   \
25979              report_errors_iterator_host (errors) ?: "",                   \
25980              asset_id ? asset_id : "",                                     \
25981              report_errors_iterator_port (errors),                         \
25982              report_errors_iterator_desc (errors),                         \
25983              report_errors_iterator_nvt_oid (errors),                      \
25984              report_errors_iterator_nvt_name (errors),                     \
25985              report_errors_iterator_nvt_cvss (errors),                     \
25986              report_errors_iterator_scan_nvt_version (errors),             \
25987              report_errors_iterator_severity (errors));                    \
25988     }                                                                      \
25989   while (0)
25990 
25991 /**
25992  * @brief Print the XML for a report's error messages to a file stream.
25993  * @param[in]  report   The report.
25994  * @param[in]  stream   File stream to write to.
25995  *
25996  * @return 0 on success, -1 error.
25997  */
25998 static int
print_report_errors_xml(report_t report,FILE * stream)25999 print_report_errors_xml (report_t report, FILE *stream)
26000 {
26001   iterator_t errors;
26002 
26003   init_report_errors_iterator
26004    (&errors, report);
26005 
26006   PRINT (stream, "<errors><count>%i</count>", report_error_count (report));
26007   while (next (&errors))
26008     {
26009       char *asset_id;
26010 
26011       asset_id = result_host_asset_id (report_errors_iterator_host (&errors),
26012                                        report_errors_iterator_result (&errors));
26013       PRINT_REPORT_ERROR (stream, &errors, asset_id);
26014       free (asset_id);
26015     }
26016   cleanup_iterator (&errors);
26017   PRINT (stream, "</errors>");
26018 
26019   return 0;
26020 }
26021 
26022 /**
26023  * @brief Print the XML for a report port summary to a file.
26024  *
26025  * @param[in]  report           The report.
26026  * @param[in]  out              File stream.
26027  * @param[in]  get              Result get data.
26028  * @param[in]  first_result     The result to start from.  The results are 0
26029  *                              indexed.
26030  * @param[in]  max_results      The maximum number of results returned.
26031  * @param[in]  sort_order       Whether to sort ascending or descending.
26032  * @param[in]  sort_field       Field to sort on.
26033  * @param[out] host_ports       Hash table for counting ports per host.
26034  * @param[in,out] results       Result iterator.  For caller to reuse.
26035  *
26036  * @return 0 on success, -1 error.
26037  */
26038 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)26039 print_report_port_xml (report_t report, FILE *out, const get_data_t *get,
26040                        int first_result, int max_results,
26041                        int sort_order, const char *sort_field,
26042                        GHashTable *host_ports, iterator_t *results)
26043 {
26044   result_buffer_t *last_item;
26045   GArray *ports = g_array_new (TRUE, FALSE, sizeof (gchar*));
26046 
26047   init_result_get_iterator (results, get, report, NULL, NULL);
26048 
26049   /* Buffer the results, removing duplicates. */
26050 
26051   last_item = NULL;
26052   while (next (results))
26053     {
26054       const char *port = result_iterator_port (results);
26055       const char *host = result_iterator_host (results);
26056       double cvss_double;
26057 
26058       cvss_double = result_iterator_severity_double (results);
26059 
26060       if (last_item
26061           && strcmp (port, last_item->port) == 0
26062           && strcmp (host, last_item->host) == 0
26063           && last_item->severity_double <= cvss_double)
26064         {
26065           last_item->severity_double = cvss_double;
26066           g_free (last_item->severity);
26067           last_item->severity = g_strdup (result_iterator_severity (results));
26068         }
26069       else
26070         {
26071           const char *cvss;
26072           result_buffer_t *item;
26073 
26074           cvss = result_iterator_severity (results);
26075           if (cvss == NULL)
26076             {
26077               cvss_double = 0.0;
26078               cvss = "0.0";
26079             }
26080           item = result_buffer_new (host, port, cvss, cvss_double);
26081           g_array_append_val (ports, item);
26082           last_item = item;
26083         }
26084 
26085     }
26086 
26087   /* Handle sorting by threat and ROWID. */
26088 
26089   if (sort_field == NULL || strcmp (sort_field, "port"))
26090     {
26091       int index, length;
26092 
26093       /** @todo Sort by ROWID if was requested. */
26094 
26095       /* Sort by port then severity. */
26096 
26097       g_array_sort (ports, compare_port_severity);
26098 
26099       /* Remove duplicates. */
26100 
26101       last_item = NULL;
26102       for (index = 0, length = ports->len; index < length; index++)
26103         {
26104           result_buffer_t *item;
26105 
26106           item = g_array_index (ports, result_buffer_t*, index);
26107           if (last_item
26108               && (strcmp (item->port, last_item->port) == 0)
26109               && (strcmp (item->host, last_item->host) == 0))
26110             {
26111               if (item->severity_double > last_item->severity_double)
26112                 {
26113                   gchar *severity;
26114                   severity = last_item->severity;
26115                   last_item->severity = item->severity;
26116                   item->severity = severity;
26117                   last_item->severity_double = item->severity_double;
26118                 }
26119               g_array_remove_index (ports, index);
26120               length = ports->len;
26121               index--;
26122             }
26123           else
26124             last_item = item;
26125         }
26126 
26127       /* Sort by severity. */
26128 
26129       if (sort_order)
26130         g_array_sort (ports, compare_severity_asc);
26131       else
26132         g_array_sort (ports, compare_severity_desc);
26133     }
26134 
26135   /* Write to file from the buffer. */
26136 
26137   PRINT (out,
26138            "<ports"
26139            " start=\"%i\""
26140            " max=\"%i\">"
26141            "<count>%i</count>",
26142            /* Add 1 for 1 indexing. */
26143            first_result + 1,
26144            max_results,
26145            report_port_count (report));
26146   {
26147     result_buffer_t *item;
26148     int index = 0;
26149 
26150     while ((item = g_array_index (ports, result_buffer_t*, index++)))
26151       {
26152         int host_port_count
26153               = GPOINTER_TO_INT (g_hash_table_lookup (host_ports, item->host));
26154 
26155         PRINT (out,
26156                "<port>"
26157                "<host>%s</host>"
26158                "%s"
26159                "<severity>%1.1f</severity>"
26160                "<threat>%s</threat>"
26161                "</port>",
26162                item->host,
26163                item->port,
26164                item->severity_double,
26165                severity_to_level (g_strtod (item->severity, NULL), 0));
26166 
26167         if (g_str_has_prefix(item->port, "general/") == FALSE)
26168           {
26169             g_hash_table_replace (host_ports,
26170                                   g_strdup (item->host),
26171                                   GINT_TO_POINTER (host_port_count + 1));
26172           }
26173         result_buffer_free (item);
26174       }
26175     g_array_free (ports, TRUE);
26176   }
26177   PRINT (out, "</ports>");
26178 
26179   return 0;
26180 }
26181 
26182 /**
26183  * @brief Calculate the progress of a report.
26184  *
26185  * @param[in]  report     Report.
26186  *
26187  * @return Progress.
26188  */
26189 int
report_progress(report_t report)26190 report_progress (report_t report)
26191 {
26192   if (report == 0)
26193     return -1;
26194 
26195   return report_slave_progress (report);
26196 }
26197 
26198 /**
26199  * @brief Restore original TZ.
26200  *
26201  * @param[in]  zone             Only revert if this is at least one character.
26202  *                               Freed here always.
26203  * @param[in]  tz               Original TZ.  Freed here if revert occurs.
26204  * @param[in]  old_tz_override  Original tz_override.  Freed here on revert.
26205  *
26206  * @return 0 success, -1 error.
26207  */
26208 static int
tz_revert(gchar * zone,char * tz,char * old_tz_override)26209 tz_revert (gchar *zone, char *tz, char *old_tz_override)
26210 {
26211   if (zone && strlen (zone))
26212     {
26213       gchar *quoted_old_tz_override;
26214       /* Revert to stored TZ. */
26215       if (tz)
26216         {
26217           if (setenv ("TZ", tz, 1) == -1)
26218             {
26219               g_warning ("%s: Failed to switch to original TZ", __func__);
26220               g_free (tz);
26221               g_free (zone);
26222               free (old_tz_override);
26223               return -1;
26224             }
26225         }
26226       else
26227         unsetenv ("TZ");
26228 
26229       quoted_old_tz_override = sql_insert (old_tz_override);
26230       sql ("SET SESSION \"gvmd.tz_override\" = %s;",
26231            quoted_old_tz_override);
26232       g_free (quoted_old_tz_override);
26233 
26234       free (old_tz_override);
26235       g_free (tz);
26236     }
26237   g_free (zone);
26238   return 0;
26239 }
26240 
26241 /**
26242  * @brief Print the XML for a report to a file.
26243  *
26244  * @param[in]  host_summary_buffer  Summary.
26245  * @param[in]  host                 Host.
26246  * @param[in]  start_iso            Start time, in ISO format.
26247  * @param[in]  end_iso              End time, in ISO format.
26248  */
26249 static void
host_summary_append(GString * host_summary_buffer,const char * host,const char * start_iso,const char * end_iso)26250 host_summary_append (GString *host_summary_buffer, const char *host,
26251                      const char *start_iso, const char *end_iso)
26252 {
26253   if (host_summary_buffer)
26254     {
26255       char start[200], end[200];
26256 
26257       if (start_iso)
26258         {
26259           struct tm start_tm;
26260 
26261           memset (&start_tm, 0, sizeof (struct tm));
26262           #if !defined(__GLIBC__)
26263             if (strptime (start_iso, "%Y-%m-%dT%H:%M:%S", &start_tm) == NULL)
26264           #else
26265             if (strptime (start_iso, "%FT%H:%M:%S", &start_tm) == NULL)
26266           #endif
26267             {
26268               g_warning ("%s: Failed to parse start", __func__);
26269               return;
26270             }
26271 
26272           if (strftime (start, 200, "%b %d, %H:%M:%S", &start_tm) == 0)
26273             {
26274               g_warning ("%s: Failed to format start", __func__);
26275               return;
26276             }
26277         }
26278       else
26279         strcpy (start, "(not started)");
26280 
26281       if (end_iso)
26282         {
26283           struct tm end_tm;
26284 
26285           memset (&end_tm, 0, sizeof (struct tm));
26286           #if !defined(__GLIBC__)
26287             if (strptime (end_iso, "%Y-%m-%dT%H:%M:%S", &end_tm) == NULL)
26288           #else
26289             if (strptime (end_iso, "%FT%H:%M:%S", &end_tm) == NULL)
26290           #endif
26291             {
26292               g_warning ("%s: Failed to parse end", __func__);
26293               return;
26294             }
26295 
26296           if (strftime (end, 200, "%b %d, %H:%M:%S", &end_tm) == 0)
26297             {
26298               g_warning ("%s: Failed to format end", __func__);
26299               return;
26300             }
26301         }
26302       else
26303         strcpy (end, "(not finished)");
26304 
26305       g_string_append_printf (host_summary_buffer,
26306                               "   %-15s   %-16s   %s\n",
26307                               host,
26308                               start,
26309                               end);
26310     }
26311 }
26312 
26313 /**
26314  * @brief Init delta iterators for print_report_xml.
26315  *
26316  * @param[in]  report         The report.
26317  * @param[in]  results        Report result iterator.
26318  * @param[in]  delta          Delta report.
26319  * @param[in]  delta_results  Delta report result iterator.
26320  * @param[in]  get            GET command data.
26321  * @param[in]  term           Filter term.
26322  * @param[out] sort_field     Sort field.
26323  *
26324  * @return 0 on success, -1 error.
26325  */
26326 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)26327 init_delta_iterators (report_t report, iterator_t *results, report_t delta,
26328                       iterator_t *delta_results, const get_data_t *get,
26329                       const char *term, const char *sort_field)
26330 {
26331   int res;
26332   gchar *order;
26333   get_data_t delta_get;
26334 
26335   /*
26336    * Order must be the same as in result_cmp, except for description
26337    *  which isn't checked there.
26338    */
26339   if ((strcmp (sort_field, "name") == 0)
26340       || (strcmp (sort_field, "vulnerability") == 0))
26341     order = g_strdup (", host, port, severity, nvt, description");
26342   else if (strcmp (sort_field, "host") == 0)
26343     order = g_strdup (", port, severity, nvt, description");
26344   else if ((strcmp (sort_field, "port") == 0)
26345            || (strcmp (sort_field, "location") == 0))
26346     order = g_strdup (", host, severity, nvt, description");
26347   else if (strcmp (sort_field, "severity") == 0)
26348     order = g_strdup (", host, port, nvt, description");
26349   else if (strcmp (sort_field, "nvt") == 0)
26350     order = g_strdup (", host, port, severity, description");
26351   else
26352     order = g_strdup (", host, port, severity, nvt, description");
26353 
26354   delta_get = *get;
26355   delta_get.filt_id = NULL;
26356   delta_get.filter = g_strdup_printf ("rows=-1 first=1 sort=%s %s",
26357                                       sort_field, term);
26358   ignore_max_rows_per_page = 1;
26359 
26360 #if 0
26361   /* For debugging. */
26362 
26363   iterator_t results2;
26364 
26365   res = init_result_get_iterator (results, &delta_get, report, NULL, order);
26366   if (res)
26367     return -1;
26368 
26369   res = init_result_get_iterator (&results2, &delta_get, delta, NULL, order);
26370   if (res)
26371     return -1;
26372 
26373   g_debug ("   delta: %s: REPORT 1:", __func__);
26374   while (next (results))
26375     g_debug ("   delta: %s: %s   %s   %s   %s   %.30s",
26376             __func__,
26377             result_iterator_nvt_name (results),
26378             result_iterator_host (results),
26379             result_iterator_type (results),
26380             result_iterator_port (results),
26381             result_iterator_descr (results));
26382   cleanup_iterator (results);
26383   g_debug ("   delta: %s: REPORT 1 END", __func__);
26384 
26385   g_debug ("   delta: %s: REPORT 2:", __func__);
26386   while (next (&results2))
26387     g_debug ("   delta: %s: %s   %s   %s   %s   %.30s",
26388             __func__,
26389             result_iterator_nvt_name (&results2),
26390             result_iterator_host (&results2),
26391             result_iterator_type (&results2),
26392             result_iterator_port (&results2),
26393             result_iterator_descr (&results2));
26394   cleanup_iterator (&results2);
26395   g_debug ("   delta: %s: REPORT 2 END", __func__);
26396 #endif
26397 
26398   res = init_result_get_iterator (results, &delta_get, report, NULL, order);
26399   if (res)
26400     {
26401       ignore_max_rows_per_page = 0;
26402       g_free (order);
26403       return -1;
26404     }
26405 
26406   res = init_result_get_iterator (delta_results, &delta_get, delta, NULL, order);
26407   if (res)
26408     {
26409       ignore_max_rows_per_page = 0;
26410       g_free (order);
26411       return -1;
26412     }
26413 
26414   g_free (delta_get.filter);
26415   ignore_max_rows_per_page = 0;
26416   g_free (order);
26417 
26418   return 0;
26419 }
26420 
26421 /**
26422  * @brief Print delta results for print_report_xml.
26423  *
26424  * @param[in]  out            File stream to write to.
26425  * @param[in]  results        Report result iterator.
26426  * @param[in]  delta_results  Delta report result iterator.
26427  * @param[in]  delta_states   String describing delta states to include in count
26428  *                            (for example, "sngc" Same, New, Gone and Changed).
26429  *                            All levels if NULL.
26430  * @param[in]  first_result   First result.
26431  * @param[in]  max_results    Max results.
26432  * @param[in]  task           The task.
26433  * @param[in]  notes          Whether to include notes.
26434  * @param[in]  notes_details  Whether to include note details.
26435  * @param[in]  overrides          Whether to include overrides.
26436  * @param[in]  overrides_details  Whether to include override details.
26437  * @param[in]  sort_order         Sort order.
26438  * @param[in]  sort_field         Sort field.
26439  * @param[in]  result_hosts_only  Whether to only include hosts with results.
26440  * @param[in]  orig_filtered_result_count  Result count.
26441  * @param[in]  filtered_result_count       Result count.
26442  * @param[in]  orig_f_holes   Result count.
26443  * @param[in]  f_holes        Result count.
26444  * @param[in]  orig_f_infos   Result count.
26445  * @param[in]  f_infos        Result count.
26446  * @param[in]  orig_f_logs    Result count.
26447  * @param[in]  f_logs         Result count.
26448  * @param[in]  orig_f_warnings  Result count.
26449  * @param[in]  f_warnings       Result count.
26450  * @param[in]  orig_f_false_positives  Result count.
26451  * @param[in]  f_false_positives       Result count.
26452  * @param[in]  result_hosts   Result hosts.
26453  *
26454  * @return 0 on success, -1 error.
26455  */
26456 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)26457 print_report_delta_xml (FILE *out, iterator_t *results,
26458                         iterator_t *delta_results, const char *delta_states,
26459                         int first_result, int max_results, task_t task,
26460                         int notes, int notes_details, int overrides,
26461                         int overrides_details, int sort_order,
26462                         const char *sort_field, int result_hosts_only,
26463                         int *orig_filtered_result_count,
26464                         int *filtered_result_count,
26465                         int *orig_f_holes, int *f_holes,
26466                         int *orig_f_infos, int *f_infos,
26467                         int *orig_f_logs, int *f_logs,
26468                         int *orig_f_warnings, int *f_warnings,
26469                         int *orig_f_false_positives, int *f_false_positives,
26470                         array_t *result_hosts)
26471 {
26472   gboolean done, delta_done;
26473   int changed, gone, new, same;
26474   /* A tree of host, tree pairs, where the inner tree is a sorted tree
26475    * of port, threat pairs. */
26476   GTree *ports;
26477   gchar *msg;
26478 
26479   *orig_f_holes = *f_holes;
26480   *orig_f_infos = *f_infos;
26481   *orig_f_logs = *f_logs;
26482   *orig_f_warnings = *f_warnings;
26483   *orig_f_false_positives = *f_false_positives;
26484   *orig_filtered_result_count = *filtered_result_count;
26485 
26486   changed = (strchr (delta_states, 'c') != NULL);
26487   gone = (strchr (delta_states, 'g') != NULL);
26488   new = (strchr (delta_states, 'n') != NULL);
26489   same = (strchr (delta_states, 's') != NULL);
26490 
26491   ports = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, g_free,
26492                            (GDestroyNotify) free_host_ports);
26493 
26494   /* Compare the results in the two iterators, which are sorted. */
26495 
26496   g_debug ("   delta: %s: start", __func__);
26497   g_debug ("   delta: %s: sort_field: %s", __func__, sort_field);
26498   g_debug ("   delta: %s: sort_order: %i", __func__, sort_order);
26499   g_debug ("   delta: %s: max_results: %i", __func__, max_results);
26500   done = !next (results);
26501   delta_done = !next (delta_results);
26502   while (1)
26503     {
26504       GString *buffer;
26505       compare_results_t state;
26506       int used, would_use;
26507 
26508       if (max_results == 0)
26509         break;
26510 
26511       if (done)
26512         {
26513           if (delta_done)
26514             break;
26515           if (new)
26516             /* Extra results in 'delta_results'. */
26517             do
26518               {
26519                 const char *level;
26520 
26521                 g_debug ("   delta: %s: extra from report 2: %s",
26522                         __func__,
26523                         result_iterator_nvt_oid (results));
26524 
26525                 if (first_result)
26526                   {
26527                     g_debug ("   delta: skip");
26528                     first_result--;
26529                     continue;
26530                   }
26531 
26532                 /* Increase the result count. */
26533                 level = result_iterator_level (delta_results);
26534                 (*orig_filtered_result_count)++;
26535                 (*filtered_result_count)++;
26536                 if (strcmp (level, "High") == 0)
26537                   {
26538                     (*orig_f_holes)++;
26539                     (*f_holes)++;
26540                   }
26541                 else if (strcmp (level, "Medium") == 0)
26542                   {
26543                     (*orig_f_warnings)++;
26544                     (*f_warnings)++;
26545                   }
26546                 else if (strcmp (level, "Low") == 0)
26547                   {
26548                     (*orig_f_infos)++;
26549                     (*f_infos)++;
26550                   }
26551                 else if (strcmp (level, "Log") == 0)
26552                   {
26553                     (*orig_f_logs)++;
26554                     (*f_logs)++;
26555                   }
26556                 else if (strcmp (level, "False Positive") == 0)
26557                   {
26558                     (*orig_f_false_positives)++;
26559                     (*f_false_positives)++;
26560                   }
26561 
26562                 g_debug ("   delta: %s: extra from report 2: %s",
26563                         __func__,
26564                         result_iterator_nvt_oid (delta_results));
26565                 buffer = g_string_new ("");
26566                 buffer_results_xml (buffer,
26567                                     delta_results,
26568                                     task,
26569                                     notes,
26570                                     notes_details,
26571                                     overrides,
26572                                     overrides_details,
26573                                     0,
26574                                     0,
26575                                     0,
26576                                     "new",
26577                                     NULL,
26578                                     0,
26579                                     -1,
26580                                     0);
26581                 if (fprintf (out, "%s", buffer->str) < 0)
26582                   return -1;
26583                 g_string_free (buffer, TRUE);
26584                 if (result_hosts_only)
26585                   array_add_new_string (result_hosts,
26586                                         result_iterator_host (delta_results));
26587                 add_port (ports, delta_results);
26588                 max_results--;
26589                 if (max_results == 0)
26590                   break;
26591               }
26592             while (next (delta_results));
26593           delta_done = TRUE;
26594           break;
26595         }
26596 
26597       if (delta_done)
26598         {
26599           /* Extra results in 'results'. */
26600           if (gone)
26601             do
26602               {
26603                 g_debug ("   delta: %s: extra from report 1: %s",
26604                         __func__,
26605                         result_iterator_nvt_oid (results));
26606                 if (first_result)
26607                   {
26608                     g_debug ("   delta: skip");
26609                     first_result--;
26610                     continue;
26611                   }
26612                 buffer = g_string_new ("");
26613                 buffer_results_xml (buffer,
26614                                     results,
26615                                     task,
26616                                     notes,
26617                                     notes_details,
26618                                     overrides,
26619                                     overrides_details,
26620                                     0,
26621                                     0,
26622                                     0,
26623                                     "gone",
26624                                     NULL,
26625                                     0,
26626                                     -1,
26627                                     0);
26628                 if (fprintf (out, "%s", buffer->str) < 0)
26629                   return -1;
26630                 g_string_free (buffer, TRUE);
26631                 if (result_hosts_only)
26632                   array_add_new_string (result_hosts,
26633                                         result_iterator_host (results));
26634                 add_port (ports, results);
26635                 max_results--;
26636                 if (max_results == 0)
26637                   break;
26638               }
26639             while (next (results));
26640           else
26641             do
26642               {
26643                 const char *level;
26644 
26645                 /* Decrease the result count. */
26646                 level = result_iterator_level (results);
26647                 (*orig_filtered_result_count)--;
26648                 (*filtered_result_count)--;
26649                 if (strcmp (level, "High") == 0)
26650                   {
26651                     (*orig_f_holes)--;
26652                     (*f_holes)--;
26653                   }
26654                 else if (strcmp (level, "Medium") == 0)
26655                   {
26656                     (*orig_f_warnings)--;
26657                     (*f_warnings)--;
26658                   }
26659                 else if (strcmp (level, "Low") == 0)
26660                   {
26661                     (*orig_f_infos)--;
26662                     (*f_infos)--;
26663                   }
26664                 else if (strcmp (level, "Log") == 0)
26665                   {
26666                     (*orig_f_logs)--;
26667                     (*f_logs)--;
26668                   }
26669                 else if (strcmp (level, "False Positive") == 0)
26670                   {
26671                     (*orig_f_false_positives)--;
26672                     (*f_false_positives)--;
26673                   }
26674               }
26675             while (next (results));
26676           done = TRUE;
26677           break;
26678         }
26679 
26680       /* Compare the two results. */
26681 
26682       buffer = g_string_new ("");
26683       state = compare_and_buffer_results (buffer,
26684                                           results,
26685                                           delta_results,
26686                                           task,
26687                                           notes,
26688                                           notes_details,
26689                                           overrides,
26690                                           overrides_details,
26691                                           sort_order,
26692                                           sort_field,
26693                                           changed,
26694                                           gone,
26695                                           new,
26696                                           same,
26697                                           &max_results,
26698                                           &first_result,
26699                                           &used,
26700                                           &would_use);
26701       if (state == COMPARE_RESULTS_ERROR)
26702         {
26703           g_warning ("%s: compare_and_buffer_results failed",
26704                      __func__);
26705           return -1;
26706         }
26707       if (fprintf (out, "%s", buffer->str) < 0)
26708         return -1;
26709       g_string_free (buffer, TRUE);
26710 
26711       if ((used == 0)
26712           && ((state == COMPARE_RESULTS_GONE)
26713               || (state == COMPARE_RESULTS_SAME)
26714               || (state == COMPARE_RESULTS_CHANGED)))
26715         {
26716           const char *level;
26717 
26718           /* Decrease the result count. */
26719           level = result_iterator_level (results);
26720           (*filtered_result_count)--;
26721           if (strcmp (level, "High") == 0)
26722             {
26723               (*f_holes)--;
26724             }
26725           else if (strcmp (level, "Medium") == 0)
26726             {
26727               (*f_warnings)--;
26728             }
26729           else if (strcmp (level, "Low") == 0)
26730             {
26731               (*f_infos)--;
26732             }
26733           else if (strcmp (level, "Log") == 0)
26734             {
26735               (*f_logs)--;
26736             }
26737           else if (strcmp (level, "False Positive") == 0)
26738             {
26739               (*f_false_positives)--;
26740             }
26741         }
26742 
26743       if ((would_use == 0)
26744           && ((state == COMPARE_RESULTS_GONE)
26745               || (state == COMPARE_RESULTS_SAME)
26746               || (state == COMPARE_RESULTS_CHANGED)))
26747         {
26748           const char *level;
26749 
26750           /* Decrease the result count. */
26751           level = result_iterator_level (results);
26752           (*orig_filtered_result_count)--;
26753           if (strcmp (level, "High") == 0)
26754             {
26755               (*orig_f_holes)--;
26756             }
26757           else if (strcmp (level, "Medium") == 0)
26758             {
26759               (*orig_f_warnings)--;
26760             }
26761           else if (strcmp (level, "Low") == 0)
26762             {
26763               (*orig_f_infos)--;
26764             }
26765           else if (strcmp (level, "Log") == 0)
26766             {
26767               (*orig_f_logs)--;
26768             }
26769           else if (strcmp (level, "False Positive") == 0)
26770             {
26771               (*orig_f_false_positives)--;
26772             }
26773         }
26774 
26775       /* Move on to the next. */
26776 
26777       if (state == COMPARE_RESULTS_GONE)
26778         {
26779           /* "Used" just the 'results' result. */
26780           if (used)
26781             {
26782               if (result_hosts_only)
26783                 array_add_new_string (result_hosts,
26784                                       result_iterator_host (results));
26785               add_port (ports, results);
26786             }
26787           done = !next (results);
26788         }
26789       else if ((state == COMPARE_RESULTS_SAME)
26790                || (state == COMPARE_RESULTS_CHANGED))
26791         {
26792           /* "Used" both results. */
26793           if (used)
26794             {
26795               if (result_hosts_only)
26796                 array_add_new_string (result_hosts,
26797                                       result_iterator_host (results));
26798               add_port (ports, results);
26799             }
26800           done = !next (results);
26801           delta_done = !next (delta_results);
26802         }
26803       else if (state == COMPARE_RESULTS_NEW)
26804         {
26805           if (would_use)
26806             {
26807               const char *level;
26808 
26809               /* Would have "used" just the 'delta_results' result, on
26810                * an earlier page. */
26811 
26812               /* Increase the result count. */
26813               level = result_iterator_level (delta_results);
26814               (*orig_filtered_result_count)++;
26815               if (strcmp (level, "High") == 0)
26816                 {
26817                   (*orig_f_holes)++;
26818                 }
26819               else if (strcmp (level, "Medium") == 0)
26820                 {
26821                   (*orig_f_warnings)++;
26822                 }
26823               else if (strcmp (level, "Low") == 0)
26824                 {
26825                   (*orig_f_infos)++;
26826                 }
26827               else if (strcmp (level, "Log") == 0)
26828                 {
26829                   (*orig_f_logs)++;
26830                 }
26831               else if (strcmp (level, "False Positive") == 0)
26832                 {
26833                   (*orig_f_false_positives)++;
26834                 }
26835             }
26836 
26837           if (used)
26838             {
26839               const char *level;
26840 
26841               /* "Used" just the 'delta_results' result. */
26842 
26843               /* Increase the result count. */
26844               level = result_iterator_level (delta_results);
26845               (*filtered_result_count)++;
26846               if (strcmp (level, "High") == 0)
26847                 {
26848                   (*f_holes)++;
26849                 }
26850               else if (strcmp (level, "Medium") == 0)
26851                 {
26852                   (*f_warnings)++;
26853                 }
26854               else if (strcmp (level, "Low") == 0)
26855                 {
26856                   (*f_infos)++;
26857                 }
26858               else if (strcmp (level, "Log") == 0)
26859                 {
26860                   (*f_logs)++;
26861                 }
26862               else if (strcmp (level, "False Positive") == 0)
26863                 {
26864                   (*f_false_positives)++;
26865                 }
26866 
26867               if (result_hosts_only)
26868                 array_add_new_string (result_hosts,
26869                                       result_iterator_host
26870                                        (delta_results));
26871 
26872               add_port (ports, delta_results);
26873             }
26874           delta_done = !next (delta_results);
26875         }
26876       else
26877         assert (0);
26878     }
26879 
26880   /* Compare remaining results, for the filtered report counts. */
26881 
26882   g_debug ("   delta: %s: counting rest", __func__);
26883   while (1)
26884     {
26885       compare_results_t state;
26886       int used, would_use;
26887 
26888       if (done)
26889         {
26890           if (delta_done)
26891             break;
26892           if (new)
26893             /* Extra results in 'delta_results'. */
26894             do
26895               {
26896                 const char *level;
26897 
26898                 g_debug ("   delta: %s: extra from report 2: %s",
26899                         __func__,
26900                         result_iterator_nvt_oid (delta_results));
26901 
26902                 /* Increase the result count. */
26903                 level = result_iterator_level (delta_results);
26904                 (*orig_filtered_result_count)++;
26905                 if (strcmp (level, "High") == 0)
26906                   {
26907                     (*orig_f_holes)++;
26908                   }
26909                 else if (strcmp (level, "Medium") == 0)
26910                   {
26911                     (*orig_f_warnings)++;
26912                   }
26913                 else if (strcmp (level, "Low") == 0)
26914                   {
26915                     (*orig_f_infos)++;
26916                   }
26917                 else if (strcmp (level, "Log") == 0)
26918                   {
26919                     (*orig_f_logs)++;
26920                   }
26921                 else if (strcmp (level, "False Positive") == 0)
26922                   {
26923                     (*orig_f_false_positives)++;
26924                   }
26925               }
26926             while (next (delta_results));
26927           break;
26928         }
26929 
26930       if (delta_done)
26931         {
26932           /* Extra results in 'results'. */
26933           if (gone)
26934             do
26935               {
26936                 g_debug ("   delta: %s: extra from report 1: %s",
26937                         __func__,
26938                         result_iterator_nvt_oid (results));
26939 
26940                 /* It's in the count already. */
26941               }
26942             while (next (results));
26943           else
26944             do
26945               {
26946                 const char *level;
26947 
26948                 /* Decrease the result count. */
26949                 level = result_iterator_level (results);
26950                 (*orig_filtered_result_count)--;
26951                 if (strcmp (level, "High") == 0)
26952                   {
26953                     (*orig_f_holes)--;
26954                   }
26955                 else if (strcmp (level, "Medium") == 0)
26956                   {
26957                     (*orig_f_warnings)--;
26958                   }
26959                 else if (strcmp (level, "Low") == 0)
26960                   {
26961                     (*orig_f_infos)--;
26962                   }
26963                 else if (strcmp (level, "Log") == 0)
26964                   {
26965                     (*orig_f_logs)--;
26966                   }
26967                 else if (strcmp (level, "False Positive") == 0)
26968                   {
26969                     (*orig_f_false_positives)--;
26970                   }
26971               }
26972             while (next (results));
26973           break;
26974         }
26975 
26976       /* Compare the two results. */
26977 
26978       state = compare_and_buffer_results (NULL,
26979                                           results,
26980                                           delta_results,
26981                                           task,
26982                                           notes,
26983                                           notes_details,
26984                                           overrides,
26985                                           overrides_details,
26986                                           sort_order,
26987                                           sort_field,
26988                                           changed,
26989                                           gone,
26990                                           new,
26991                                           same,
26992                                           &max_results,
26993                                           &first_result,
26994                                           &used,
26995                                           &would_use);
26996       if (state == COMPARE_RESULTS_ERROR)
26997         {
26998           g_warning ("%s: compare_and_buffer_results failed",
26999                      __func__);
27000           return -1;
27001         }
27002 
27003       if (state == COMPARE_RESULTS_NEW)
27004         {
27005           if (used)
27006             {
27007               const char *level;
27008 
27009               /* "Used" just the 'delta_results' result. */
27010 
27011               /* Increase the result count. */
27012               level = result_iterator_level (delta_results);
27013               (*orig_filtered_result_count)++;
27014               if (strcmp (level, "High") == 0)
27015                 {
27016                   (*orig_f_holes)++;
27017                 }
27018               else if (strcmp (level, "Medium") == 0)
27019                 {
27020                   (*orig_f_warnings)++;
27021                 }
27022               else if (strcmp (level, "Low") == 0)
27023                 {
27024                   (*orig_f_infos)++;
27025                 }
27026               else if (strcmp (level, "Log") == 0)
27027                 {
27028                   (*orig_f_logs)++;
27029                 }
27030               else if (strcmp (level, "False Positive") == 0)
27031                 {
27032                   (*orig_f_false_positives)++;
27033                 }
27034             }
27035         }
27036       else if (used)
27037         {
27038           /* It's in the count already. */
27039         }
27040       else
27041         {
27042           const char *level;
27043 
27044           /* Decrease the result count. */
27045           level = result_iterator_level (results);
27046           (*orig_filtered_result_count)--;
27047           if (strcmp (level, "High") == 0)
27048             {
27049               (*orig_f_holes)--;
27050             }
27051           else if (strcmp (level, "Medium") == 0)
27052             {
27053               (*orig_f_warnings)--;
27054             }
27055           else if (strcmp (level, "Low") == 0)
27056             {
27057               (*orig_f_infos)--;
27058             }
27059           else if (strcmp (level, "Log") == 0)
27060             {
27061               (*orig_f_logs)--;
27062             }
27063           else if (strcmp (level, "False Positive") == 0)
27064             {
27065               (*orig_f_false_positives)--;
27066             }
27067         }
27068 
27069       /* Move on to the next. */
27070 
27071       if (state == COMPARE_RESULTS_GONE)
27072         {
27073           /* "Used" just the 'results' result. */
27074           done = !next (results);
27075         }
27076       else if ((state == COMPARE_RESULTS_SAME)
27077                || (state == COMPARE_RESULTS_CHANGED))
27078         {
27079           /* "Used" both results. */
27080           done = !next (results);
27081           delta_done = !next (delta_results);
27082         }
27083       else if (state == COMPARE_RESULTS_NEW)
27084         {
27085           /* "Used" just the 'delta_results' result. */
27086           delta_done = !next (delta_results);
27087         }
27088       else
27089         assert (0);
27090     }
27091   msg = g_markup_printf_escaped ("</results>");
27092   if (fprintf (out, "%s", msg) < 0)
27093     {
27094       g_free (msg);
27095       fclose (out);
27096       return -1;
27097     }
27098   g_free (msg);
27099 
27100   /* Write ports to file. */
27101 
27102   msg = g_markup_printf_escaped ("<ports"
27103                                  " start=\"%i\""
27104                                  " max=\"%i\">",
27105                                  /* Add 1 for 1 indexing. */
27106                                  first_result + 1,
27107                                  max_results);
27108   if (fprintf (out, "%s", msg) < 0)
27109     {
27110       g_free (msg);
27111       fclose (out);
27112       return -1;
27113     }
27114   g_free (msg);
27115   if (sort_field == NULL || strcmp (sort_field, "port"))
27116     {
27117       if (sort_order)
27118         g_tree_foreach (ports, print_host_ports_by_severity_asc, out);
27119       else
27120         g_tree_foreach (ports, print_host_ports_by_severity_desc, out);
27121     }
27122   else if (sort_order)
27123     g_tree_foreach (ports, print_host_ports, out);
27124   else
27125     g_tree_foreach (ports, print_host_ports_desc, out);
27126   g_tree_destroy (ports);
27127   msg = g_markup_printf_escaped ("</ports>");
27128   if (fprintf (out, "%s", msg) < 0)
27129     {
27130       g_free (msg);
27131       fclose (out);
27132       return -1;
27133     }
27134   g_free (msg);
27135 
27136   return 0;
27137 }
27138 
27139 /**
27140  * @brief Print the main XML content for a report to a file.
27141  *
27142  * @param[in]  report      The report.
27143  * @param[in]  delta       Report to compare with the report.
27144  * @param[in]  task        Task associated with report.
27145  * @param[in]  xml_start   File name.
27146  * @param[in]  get         GET command data.
27147  * @param[in]  notes_details      If notes, Whether to include details.
27148  * @param[in]  overrides_details  If overrides, Whether to include details.
27149  * @param[in]  result_tags        Whether to include tags in results.
27150  * @param[in]  ignore_pagination   Whether to ignore pagination data.
27151  * @param[in]  lean                Whether to return lean report.
27152  * @param[out] filter_term_return  Filter term used in report.
27153  * @param[out] zone_return         Actual timezone used in report.
27154  * @param[out] host_summary    Summary of results per host.
27155  *
27156  * @return 0 on success, -1 error, 2 failed to find filter (before any printing).
27157  */
27158 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)27159 print_report_xml_start (report_t report, report_t delta, task_t task,
27160                         gchar* xml_start, const get_data_t *get,
27161                         int notes_details, int overrides_details,
27162                         int result_tags, int ignore_pagination, int lean,
27163                         gchar **filter_term_return, gchar **zone_return,
27164                         gchar **host_summary)
27165 {
27166   int result_hosts_only;
27167   int notes, overrides;
27168 
27169   int first_result, max_results, sort_order;
27170 
27171   FILE *out;
27172   gchar *clean, *term, *sort_field, *levels, *search_phrase;
27173   gchar *min_qod;
27174   gchar *delta_states, *timestamp;
27175   int min_qod_int;
27176   char *uuid, *tsk_uuid = NULL, *start_time, *end_time;
27177   int total_result_count, filtered_result_count;
27178   array_t *result_hosts;
27179   int reuse_result_iterator;
27180   iterator_t results, delta_results;
27181   int holes, infos, logs, warnings, false_positives;
27182   int f_holes, f_infos, f_logs, f_warnings, f_false_positives;
27183   int orig_f_holes, orig_f_infos, orig_f_logs;
27184   int orig_f_warnings, orig_f_false_positives, orig_filtered_result_count;
27185   int search_phrase_exact, apply_overrides, count_filtered;
27186   double severity, f_severity;
27187   gchar *tz, *zone;
27188   char *old_tz_override;
27189   GString *filters_buffer, *filters_extra_buffer, *host_summary_buffer;
27190   gchar *term_value;
27191   GHashTable *f_host_ports;
27192   GHashTable *f_host_holes, *f_host_warnings, *f_host_infos;
27193   GHashTable *f_host_logs, *f_host_false_positives;
27194   task_status_t run_status;
27195 
27196   /* Init some vars to prevent warnings from older compilers. */
27197   max_results = -1;
27198   levels = NULL;
27199   zone = NULL;
27200   delta_states = NULL;
27201   min_qod = NULL;
27202   search_phrase = NULL;
27203   total_result_count = filtered_result_count = 0;
27204   orig_filtered_result_count = 0;
27205   orig_f_false_positives = orig_f_warnings = orig_f_logs = orig_f_infos = 0;
27206   orig_f_holes = 0;
27207   f_host_ports = NULL;
27208   f_host_holes = NULL;
27209   f_host_warnings = NULL;
27210   f_host_infos = NULL;
27211   f_host_logs = NULL;
27212   f_host_false_positives = NULL;
27213 
27214   /** @todo Leaks on error in PRINT and PRINT_XML.  The process normally exits
27215    *        then anyway. */
27216 
27217   /* run_status is set by report_scan_run_status when either of "delta" and
27218    * "report" are true.  run_status is only used by run_status_name, only when
27219    * either of "delta" and "report" are true, and only after a
27220    * report_scan_run_status call.  Still GCC 4.4.5 (Debian 4.4.5-8) gives a
27221    * "may be used uninitialized" warning, so init it here to quiet the
27222    * warning. */
27223   run_status = TASK_STATUS_INTERRUPTED;
27224 
27225   if (report == 0)
27226     {
27227       assert (0);
27228       return -1;
27229     }
27230 
27231   out = fopen (xml_start, "w");
27232 
27233   if (out == NULL)
27234     {
27235       g_warning ("%s: fopen failed: %s",
27236                  __func__,
27237                  strerror (errno));
27238       return -1;
27239     }
27240 
27241   assert (get);
27242 
27243   if ((get->filt_id && strlen (get->filt_id)
27244        && strcmp (get->filt_id, FILT_ID_NONE))
27245       || (get->filter && strlen (get->filter)))
27246     {
27247       term = NULL;
27248       if (get->filt_id && strlen (get->filt_id)
27249           && strcmp (get->filt_id, FILT_ID_NONE))
27250         {
27251           term = filter_term (get->filt_id);
27252           if (term == NULL)
27253             {
27254               fclose (out);
27255               return 2;
27256             }
27257         }
27258 
27259       /* Set the filter parameters from the filter term. */
27260       manage_report_filter_controls (term ? term : get->filter,
27261                                      &first_result, &max_results, &sort_field,
27262                                      &sort_order, &result_hosts_only,
27263                                      &min_qod, &levels, &delta_states,
27264                                      &search_phrase, &search_phrase_exact,
27265                                      &notes, &overrides,
27266                                      &apply_overrides, &zone);
27267     }
27268   else
27269     {
27270       term = g_strdup ("");
27271       /* Set the filter parameters to defaults */
27272       manage_report_filter_controls (term,
27273                                      &first_result, &max_results, &sort_field,
27274                                      &sort_order, &result_hosts_only,
27275                                      &min_qod, &levels, &delta_states,
27276                                      &search_phrase, &search_phrase_exact,
27277                                      &notes, &overrides,
27278                                      &apply_overrides, &zone);
27279     }
27280 
27281   max_results = manage_max_rows (max_results);
27282 
27283   levels = levels ? levels : g_strdup ("hmlgd");
27284 
27285   if (task && task_uuid (task, &tsk_uuid))
27286     {
27287       fclose (out);
27288       g_free (term);
27289       g_free (levels);
27290       g_free (search_phrase);
27291       g_free (min_qod);
27292       g_free (delta_states);
27293       return -1;
27294     }
27295 
27296   if (zone && strlen (zone))
27297     {
27298       gchar *quoted_zone;
27299       /* Store current TZ. */
27300       tz = getenv ("TZ") ? g_strdup (getenv ("TZ")) : NULL;
27301 
27302       if (setenv ("TZ", zone, 1) == -1)
27303         {
27304           g_warning ("%s: Failed to switch to timezone", __func__);
27305           if (tz != NULL)
27306             setenv ("TZ", tz, 1);
27307           g_free (tz);
27308           g_free (zone);
27309           return -1;
27310         }
27311 
27312       old_tz_override = sql_string ("SELECT current_setting"
27313                                     "        ('gvmd.tz_override');");
27314 
27315       quoted_zone = sql_insert (zone);
27316       sql ("SET SESSION \"gvmd.tz_override\" = %s;", quoted_zone);
27317       g_free (quoted_zone);
27318 
27319       tzset ();
27320     }
27321   else
27322     {
27323       /* Keep compiler quiet. */
27324       tz = NULL;
27325       old_tz_override = NULL;
27326     }
27327 
27328   if (delta && report)
27329     {
27330       uuid = report_uuid (report);
27331       PRINT (out, "<report type=\"delta\" id=\"%s\">", uuid);
27332       free (uuid);
27333     }
27334   else
27335     {
27336       uuid = report_uuid (report);
27337       PRINT (out, "<report id=\"%s\">", uuid);
27338       free (uuid);
27339     }
27340 
27341   PRINT (out, "<gmp><version>%s</version></gmp>", GMP_VERSION);
27342 
27343   if (delta)
27344     {
27345       delta_states = delta_states ? delta_states : g_strdup ("cgns");
27346       report_scan_run_status (delta, &run_status);
27347 
27348       uuid = report_uuid (delta);
27349       PRINT (out,
27350              "<delta>"
27351              "<report id=\"%s\">"
27352              "<scan_run_status>%s</scan_run_status>",
27353              uuid,
27354              run_status_name (run_status
27355                                ? run_status
27356                                : TASK_STATUS_INTERRUPTED));
27357 
27358       if (report_timestamp (uuid, &timestamp))
27359         {
27360           free (uuid);
27361           g_free (levels);
27362           g_free (search_phrase);
27363           g_free (min_qod);
27364           g_free (delta_states);
27365           tz_revert (zone, tz, old_tz_override);
27366           return -1;
27367         }
27368       PRINT (out,
27369              "<timestamp>%s</timestamp>",
27370              timestamp);
27371       g_free (timestamp);
27372 
27373       start_time = scan_start_time (delta);
27374       PRINT (out,
27375              "<scan_start>%s</scan_start>",
27376              start_time);
27377       free (start_time);
27378 
27379       end_time = scan_end_time (delta);
27380       PRINT (out,
27381              "<scan_end>%s</scan_end>",
27382              end_time);
27383       free (end_time);
27384 
27385       PRINT (out,
27386              "</report>"
27387              "</delta>");
27388     }
27389 
27390   count_filtered = (delta == 0 && ignore_pagination && get->details);
27391 
27392   if (report)
27393     {
27394       /* Get total counts of full results. */
27395 
27396       if (delta == 0)
27397         {
27398           int total_holes, total_infos, total_logs;
27399           int total_warnings, total_false_positives;
27400           get_data_t *all_results_get;
27401 
27402           all_results_get = report_results_get_data (1, -1, 0, 0);
27403           report_counts_id (report, &total_holes, &total_infos,
27404                             &total_logs, &total_warnings,
27405                             &total_false_positives, NULL, all_results_get,
27406                             NULL);
27407           total_result_count = total_holes + total_infos
27408                                + total_logs + total_warnings
27409                                + total_false_positives;
27410           get_data_reset (all_results_get);
27411           free (all_results_get);
27412         }
27413 
27414       /* Get total counts of filtered results. */
27415 
27416       if (count_filtered)
27417         {
27418           /* We're getting all the filtered results, so we can count them as we
27419            * print them, to save time. */
27420 
27421           filtered_result_count = 0;
27422         }
27423       else
27424         {
27425           /* Beware, we're using the full variables temporarily here, but
27426            * report_counts_id counts the filtered results. */
27427           report_counts_id (report, &holes, &infos, &logs, &warnings,
27428                             &false_positives, NULL, get, NULL);
27429 
27430           filtered_result_count = holes + infos + logs + warnings
27431                                   + false_positives;
27432         }
27433 
27434       /* Get report run status. */
27435 
27436       report_scan_run_status (report, &run_status);
27437     }
27438 
27439   clean = manage_clean_filter (term
27440                                 ? term
27441                                 : (get->filter ? get->filter : ""));
27442 
27443   term_value = filter_term_value (clean, "min_qod");
27444   if (term_value == NULL)
27445     {
27446       gchar *new_filter;
27447       new_filter = g_strdup_printf ("min_qod=%i %s",
27448                                     MIN_QOD_DEFAULT,
27449                                     clean);
27450       g_free (clean);
27451       clean = new_filter;
27452     }
27453   g_free (term_value);
27454 
27455   term_value = filter_term_value (clean, "apply_overrides");
27456   if (term_value == NULL)
27457     {
27458       gchar *new_filter;
27459       new_filter = g_strdup_printf ("apply_overrides=%i %s",
27460                                     APPLY_OVERRIDES_DEFAULT,
27461                                     clean);
27462       g_free (clean);
27463       clean = new_filter;
27464     }
27465   g_free (term_value);
27466 
27467   g_free (term);
27468   term = clean;
27469 
27470   if (delta
27471       && sort_field
27472       /* These are all checked in result_cmp. */
27473       && strcmp (sort_field, "name")
27474       && strcmp (sort_field, "vulnerability")
27475       && strcmp (sort_field, "host")
27476       && strcmp (sort_field, "port")
27477       && strcmp (sort_field, "location")
27478       && strcmp (sort_field, "severity")
27479       && strcmp (sort_field, "nvt")
27480       && strcmp (sort_field, "description")
27481       && strcmp (sort_field, "type")
27482       && strcmp (sort_field, "original_type"))
27483     {
27484       gchar *new_term;
27485 
27486       if ((strcmp (sort_field, "task") == 0)
27487           || (strcmp (sort_field, "task_id") == 0)
27488           || (strcmp (sort_field, "report_id") == 0))
27489         {
27490           /* These don't affect delta report, so sort by vulnerability. */
27491           g_free (sort_field);
27492           sort_field = g_strdup ("vulnerability");
27493         }
27494       else
27495         {
27496           /* The remaining filterable fields for the result iterator, all of
27497            * which may be used as a sort field.  These could be added to
27498            * result_cmp.  For now sort by vulnerability. */
27499 #if 0
27500           "uuid", "comment", "created", "modified", "_owner",
27501           "cvss_base", "nvt_version", "original_severity", "date",
27502           "solution_type", "qod", "qod_type", "cve", "hostname", "path"
27503 #endif
27504           g_free (sort_field);
27505           sort_field = g_strdup ("vulnerability");
27506         }
27507 
27508       /* Adjust "term" to match sort_field, because "term" will be used in the
27509        * REPORT XML FILTERS (sent by buffer_get_filter_xml below). */
27510       new_term = g_strdup_printf ("sort=%s %s",
27511                                   sort_field,
27512                                   term);
27513       g_free (term);
27514       term = new_term;
27515       /* Similarly, the order will now be ascending. */
27516       sort_order = 1;
27517     }
27518 
27519   if (filter_term_return)
27520     *filter_term_return = g_strdup (term);
27521 
27522   PRINT
27523    (out,
27524     "<sort><field>%s<order>%s</order></field></sort>",
27525     sort_field ? sort_field : "type",
27526     sort_order ? "ascending" : "descending");
27527 
27528   filters_extra_buffer = g_string_new ("");
27529 
27530   if (strchr (levels, 'h'))
27531     g_string_append (filters_extra_buffer, "<filter>High</filter>");
27532   if (strchr (levels, 'm'))
27533     g_string_append (filters_extra_buffer, "<filter>Medium</filter>");
27534   if (strchr (levels, 'l'))
27535     g_string_append (filters_extra_buffer, "<filter>Low</filter>");
27536   if (strchr (levels, 'g'))
27537     g_string_append (filters_extra_buffer, "<filter>Log</filter>");
27538   if (strchr (levels, 'f'))
27539     g_string_append (filters_extra_buffer, "<filter>False Positive</filter>");
27540 
27541   if (delta)
27542     {
27543       gchar *escaped_delta_states = g_markup_escape_text (delta_states, -1);
27544       g_string_append_printf (filters_extra_buffer,
27545                               "<delta>"
27546                               "%s"
27547                               "<changed>%i</changed>"
27548                               "<gone>%i</gone>"
27549                               "<new>%i</new>"
27550                               "<same>%i</same>"
27551                               "</delta>",
27552                               escaped_delta_states,
27553                               strchr (delta_states, 'c') != NULL,
27554                               strchr (delta_states, 'g') != NULL,
27555                               strchr (delta_states, 'n') != NULL,
27556                               strchr (delta_states, 's') != NULL);
27557       g_free (escaped_delta_states);
27558     }
27559 
27560   filters_buffer = g_string_new ("");
27561   buffer_get_filter_xml (filters_buffer, "result", get, term,
27562                          filters_extra_buffer->str);
27563   g_string_free (filters_extra_buffer, TRUE);
27564 
27565   PRINT_XML (out, filters_buffer->str);
27566   g_string_free (filters_buffer, TRUE);
27567 
27568   if (report)
27569     {
27570       int tag_count = resource_tag_count ("report", report, 1);
27571 
27572       if (tag_count)
27573         {
27574           PRINT (out,
27575                  "<user_tags>"
27576                  "<count>%i</count>",
27577                  tag_count);
27578 
27579           if (get->details || get->id)
27580             {
27581               iterator_t tags;
27582 
27583               init_resource_tag_iterator (&tags, "report", report, 1, NULL, 1);
27584 
27585               while (next (&tags))
27586                 {
27587                   PRINT (out,
27588                         "<tag id=\"%s\">"
27589                         "<name>%s</name>"
27590                         "<value>%s</value>"
27591                         "<comment>%s</comment>"
27592                         "</tag>",
27593                         resource_tag_iterator_uuid (&tags),
27594                         resource_tag_iterator_name (&tags),
27595                         resource_tag_iterator_value (&tags),
27596                         resource_tag_iterator_comment (&tags));
27597                 }
27598 
27599               cleanup_iterator (&tags);
27600             }
27601 
27602           PRINT (out, "</user_tags>");
27603         }
27604     }
27605 
27606   if (report)
27607     {
27608       PRINT
27609        (out,
27610         "<scan_run_status>%s</scan_run_status>",
27611         run_status_name (run_status
27612                           ? run_status
27613                           : TASK_STATUS_INTERRUPTED));
27614 
27615       PRINT (out,
27616              "<hosts><count>%i</count></hosts>",
27617              report_host_count (report));
27618 
27619       PRINT (out,
27620              "<closed_cves><count>%i</count></closed_cves>",
27621              report_closed_cve_count (report));
27622 
27623       PRINT (out,
27624              "<vulns><count>%i</count></vulns>",
27625              report_vuln_count (report));
27626 
27627       PRINT (out,
27628              "<os><count>%i</count></os>",
27629              report_os_count (report));
27630 
27631       PRINT (out,
27632              "<apps><count>%i</count></apps>",
27633              report_app_count (report));
27634 
27635       PRINT (out,
27636              "<ssl_certs><count>%i</count></ssl_certs>",
27637              report_ssl_cert_count (report));
27638 
27639     }
27640 
27641   if (task && tsk_uuid)
27642     {
27643       char *tsk_name, *task_target_uuid, *task_target_name;
27644       char *task_target_comment, *comment;
27645       target_t target;
27646       gchar *progress_xml;
27647       iterator_t tags;
27648       int task_tag_count = resource_tag_count ("task", task, 1);
27649 
27650       tsk_name = task_name (task);
27651 
27652       comment = task_comment (task);
27653 
27654       target = task_target (task);
27655       if (task_target_in_trash (task))
27656         {
27657           task_target_uuid = trash_target_uuid (target);
27658           task_target_name = trash_target_name (target);
27659           task_target_comment = trash_target_comment (target);
27660         }
27661       else
27662         {
27663           task_target_uuid = target_uuid (target);
27664           task_target_name = target_name (target);
27665           task_target_comment = target_comment (target);
27666         }
27667 
27668       if ((target == 0)
27669           && (task_run_status (task) == TASK_STATUS_RUNNING))
27670         progress_xml = g_strdup_printf
27671                         ("%i",
27672                          task_upload_progress (task));
27673       else
27674         {
27675           int progress;
27676           progress = report_progress (report);
27677           progress_xml = g_strdup_printf ("%i", progress);
27678         }
27679 
27680       PRINT (out,
27681              "<task id=\"%s\">"
27682              "<name>%s</name>"
27683              "<comment>%s</comment>"
27684              "<target id=\"%s\">"
27685              "<trash>%i</trash>"
27686              "<name>%s</name>"
27687              "<comment>%s</comment>"
27688              "</target>"
27689              "<progress>%s</progress>",
27690              tsk_uuid,
27691              tsk_name ? tsk_name : "",
27692              comment ? comment : "",
27693              task_target_uuid ? task_target_uuid : "",
27694              task_target_in_trash (task),
27695              task_target_name ? task_target_name : "",
27696              task_target_comment ? task_target_comment : "",
27697              progress_xml);
27698       g_free (progress_xml);
27699       free (comment);
27700       free (tsk_name);
27701       free (tsk_uuid);
27702       free (task_target_uuid);
27703       free (task_target_name);
27704       free (task_target_comment);
27705 
27706       if (task_tag_count)
27707         {
27708           PRINT (out,
27709                  "<user_tags>"
27710                  "<count>%i</count>",
27711                  task_tag_count);
27712 
27713           init_resource_tag_iterator (&tags, "task", task, 1, NULL, 1);
27714           while (next (&tags))
27715             {
27716               PRINT (out,
27717                     "<tag id=\"%s\">"
27718                     "<name>%s</name>"
27719                     "<value>%s</value>"
27720                     "<comment>%s</comment>"
27721                     "</tag>",
27722                     resource_tag_iterator_uuid (&tags),
27723                     resource_tag_iterator_name (&tags),
27724                     resource_tag_iterator_value (&tags),
27725                     resource_tag_iterator_comment (&tags));
27726             }
27727           cleanup_iterator (&tags);
27728 
27729           PRINT (out,
27730                 "</user_tags>");
27731         }
27732 
27733       PRINT (out,
27734              "</task>");
27735 
27736       {
27737         char *source_iface;
27738 
27739         /* Info about the situation at the time of scan. */
27740 
27741         PRINT (out,
27742                "<scan>"
27743                "<task>");
27744 
27745         source_iface = report_source_iface (report);
27746 
27747         if (source_iface)
27748           /* VALUE "" means preference was not set.  Missing PREFERENCE means
27749            * we don't know. */
27750           PRINT (out,
27751                  "<preferences>"
27752                  "<preference>"
27753                  "<name>Network Source Interface</name>"
27754                  "<scanner_name>source_iface</scanner_name>"
27755                  "<value>%s</value>"
27756                  "</preference>"
27757                  "</preferences>",
27758                  source_iface);
27759 
27760         free (source_iface);
27761 
27762         PRINT (out,
27763                "</task>"
27764                "</scan>");
27765       }
27766     }
27767 
27768   uuid = report_uuid (report);
27769   if (report_timestamp (uuid, &timestamp))
27770     {
27771       free (uuid);
27772       g_free (term);
27773       tz_revert (zone, tz, old_tz_override);
27774       return -1;
27775     }
27776   free (uuid);
27777   PRINT (out,
27778          "<timestamp>%s</timestamp>",
27779          timestamp);
27780   g_free (timestamp);
27781 
27782   start_time = scan_start_time (report);
27783   PRINT (out,
27784          "<scan_start>%s</scan_start>",
27785          start_time);
27786   free (start_time);
27787 
27788   {
27789     time_t start_time_epoch;
27790     const char *abbrev;
27791     gchar *report_zone;
27792 
27793     start_time_epoch = scan_start_time_epoch (report);
27794     abbrev = NULL;
27795     if (zone && strlen (zone))
27796       report_zone = g_strdup (zone);
27797     else
27798       report_zone = setting_timezone ();
27799     iso_time_tz (&start_time_epoch, report_zone, &abbrev);
27800 
27801     if (zone_return)
27802       *zone_return = g_strdup (report_zone ? report_zone : "");
27803 
27804     PRINT (out,
27805            "<timezone>%s</timezone>"
27806            "<timezone_abbrev>%s</timezone_abbrev>",
27807            report_zone
27808             ? report_zone
27809             : "Coordinated Universal Time",
27810            abbrev ? abbrev : "UTC");
27811     g_free (report_zone);
27812   }
27813 
27814   /* Port summary. */
27815 
27816   f_host_ports = g_hash_table_new_full (g_str_hash, g_str_equal,
27817                                         g_free, NULL);
27818 
27819   reuse_result_iterator = 0;
27820   if (get->details && (delta == 0))
27821     {
27822       reuse_result_iterator = 1;
27823       if (print_report_port_xml (report, out, get, first_result, max_results,
27824                                  sort_order, sort_field, f_host_ports, &results))
27825         {
27826           g_free (term);
27827           tz_revert (zone, tz, old_tz_override);
27828           g_hash_table_destroy (f_host_ports);
27829           return -1;
27830         }
27831     }
27832 
27833   /* Prepare result counts. */
27834 
27835   if (count_filtered)
27836     {
27837       /* We're getting all the filtered results, so we can count them as we
27838        * print them, to save time. */
27839 
27840       report_counts_id_full (report, &holes, &infos, &logs,
27841                              &warnings, &false_positives, &severity,
27842                              get, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
27843 
27844       f_holes = f_infos = f_logs = f_warnings = 0;
27845       f_false_positives = f_severity = 0;
27846     }
27847   else
27848     report_counts_id_full (report, &holes, &infos, &logs,
27849                            &warnings, &false_positives, &severity,
27850                            get, NULL,
27851                            &f_holes, &f_infos, &f_logs, &f_warnings,
27852                            &f_false_positives, &f_severity);
27853 
27854   /* Results. */
27855 
27856   if (min_qod == NULL || sscanf (min_qod, "%d", &min_qod_int) != 1)
27857     min_qod_int = MIN_QOD_DEFAULT;
27858 
27859   if (delta && get->details)
27860     {
27861       if (init_delta_iterators (report, &results, delta, &delta_results, get,
27862                                 term, sort_field))
27863         {
27864           g_free (term);
27865           g_hash_table_destroy (f_host_ports);
27866           return -1;
27867         }
27868       g_free (term);
27869     }
27870   else if (get->details)
27871     {
27872       int res;
27873       g_free (term);
27874       if (reuse_result_iterator)
27875         iterator_rewind (&results);
27876       else
27877         {
27878           res = init_result_get_iterator (&results, get, report, NULL, NULL);
27879           if (res)
27880             {
27881               g_hash_table_destroy (f_host_ports);
27882               return -1;
27883             }
27884         }
27885     }
27886   else
27887     g_free (term);
27888 
27889   if (get->details)
27890     PRINT (out,
27891              "<results"
27892              " start=\"%i\""
27893              " max=\"%i\">",
27894              /* Add 1 for 1 indexing. */
27895              ignore_pagination ? 1 : first_result + 1,
27896              ignore_pagination ? -1 : max_results);
27897   if (get->details && result_hosts_only)
27898     result_hosts = make_array ();
27899   else
27900     /* Quiet erroneous compiler warning. */
27901     result_hosts = NULL;
27902 
27903   f_host_holes = g_hash_table_new_full (g_str_hash, g_str_equal,
27904                                         g_free, NULL);
27905   f_host_warnings = g_hash_table_new_full (g_str_hash, g_str_equal,
27906                                            g_free, NULL);
27907   f_host_infos = g_hash_table_new_full (g_str_hash, g_str_equal,
27908                                       g_free, NULL);
27909   f_host_logs = g_hash_table_new_full (g_str_hash, g_str_equal,
27910                                       g_free, NULL);
27911   f_host_false_positives = g_hash_table_new_full (g_str_hash, g_str_equal,
27912                                                   g_free, NULL);
27913 
27914   if (delta && get->details)
27915     {
27916       if (print_report_delta_xml (out, &results, &delta_results, delta_states,
27917                                   ignore_pagination ? 1 : first_result,
27918                                   ignore_pagination ? -1 : max_results,
27919                                   task, notes,
27920                                   notes_details, overrides, overrides_details,
27921                                   sort_order, sort_field, result_hosts_only,
27922                                   &orig_filtered_result_count,
27923                                   &filtered_result_count,
27924                                   &orig_f_holes, &f_holes,
27925                                   &orig_f_infos, &f_infos,
27926                                   &orig_f_logs, &f_logs,
27927                                   &orig_f_warnings, &f_warnings,
27928                                   &orig_f_false_positives, &f_false_positives,
27929                                   result_hosts))
27930         {
27931           fclose (out);
27932           g_free (sort_field);
27933           g_free (levels);
27934           g_free (search_phrase);
27935           g_free (min_qod);
27936           g_free (delta_states);
27937           cleanup_iterator (&results);
27938           cleanup_iterator (&delta_results);
27939           tz_revert (zone, tz, old_tz_override);
27940           g_hash_table_destroy (f_host_ports);
27941           g_hash_table_destroy (f_host_holes);
27942           g_hash_table_destroy (f_host_warnings);
27943           g_hash_table_destroy (f_host_infos);
27944           g_hash_table_destroy (f_host_logs);
27945           g_hash_table_destroy (f_host_false_positives);
27946 
27947           return -1;
27948         }
27949     }
27950   else if (get->details)
27951     {
27952       int cert_loaded;
27953 
27954       cert_loaded = manage_cert_loaded ();
27955       while (next (&results))
27956         {
27957           const char* level;
27958           GHashTable *f_host_result_counts;
27959           GString *buffer = g_string_new ("");
27960           double result_severity;
27961 
27962           buffer_results_xml (buffer,
27963                               &results,
27964                               task,
27965                               notes,
27966                               notes_details,
27967                               overrides,
27968                               overrides_details,
27969                               result_tags,
27970                               1,
27971                               0,
27972                               NULL,
27973                               NULL,
27974                               0,
27975                               cert_loaded,
27976                               lean);
27977           PRINT_XML (out, buffer->str);
27978           g_string_free (buffer, TRUE);
27979           if (result_hosts_only)
27980             array_add_new_string (result_hosts,
27981                                   result_iterator_host (&results));
27982 
27983           result_severity = result_iterator_severity_double (&results);
27984           if (result_severity > f_severity)
27985             f_severity = result_severity;
27986 
27987           level = result_iterator_level (&results);
27988           if (strcasecmp (level, "log") == 0)
27989             {
27990               f_host_result_counts = f_host_logs;
27991               if (count_filtered)
27992                 f_logs++;
27993             }
27994           else if (strcasecmp (level, "high") == 0)
27995             {
27996               f_host_result_counts = f_host_holes;
27997               if (count_filtered)
27998                 f_holes++;
27999             }
28000           else if (strcasecmp (level, "medium") == 0)
28001             {
28002               f_host_result_counts = f_host_warnings;
28003               if (count_filtered)
28004                 f_warnings++;
28005             }
28006           else if (strcasecmp (level, "low") == 0)
28007             {
28008               f_host_result_counts = f_host_infos;
28009               if (count_filtered)
28010                 f_infos++;
28011             }
28012           else if (strcasecmp (level, "false positive") == 0)
28013             {
28014               f_host_result_counts = f_host_false_positives;
28015               if (count_filtered)
28016                 f_false_positives++;
28017             }
28018           else
28019             f_host_result_counts = NULL;
28020 
28021           if (f_host_result_counts)
28022             {
28023               const char *result_host = result_iterator_host (&results);
28024               int result_count
28025                     = GPOINTER_TO_INT
28026                         (g_hash_table_lookup (f_host_result_counts, result_host));
28027 
28028               g_hash_table_replace (f_host_result_counts,
28029                                     g_strdup (result_host),
28030                                     GINT_TO_POINTER (result_count + 1));
28031             }
28032 
28033         }
28034       PRINT (out, "</results>");
28035     }
28036   if (get->details)
28037     cleanup_iterator (&results);
28038   if (delta && get->details)
28039     cleanup_iterator (&delta_results);
28040 
28041   /* Print result counts and severity. */
28042 
28043   if (delta)
28044     /** @todo The f_holes, etc. vars are setup to give the page count. */
28045     PRINT (out,
28046            "<result_count>"
28047            "<filtered>%i</filtered>"
28048            "<hole><filtered>%i</filtered></hole>"
28049            "<info><filtered>%i</filtered></info>"
28050            "<log><filtered>%i</filtered></log>"
28051            "<warning><filtered>%i</filtered></warning>"
28052            "<false_positive>"
28053            "<filtered>%i</filtered>"
28054            "</false_positive>"
28055            "</result_count>",
28056            orig_filtered_result_count,
28057            (strchr (levels, 'h') ? orig_f_holes : 0),
28058            (strchr (levels, 'l') ? orig_f_infos : 0),
28059            (strchr (levels, 'g') ? orig_f_logs : 0),
28060            (strchr (levels, 'm') ? orig_f_warnings : 0),
28061            (strchr (levels, 'f') ? orig_f_false_positives : 0));
28062   else
28063     {
28064       if (count_filtered)
28065         filtered_result_count = f_holes + f_infos + f_logs
28066                                 + f_warnings + false_positives;
28067 
28068       PRINT (out,
28069              "<result_count>"
28070              "%i"
28071              "<full>%i</full>"
28072              "<filtered>%i</filtered>"
28073              "<hole><full>%i</full><filtered>%i</filtered></hole>"
28074              "<info><full>%i</full><filtered>%i</filtered></info>"
28075              "<log><full>%i</full><filtered>%i</filtered></log>"
28076              "<warning><full>%i</full><filtered>%i</filtered></warning>"
28077              "<false_positive>"
28078              "<full>%i</full>"
28079              "<filtered>%i</filtered>"
28080              "</false_positive>"
28081              "</result_count>",
28082              total_result_count,
28083              total_result_count,
28084              filtered_result_count,
28085              holes,
28086              (strchr (levels, 'h') ? f_holes : 0),
28087              infos,
28088              (strchr (levels, 'l') ? f_infos : 0),
28089              logs,
28090              (strchr (levels, 'g') ? f_logs : 0),
28091              warnings,
28092              (strchr (levels, 'm') ? f_warnings : 0),
28093              false_positives,
28094              (strchr (levels, 'f') ? f_false_positives : 0));
28095 
28096       PRINT (out,
28097              "<severity>"
28098              "<full>%1.1f</full>"
28099              "<filtered>%1.1f</filtered>"
28100              "</severity>",
28101              severity,
28102              f_severity);
28103     }
28104 
28105   if (host_summary)
28106     {
28107       host_summary_buffer = g_string_new ("");
28108       g_string_append_printf (host_summary_buffer,
28109                               "   %-15s   %-16s   End\n",
28110                               "Host", "Start");
28111     }
28112   else
28113     host_summary_buffer = NULL;
28114 
28115   if (get->details && result_hosts_only)
28116     {
28117       gchar *result_host;
28118       int index = 0;
28119       array_terminate (result_hosts);
28120       while ((result_host = g_ptr_array_index (result_hosts, index++)))
28121         {
28122           gboolean present;
28123           iterator_t hosts;
28124           init_report_host_iterator (&hosts, report, result_host, 0);
28125           present = next (&hosts);
28126           if (delta && (present == FALSE))
28127             {
28128               cleanup_iterator (&hosts);
28129               init_report_host_iterator (&hosts, delta, result_host, 0);
28130               present = next (&hosts);
28131             }
28132           if (present)
28133             {
28134               const char *current_host;
28135               int ports_count;
28136               int holes_count, warnings_count, infos_count;
28137               int logs_count, false_positives_count;
28138 
28139               current_host = host_iterator_host (&hosts);
28140 
28141               ports_count
28142                 = GPOINTER_TO_INT
28143                     (g_hash_table_lookup (f_host_ports, current_host));
28144               holes_count
28145                 = GPOINTER_TO_INT
28146                     (g_hash_table_lookup ( f_host_holes, current_host));
28147               warnings_count
28148                 = GPOINTER_TO_INT
28149                     (g_hash_table_lookup ( f_host_warnings, current_host));
28150               infos_count
28151                 = GPOINTER_TO_INT
28152                     (g_hash_table_lookup ( f_host_infos, current_host));
28153               logs_count
28154                 = GPOINTER_TO_INT
28155                     (g_hash_table_lookup ( f_host_logs, current_host));
28156               false_positives_count
28157                 = GPOINTER_TO_INT
28158                     (g_hash_table_lookup ( f_host_false_positives, current_host));
28159 
28160               host_summary_append (host_summary_buffer,
28161                                    result_host,
28162                                    host_iterator_start_time (&hosts),
28163                                    host_iterator_end_time (&hosts));
28164               PRINT (out,
28165                      "<host>"
28166                      "<ip>%s</ip>",
28167                      result_host);
28168 
28169               if (host_iterator_asset_uuid (&hosts)
28170                   && strlen (host_iterator_asset_uuid (&hosts)))
28171                 PRINT (out,
28172                        "<asset asset_id=\"%s\"/>",
28173                        host_iterator_asset_uuid (&hosts));
28174               else if (lean == 0)
28175                 PRINT (out,
28176                        "<asset asset_id=\"\"/>");
28177 
28178               PRINT (out,
28179                      "<start>%s</start>"
28180                      "<end>%s</end>"
28181                      "<port_count><page>%d</page></port_count>"
28182                      "<result_count>"
28183                      "<page>%d</page>"
28184                      "<hole><page>%d</page></hole>"
28185                      "<warning><page>%d</page></warning>"
28186                      "<info><page>%d</page></info>"
28187                      "<log><page>%d</page></log>"
28188                      "<false_positive><page>%d</page></false_positive>"
28189                      "</result_count>",
28190                      host_iterator_start_time (&hosts),
28191                      host_iterator_end_time (&hosts)
28192                        ? host_iterator_end_time (&hosts)
28193                        : "",
28194                      ports_count,
28195                      (holes_count + warnings_count + infos_count
28196                       + logs_count + false_positives_count),
28197                      holes_count,
28198                      warnings_count,
28199                      infos_count,
28200                      logs_count,
28201                      false_positives_count);
28202 
28203               if (print_report_host_details_xml
28204                    (host_iterator_report_host (&hosts), out, lean))
28205                 {
28206                   tz_revert (zone, tz, old_tz_override);
28207                   if (host_summary_buffer)
28208                     g_string_free (host_summary_buffer, TRUE);
28209                   g_hash_table_destroy (f_host_ports);
28210                   g_hash_table_destroy (f_host_holes);
28211                   g_hash_table_destroy (f_host_warnings);
28212                   g_hash_table_destroy (f_host_infos);
28213                   g_hash_table_destroy (f_host_logs);
28214                   g_hash_table_destroy (f_host_false_positives);
28215                   return -1;
28216                 }
28217 
28218               PRINT (out,
28219                      "</host>");
28220             }
28221           cleanup_iterator (&hosts);
28222         }
28223       array_free (result_hosts);
28224     }
28225   else if (get->details)
28226     {
28227       iterator_t hosts;
28228       init_report_host_iterator (&hosts, report, NULL, 0);
28229       while (next (&hosts))
28230         {
28231           const char *current_host;
28232           int ports_count;
28233           int holes_count, warnings_count, infos_count;
28234           int logs_count, false_positives_count;
28235 
28236           current_host = host_iterator_host (&hosts);
28237 
28238           ports_count
28239             = GPOINTER_TO_INT
28240                 (g_hash_table_lookup (f_host_ports, current_host));
28241           holes_count
28242             = GPOINTER_TO_INT
28243                 (g_hash_table_lookup (f_host_holes, current_host));
28244           warnings_count
28245             = GPOINTER_TO_INT
28246                 (g_hash_table_lookup (f_host_warnings, current_host));
28247           infos_count
28248             = GPOINTER_TO_INT
28249                 (g_hash_table_lookup (f_host_infos, current_host));
28250           logs_count
28251             = GPOINTER_TO_INT
28252                 (g_hash_table_lookup (f_host_logs, current_host));
28253           false_positives_count
28254             = GPOINTER_TO_INT
28255                 (g_hash_table_lookup (f_host_false_positives, current_host));
28256 
28257           host_summary_append (host_summary_buffer,
28258                                host_iterator_host (&hosts),
28259                                host_iterator_start_time (&hosts),
28260                                host_iterator_end_time (&hosts));
28261           PRINT (out,
28262                  "<host>"
28263                  "<ip>%s</ip>",
28264                  host_iterator_host (&hosts));
28265 
28266           if (host_iterator_asset_uuid (&hosts)
28267               && strlen (host_iterator_asset_uuid (&hosts)))
28268             PRINT (out,
28269                    "<asset asset_id=\"%s\"/>",
28270                    host_iterator_asset_uuid (&hosts));
28271           else if (lean == 0)
28272             PRINT (out,
28273                    "<asset asset_id=\"\"/>");
28274 
28275           PRINT (out,
28276                  "<start>%s</start>"
28277                  "<end>%s</end>"
28278                  "<port_count><page>%d</page></port_count>"
28279                  "<result_count>"
28280                  "<page>%d</page>"
28281                  "<hole><page>%d</page></hole>"
28282                  "<warning><page>%d</page></warning>"
28283                  "<info><page>%d</page></info>"
28284                  "<log><page>%d</page></log>"
28285                  "<false_positive><page>%d</page></false_positive>"
28286                  "</result_count>",
28287                  host_iterator_start_time (&hosts),
28288                  host_iterator_end_time (&hosts)
28289                    ? host_iterator_end_time (&hosts)
28290                    : "",
28291                  ports_count,
28292                  (holes_count + warnings_count + infos_count
28293                   + logs_count + false_positives_count),
28294                  holes_count,
28295                  warnings_count,
28296                  infos_count,
28297                  logs_count,
28298                  false_positives_count);
28299 
28300           if (print_report_host_details_xml
28301                (host_iterator_report_host (&hosts), out, lean))
28302             {
28303               tz_revert (zone, tz, old_tz_override);
28304               if (host_summary_buffer)
28305                 g_string_free (host_summary_buffer, TRUE);
28306               g_hash_table_destroy (f_host_ports);
28307               g_hash_table_destroy (f_host_holes);
28308               g_hash_table_destroy (f_host_warnings);
28309               g_hash_table_destroy (f_host_infos);
28310               g_hash_table_destroy (f_host_logs);
28311               g_hash_table_destroy (f_host_false_positives);
28312               return -1;
28313             }
28314 
28315           PRINT (out,
28316                  "</host>");
28317         }
28318       cleanup_iterator (&hosts);
28319     }
28320 
28321   g_hash_table_destroy (f_host_ports);
28322   g_hash_table_destroy (f_host_holes);
28323   g_hash_table_destroy (f_host_warnings);
28324   g_hash_table_destroy (f_host_infos);
28325   g_hash_table_destroy (f_host_logs);
28326   g_hash_table_destroy (f_host_false_positives);
28327 
28328   end_time = scan_end_time (report);
28329   PRINT (out,
28330            "<scan_end>%s</scan_end>",
28331            end_time);
28332   free (end_time);
28333 
28334   if (delta == 0 && print_report_errors_xml (report, out))
28335     {
28336       tz_revert (zone, tz, old_tz_override);
28337       if (host_summary_buffer)
28338         g_string_free (host_summary_buffer, TRUE);
28339       return -1;
28340     }
28341 
28342   g_free (sort_field);
28343   g_free (levels);
28344   g_free (search_phrase);
28345   g_free (min_qod);
28346   g_free (delta_states);
28347 
28348   if (host_summary && host_summary_buffer)
28349     *host_summary = g_string_free (host_summary_buffer, FALSE);
28350 
28351   if (fclose (out))
28352     {
28353       g_warning ("%s: fclose failed: %s",
28354                  __func__,
28355                  strerror (errno));
28356       return -1;
28357     }
28358 
28359   return 0;
28360 }
28361 
28362 /**
28363  * @brief Generate a report.
28364  *
28365  * @param[in]  report             Report.
28366  * @param[in]  delta_report       Report to compare with.
28367  * @param[in]  get                GET data for report.
28368  * @param[in]  report_format      Report format.
28369  * @param[in]  notes_details      If notes, Whether to include details.
28370  * @param[in]  overrides_details  If overrides, Whether to include details.
28371  * @param[out] output_length      NULL or location for length of return.
28372  * @param[out] extension          NULL or location for report format extension.
28373  *                                Only defined on success.
28374  * @param[out] content_type       NULL or location for report format content
28375  *                                type.  Only defined on success.
28376  * @param[out] filter_term_return  Filter term used in report.
28377  * @param[out] zone_return         Actual timezone used in report.
28378  * @param[out] host_summary    Summary of results per host.
28379  *
28380  * @return Contents of report on success, NULL on error.
28381  */
28382 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)28383 manage_report (report_t report, report_t delta_report, const get_data_t *get,
28384                const report_format_t report_format,
28385                int notes_details, int overrides_details,
28386                gsize *output_length, gchar **extension, gchar **content_type,
28387                gchar **filter_term_return, gchar **zone_return,
28388                gchar **host_summary)
28389 {
28390   task_t task;
28391   gchar *xml_start, *xml_file, *output_file;
28392   char xml_dir[] = "/tmp/gvmd_XXXXXX";
28393   int ret;
28394   GList *used_rfps;
28395   GError *get_error;
28396   gchar *output;
28397   gsize output_len;
28398 
28399   used_rfps = NULL;
28400 
28401   /* Print the report as XML to a file. */
28402 
28403   if (((report_format_predefined (report_format) == 0)
28404        && (report_format_trust (report_format) != TRUST_YES))
28405       || (report_task (report, &task)))
28406     {
28407       return NULL;
28408     }
28409 
28410   if (mkdtemp (xml_dir) == NULL)
28411     {
28412       g_warning ("%s: mkdtemp failed", __func__);
28413       return NULL;
28414     }
28415 
28416   xml_start = g_strdup_printf ("%s/report-start.xml", xml_dir);
28417   ret = print_report_xml_start (report, delta_report, task, xml_start, get,
28418                                 notes_details, overrides_details,
28419                                 1 /* result_tags */,
28420                                 0 /* ignore_pagination */,
28421                                 0 /* lean */,
28422                                 filter_term_return, zone_return, host_summary);
28423   if (ret)
28424     {
28425       g_free (xml_start);
28426       gvm_file_remove_recurse (xml_dir);
28427       return NULL;
28428     }
28429 
28430   xml_file = g_strdup_printf ("%s/report.xml", xml_dir);
28431 
28432   if (report_format > 0)
28433     {
28434       /* Apply report format(s) */
28435       gchar* report_format_id = report_format_uuid (report_format);
28436 
28437       output_file = apply_report_format (report_format_id,
28438                                          xml_start, xml_file, xml_dir,
28439                                          &used_rfps);
28440       g_free (report_format_id);
28441     }
28442   else
28443     {
28444       print_report_xml_end(xml_start, xml_file, -1);
28445       output_file = g_strdup(xml_file);
28446     }
28447 
28448   if (output_file == NULL)
28449     {
28450       g_warning ("%s: No file returned for report format", __func__);
28451     }
28452   g_free (xml_file);
28453   g_free (xml_start);
28454 
28455   /* Read the script output from file. */
28456   if (output_file == NULL)
28457     {
28458       gvm_file_remove_recurse (xml_dir);
28459       return NULL;
28460     }
28461 
28462   get_error = NULL;
28463   g_file_get_contents (output_file,
28464                        &output,
28465                        &output_len,
28466                        &get_error);
28467   g_free (output_file);
28468   if (get_error)
28469     {
28470       g_warning ("%s: Failed to get output: %s",
28471                   __func__,
28472                   get_error->message);
28473       g_error_free (get_error);
28474       if (extension) g_free (*extension);
28475       if (content_type) g_free (*content_type);
28476       gvm_file_remove_recurse (xml_dir);
28477       return NULL;
28478     }
28479 
28480   /* Remove the directory. */
28481 
28482   gvm_file_remove_recurse (xml_dir);
28483 
28484   /* Set convenience return parameters. */
28485 
28486   if ((report_format > 0) && (extension || content_type))
28487     {
28488       iterator_t formats;
28489       get_data_t report_format_get;
28490 
28491       memset (&report_format_get, '\0', sizeof (report_format_get));
28492       report_format_get.id = report_format_uuid(report_format);
28493 
28494       init_report_format_iterator (&formats, &report_format_get);
28495       if (next (&formats) == FALSE)
28496         {
28497           g_free (report_format_get.id);
28498           cleanup_iterator (&formats);
28499           return NULL;
28500         }
28501 
28502       assert (report_format_iterator_extension (&formats));
28503       assert (report_format_iterator_content_type (&formats));
28504       if (extension)
28505         *extension = g_strdup (report_format_iterator_extension (&formats));
28506       if (content_type)
28507         *content_type = g_strdup (report_format_iterator_content_type (&formats));
28508 
28509       cleanup_iterator (&formats);
28510       g_free (report_format_get.id);
28511     }
28512 
28513 
28514   /* Return the output. */
28515 
28516   if (output_length) *output_length = output_len;
28517 
28518   return output;
28519 }
28520 
28521 /**
28522  * @brief Size of base64 chunk in manage_send_report.
28523  */
28524 #define MANAGE_SEND_REPORT_CHUNK64_SIZE 262144
28525 
28526 /**
28527  * @brief Size of file chunk in manage_send_report.
28528  */
28529 #define MANAGE_SEND_REPORT_CHUNK_SIZE (MANAGE_SEND_REPORT_CHUNK64_SIZE * 3 / 4)
28530 
28531 /**
28532  * @brief Generate a report.
28533  *
28534  * @param[in]  report             Report.
28535  * @param[in]  delta_report       Report to compare with.
28536  * @param[in]  report_format      Report format.
28537  * @param[in]  get                GET command data.
28538  * @param[in]  notes_details      If notes, Whether to include details.
28539  * @param[in]  overrides_details  If overrides, Whether to include details.
28540  * @param[in]  result_tags        Whether to include tags in results.
28541  * @param[in]  ignore_pagination  Whether to ignore pagination.
28542  * @param[in]  lean               Whether to send lean report.
28543  * @param[in]  base64             Whether to base64 encode the report.
28544  * @param[in]  send               Function to write to client.
28545  * @param[in]  send_data_1        Second argument to \p send.
28546  * @param[in]  send_data_2        Third argument to \p send.
28547  * @param[in]  alert_id       ID of alert to escalate report with,
28548  *                                instead of getting report.  NULL to get
28549  *                                report.
28550  * @param[in]  prefix              Text to send to client before the report.
28551  *
28552  * @return 0 success, -1 error, -2 failed to find alert report format, -3 error
28553  *         during alert, -4 failed to find alert filter, 1 failed to find alert,
28554  *         2 failed to find filter (before anything sent to client).
28555  */
28556 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)28557 manage_send_report (report_t report, report_t delta_report,
28558                     report_format_t report_format, const get_data_t *get,
28559                     int notes_details, int overrides_details, int result_tags,
28560                     int ignore_pagination, int lean, int base64,
28561                     gboolean (*send) (const char *,
28562                                       int (*) (const char *, void*),
28563                                       void*),
28564                     int (*send_data_1) (const char *, void*), void *send_data_2,
28565                     const char *alert_id,
28566                     const gchar* prefix)
28567 {
28568   task_t task;
28569   gchar *xml_start, *xml_file;
28570   char xml_dir[] = "/tmp/gvmd_XXXXXX";
28571   int ret;
28572   GList *used_rfps;
28573   gchar *output_file;
28574   char chunk[MANAGE_SEND_REPORT_CHUNK_SIZE + 1];
28575   FILE *stream;
28576 
28577   used_rfps = NULL;
28578 
28579   if (report_task (report, &task))
28580     return -1;
28581 
28582   /* Escalate instead, if requested. */
28583 
28584   if (alert_id)
28585     {
28586       alert_t alert = 0;
28587       alert_condition_t condition;
28588       alert_method_t method;
28589 
28590       if (find_alert_with_permission (alert_id, &alert, "get_alerts"))
28591         return -1;
28592 
28593       if (alert == 0)
28594         return 1;
28595 
28596       condition = alert_condition (alert);
28597       method = alert_method (alert);
28598 
28599       ret = escalate_2 (alert, task, report, EVENT_TASK_RUN_STATUS_CHANGED,
28600                         (void*) TASK_STATUS_DONE, method, condition,
28601                         get, notes_details, overrides_details, NULL);
28602       if (ret == -3)
28603         return -4;
28604       if (ret == -1)
28605         return -3;
28606       if (ret)
28607         return -2;
28608       return 0;
28609     }
28610 
28611   /* Print the report as XML to a file. */
28612 
28613   if ((report_format > 0)
28614       && (report_format_predefined (report_format) == 0)
28615       && (report_format_trust (report_format) != TRUST_YES))
28616     return -1;
28617 
28618   if (mkdtemp (xml_dir) == NULL)
28619     {
28620       g_warning ("%s: mkdtemp failed", __func__);
28621       return -1;
28622     }
28623 
28624   xml_start = g_strdup_printf ("%s/report-start.xml", xml_dir);
28625   ret = print_report_xml_start (report, delta_report, task, xml_start, get,
28626                                 notes_details, overrides_details, result_tags,
28627                                 ignore_pagination, lean, NULL, NULL, NULL);
28628   if (ret)
28629     {
28630       g_free (xml_start);
28631       gvm_file_remove_recurse (xml_dir);
28632       if (ret == 2)
28633         return 2;
28634       return -1;
28635     }
28636 
28637   xml_file = g_strdup_printf ("%s/report.xml", xml_dir);
28638 
28639   if (report_format > 0)
28640     {
28641       /* Apply report format(s). */
28642       gchar* report_format_id = report_format_uuid (report_format);
28643 
28644       output_file = apply_report_format (report_format_id,
28645                                          xml_start, xml_file, xml_dir,
28646                                          &used_rfps);
28647       g_free (report_format_id);
28648     }
28649   else
28650     {
28651       print_report_xml_end(xml_start, xml_file, -1);
28652       output_file = g_strdup(xml_file);
28653     }
28654 
28655   if (output_file == NULL)
28656     {
28657       g_warning ("%s: No file returned for report format", __func__);
28658     }
28659 
28660   /* Send the report. */
28661 
28662   /* Read the script output from file in chunks, sending to client. */
28663 
28664   stream = fopen (output_file, "r");
28665   g_free (output_file);
28666   if (stream == NULL)
28667     {
28668       g_warning ("%s: %s",
28669                   __func__,
28670                   strerror (errno));
28671       gvm_file_remove_recurse (xml_dir);
28672       return -1;
28673     }
28674 
28675   if (prefix && send (prefix, send_data_1, send_data_2))
28676     {
28677       fclose (stream);
28678       g_warning ("%s: send prefix error", __func__);
28679       gvm_file_remove_recurse (xml_dir);
28680       return -1;
28681     }
28682 
28683   while (1)
28684     {
28685       int left;
28686       char *dest;
28687 
28688       /* Read a chunk. */
28689 
28690       left = MANAGE_SEND_REPORT_CHUNK_SIZE;
28691       dest = chunk;
28692       while (1)
28693         {
28694           ret = fread (dest, 1, left, stream);
28695           if (ferror (stream))
28696             {
28697               fclose (stream);
28698               g_warning ("%s: error after fread", __func__);
28699               gvm_file_remove_recurse (xml_dir);
28700               return -1;
28701             }
28702           left -= ret;
28703           if (left == 0)
28704             break;
28705           if (feof (stream))
28706             break;
28707           dest += ret;
28708         }
28709 
28710       /* Send the chunk. */
28711 
28712       if (left < MANAGE_SEND_REPORT_CHUNK_SIZE)
28713         {
28714           if (base64)
28715             {
28716               gchar *chunk64;
28717               chunk64 = g_base64_encode ((guchar*) chunk,
28718                                           MANAGE_SEND_REPORT_CHUNK_SIZE
28719                                           - left);
28720               if (send (chunk64, send_data_1, send_data_2))
28721                 {
28722                   g_free (chunk64);
28723                   fclose (stream);
28724                   g_warning ("%s: send error", __func__);
28725                   gvm_file_remove_recurse (xml_dir);
28726                   return -1;
28727                 }
28728               g_free (chunk64);
28729             }
28730           else
28731             {
28732               chunk[MANAGE_SEND_REPORT_CHUNK_SIZE - left] = '\0';
28733               if (send (chunk, send_data_1, send_data_2))
28734                 {
28735                   fclose (stream);
28736                   g_warning ("%s: send error", __func__);
28737                   gvm_file_remove_recurse (xml_dir);
28738                   return -1;
28739                 }
28740             }
28741         }
28742 
28743       /* Check if there's more. */
28744 
28745       if (feof (stream))
28746         break;
28747     }
28748 
28749   fclose (stream);
28750 
28751   /* Remove the directory. */
28752 
28753   gvm_file_remove_recurse (xml_dir);
28754 
28755   /* Return the output. */
28756 
28757   return 0;
28758 }
28759 
28760 /**
28761  * @brief Get the IP of a host, using the 'hostname' report host details.
28762  *
28763  * The most recent host detail takes preference.
28764  *
28765  * @param[in]  host  Host name or IP.
28766  *
28767  * @return Newly allocated UUID if available, else NULL.
28768  */
28769 gchar*
report_host_ip(const char * host)28770 report_host_ip (const char *host)
28771 {
28772   gchar *quoted_host, *ret;
28773   quoted_host = sql_quote (host);
28774   ret = sql_string ("SELECT host FROM report_hosts"
28775                     " WHERE id = (SELECT report_host FROM report_host_details"
28776                     "             WHERE name = 'hostname'"
28777                     "             AND value = '%s'"
28778                     "             ORDER BY id DESC LIMIT 1);",
28779                     quoted_host);
28780   g_free (quoted_host);
28781   return ret;
28782 }
28783 
28784 /**
28785  * @brief Check if a report host is alive and has at least one result.
28786  *
28787  * @param[in]  report  Report.
28788  * @param[in]  host    Host name or IP.
28789  *
28790  * @return 0 if dead, else alive.
28791  */
28792 int
report_host_noticeable(report_t report,const gchar * host)28793 report_host_noticeable (report_t report, const gchar *host)
28794 {
28795   report_host_t report_host = 0;
28796 
28797   sql_int64 (&report_host,
28798              "SELECT id FROM report_hosts"
28799              " WHERE report = %llu AND host = '%s';",
28800              report,
28801              host);
28802 
28803   return report_host
28804          && report_host_dead (report_host) == 0
28805          && report_host_result_count (report_host) > 0;
28806 }
28807 
28808 /**
28809  * @brief Parse an OSP report.
28810  *
28811  * @param[in]  task        Task.
28812  * @param[in]  report      Report.
28813  * @param[in]  report_xml  Report XML.
28814  */
28815 void
parse_osp_report(task_t task,report_t report,const char * report_xml)28816 parse_osp_report (task_t task, report_t report, const char *report_xml)
28817 {
28818   entity_t entity, child;
28819   entities_t results;
28820   const char *str;
28821   char *defs_file = NULL;
28822   time_t start_time, end_time;
28823   gboolean has_results = FALSE;
28824   GArray *results_array;
28825 
28826   assert (task);
28827   assert (report);
28828   assert (report_xml);
28829 
28830   if (parse_entity (report_xml, &entity))
28831     {
28832       g_warning ("Couldn't parse %s OSP scan report", report_xml);
28833       return;
28834     }
28835 
28836   sql_begin_immediate ();
28837   /* Set the report's start and end times. */
28838   results_array = g_array_new (TRUE, TRUE, sizeof (result_t));
28839   start_time = 0;
28840   str = entity_attribute (entity, "start_time");
28841   if (str)
28842     {
28843       start_time = atoi (str);
28844       set_scan_start_time_epoch (report, start_time);
28845     }
28846 
28847   end_time = 0;
28848   str = entity_attribute (entity, "end_time");
28849   if (str)
28850     {
28851       end_time = atoi (str);
28852       set_scan_end_time_epoch (report, end_time);
28853     }
28854 
28855   /* Insert results. */
28856   child = entity_child (entity, "results");
28857   if (!child)
28858     {
28859       g_warning ("Missing results element in OSP report %s", report_xml);
28860       goto end_parse_osp_report;
28861     }
28862   results = child->entities;
28863   if (results)
28864     has_results = TRUE;
28865 
28866   defs_file = task_definitions_file (task);
28867   while (results)
28868     {
28869       result_t result;
28870       const char *type, *name, *severity, *host, *hostname, *test_id, *port;
28871       const char *qod, *path;
28872       char *desc = NULL, *nvt_id = NULL, *severity_str = NULL;
28873       entity_t r_entity = results->data;
28874       int qod_int;
28875 
28876       if (strcmp (entity_name (r_entity), "result"))
28877         {
28878           g_warning ("Erroneous entry in OSP results %s",
28879                      entity_name (r_entity));
28880           results = next_entities (results);
28881           continue;
28882         }
28883       type = entity_attribute (r_entity, "type");
28884       name = entity_attribute (r_entity, "name");
28885       severity = entity_attribute (r_entity, "severity");
28886       test_id = entity_attribute (r_entity, "test_id");
28887       host = entity_attribute (r_entity, "host");
28888       hostname = entity_attribute (r_entity, "hostname");
28889       port = entity_attribute (r_entity, "port") ?: "";
28890       qod = entity_attribute (r_entity, "qod") ?: "";
28891       path = entity_attribute (r_entity, "uri") ?: "";
28892 
28893       if (!name || !type || !severity || !test_id || !host)
28894         {
28895           GString *string = g_string_new ("");
28896 
28897           print_entity_to_string (r_entity, string);
28898           g_warning ("Erroneous attribute in OSP result %s", string->str);
28899           g_string_free (string, TRUE);
28900           results = next_entities (results);
28901           continue;
28902         }
28903 
28904       /* Add report host if it doesn't exist. */
28905       manage_report_host_add (report, host, start_time, end_time);
28906       if (!strcmp (type, "Host Detail"))
28907         {
28908           insert_report_host_detail (report, host, "osp", "", "OSP Host Detail",
28909                                      name, entity_text (r_entity));
28910           results = next_entities (results);
28911           continue;
28912         }
28913       else if (g_str_has_prefix (test_id, "1.3.6.1.4.1.25623.1."))
28914         {
28915           nvt_id = g_strdup (test_id);
28916           severity_str = nvt_severity (test_id, type);
28917           desc = g_strdup (entity_text (r_entity));
28918         }
28919       else if (g_str_has_prefix (test_id, "oval:"))
28920         {
28921           nvt_id = ovaldef_uuid (test_id, defs_file);
28922           severity_str = ovaldef_severity (nvt_id);
28923         }
28924       else
28925         {
28926           nvt_id = g_strdup (name);
28927           desc = g_strdup (entity_text (r_entity));
28928         }
28929 
28930       qod_int = atoi (qod);
28931       if (qod_int <= 0 || qod_int > 100)
28932         qod_int = QOD_DEFAULT;
28933       if (port && strcmp (port, "general/Host_Details") == 0)
28934         {
28935           /* TODO: This should probably be handled by the "Host Detail"
28936            *        result type with extra source info in OSP.
28937            */
28938           if (manage_report_host_detail (report, host, desc))
28939             g_warning ("%s: Failed to add report detail for host '%s': %s",
28940                       __func__,
28941                       host,
28942                       desc);
28943         }
28944       else if (host && nvt_id && desc && (strcmp (nvt_id, "HOST_START") == 0))
28945         {
28946           set_scan_host_start_time_ctime (report, host, desc);
28947         }
28948       else if (host && nvt_id && desc && (strcmp (nvt_id, "HOST_END") == 0))
28949         {
28950           set_scan_host_end_time_ctime (report, host, desc);
28951           add_assets_from_host_in_report (report, host);
28952         }
28953       else
28954         {
28955           result = make_osp_result (task,
28956                                     host,
28957                                     hostname,
28958                                     nvt_id,
28959                                     type,
28960                                     desc,
28961                                     port ?: "",
28962                                     severity_str ?: severity,
28963                                     qod_int,
28964                                     path);
28965           g_array_append_val (results_array, result);
28966         }
28967       g_free (nvt_id);
28968       g_free (desc);
28969       g_free (severity_str);
28970       results = next_entities (results);
28971     }
28972 
28973   if (has_results)
28974     {
28975       report_add_results_array (report, results_array);
28976     }
28977 
28978  end_parse_osp_report:
28979   sql_commit ();
28980   g_array_free (results_array, TRUE);
28981   g_free (defs_file);
28982   free_entity (entity);
28983 }
28984 
28985 
28986 /* More task stuff. */
28987 
28988 /**
28989  * @brief Return the trend of a task, given counts.
28990  *
28991  * @param[in]  holes_a   Number of holes on earlier report.
28992  * @param[in]  warns_a   Number of warnings on earlier report.
28993  * @param[in]  infos_a   Number of infos on earlier report.
28994  * @param[in]  severity_a Severity of earlier report.
28995  * @param[in]  holes_b   Number of holes on later report.
28996  * @param[in]  warns_b   Number of warnings on later report.
28997  * @param[in]  infos_b   Number of infos on later report.
28998  * @param[in]  severity_b Severity of later report.
28999  *
29000  * @return "up", "down", "more", "less", "same" or if too few reports "".
29001  */
29002 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)29003 task_trend_calc (int holes_a, int warns_a, int infos_a, double severity_a,
29004                  int holes_b, int warns_b, int infos_b, double severity_b)
29005 {
29006   int threat_a, threat_b;
29007 
29008   /* Check if the severity score changed. */
29009 
29010   if (severity_a > severity_b)
29011     return "up";
29012 
29013   if (severity_a < severity_b)
29014     return "down";
29015 
29016   /* Calculate trend. */
29017 
29018   if (holes_a > 0)
29019     threat_a = 4;
29020   else if (warns_a > 0)
29021     threat_a = 3;
29022   else if (infos_a > 0)
29023     threat_a = 2;
29024   else
29025     threat_a = 1;
29026 
29027   if (holes_b > 0)
29028     threat_b = 4;
29029   else if (warns_b > 0)
29030     threat_b = 3;
29031   else if (infos_b > 0)
29032     threat_b = 2;
29033   else
29034     threat_b = 1;
29035 
29036   /* Check if the threat level changed. */
29037 
29038   if (threat_a > threat_b)
29039     return "up";
29040 
29041   if (threat_a < threat_b)
29042     return "down";
29043 
29044   /* Check if the threat count changed in the highest level. */
29045 
29046   if (holes_a)
29047     {
29048       if (holes_a > holes_b)
29049         return "more";
29050       if (holes_a < holes_b)
29051         return "less";
29052       return "same";
29053     }
29054 
29055   if (warns_a)
29056     {
29057       if (warns_a > warns_b)
29058         return "more";
29059       if (warns_a < warns_b)
29060         return "less";
29061       return "same";
29062     }
29063 
29064   if (infos_a)
29065     {
29066       if (infos_a > infos_b)
29067         return "more";
29068       if (infos_a < infos_b)
29069         return "less";
29070       return "same";
29071     }
29072 
29073   return "same";
29074 }
29075 
29076 /**
29077  * @brief Return the trend of a task, given counts.
29078  *
29079  * @param[in]  iterator  Task iterator.
29080  * @param[in]  holes_a   Number of holes on earlier report.
29081  * @param[in]  warns_a   Number of warnings on earlier report.
29082  * @param[in]  infos_a   Number of infos on earlier report.
29083  * @param[in]  severity_a Severity score of earlier report.
29084  * @param[in]  holes_b   Number of holes on later report.
29085  * @param[in]  warns_b   Number of warnings on later report.
29086  * @param[in]  infos_b   Number of infos on later report.
29087  * @param[in]  severity_b  Severity score of later report.
29088  *
29089  * @return "up", "down", "more", "less", "same" or if too few reports "".
29090  */
29091 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)29092 task_iterator_trend_counts (iterator_t *iterator, int holes_a, int warns_a,
29093                             int infos_a, double severity_a, int holes_b,
29094                             int warns_b, int infos_b, double severity_b)
29095 {
29096   /* Ensure there are enough reports. */
29097   if (task_iterator_finished_reports (iterator) <= 1)
29098     return "";
29099 
29100   /* Skip running tasks. */
29101 
29102   if (task_iterator_run_status (iterator) == TASK_STATUS_RUNNING)
29103     return "";
29104 
29105   return task_trend_calc (holes_a, warns_a, infos_a, severity_a,
29106                           holes_b, warns_b, infos_b, severity_b);
29107 }
29108 
29109 /**
29110  * @brief Make a task.
29111  *
29112  * The char* parameters name and comment are used directly and freed
29113  * when the task is freed.
29114  *
29115  * @param[in]  name       The name of the task.
29116  * @param[in]  comment    A comment associated the task.
29117  * @param[in]  in_assets  Whether task must be considered for assets.
29118  * @param[in]  event      Whether to be generate event and event log.
29119  *
29120  * @return A pointer to the new task.
29121  */
29122 task_t
make_task(char * name,char * comment,int in_assets,int event)29123 make_task (char* name, char* comment, int in_assets, int event)
29124 {
29125   task_t task;
29126   char* uuid = gvm_uuid_make ();
29127   gchar *quoted_name, *quoted_comment;
29128   if (uuid == NULL) abort ();
29129   quoted_name = name ? sql_quote ((gchar*) name) : NULL;
29130   quoted_comment = comment ? sql_quote ((gchar*) comment) : NULL;
29131   sql ("INSERT into tasks"
29132        " (owner, uuid, name, hidden, comment, schedule,"
29133        "  schedule_next_time, config_location, target, target_location,"
29134        "  scanner_location, schedule_location, alterable,"
29135        "  creation_time, modification_time, usage_type)"
29136        " VALUES ((SELECT id FROM users WHERE users.uuid = '%s'),"
29137        "         '%s', '%s', 0, '%s', 0, 0, 0, 0, 0, 0, 0, 0, m_now (),"
29138        "         m_now (), 'scan');",
29139        current_credentials.uuid,
29140        uuid,
29141        quoted_name ? quoted_name : "",
29142        quoted_comment ? quoted_comment : "");
29143   task = sql_last_insert_id ();
29144   if (event)
29145     set_task_run_status (task, TASK_STATUS_NEW);
29146   else
29147     set_task_run_status_internal (task, TASK_STATUS_NEW);
29148   sql ("INSERT INTO task_preferences (task, name, value)"
29149        " VALUES (%llu, 'in_assets', '%s')",
29150        task,
29151        in_assets ? "yes" : "no");
29152   sql ("INSERT INTO task_preferences (task, name, value)"
29153        " VALUES (%llu, 'assets_apply_overrides', 'yes')",
29154        task);
29155   sql ("INSERT INTO task_preferences (task, name, value)"
29156        " VALUES (%llu, 'assets_min_qod', %d)",
29157        task,
29158        MIN_QOD_DEFAULT);
29159   free (uuid);
29160   free (name);
29161   free (comment);
29162   g_free (quoted_name);
29163   g_free (quoted_comment);
29164   return task;
29165 }
29166 
29167 /**
29168  * @brief Complete the creation of a task.
29169  *
29170  * @param[in]  task     The task.
29171  */
29172 void
make_task_complete(task_t task)29173 make_task_complete (task_t task)
29174 {
29175   assert (task);
29176   cache_permissions_for_resource ("task", task, NULL);
29177 
29178   event (EVENT_TASK_RUN_STATUS_CHANGED, (void*) TASK_STATUS_NEW, task, 0);
29179 }
29180 
29181 /**
29182  * @brief Set the name of a task.
29183  *
29184  * @param[in]  task  A task.
29185  * @param[in]  name  New name.
29186  */
29187 void
set_task_name(task_t task,const char * name)29188 set_task_name (task_t task, const char *name)
29189 {
29190   gchar *quoted_name;
29191 
29192   quoted_name = sql_quote (name ? name : "");
29193   sql ("UPDATE tasks SET name = '%s', modification_time = m_now ()"
29194        " WHERE id = %llu;", quoted_name, task);
29195   g_free (quoted_name);
29196 }
29197 
29198 /**
29199  * @brief Set the comment of a task.
29200  *
29201  * @param[in]  task  A task.
29202  * @param[in]  comment  New comment.
29203  */
29204 static void
set_task_comment(task_t task,const char * comment)29205 set_task_comment (task_t task, const char *comment)
29206 {
29207   gchar *quoted_comment;
29208 
29209   assert (comment);
29210 
29211   quoted_comment = sql_quote (comment ? comment : "");
29212   sql ("UPDATE tasks SET comment = '%s', modification_time = m_now ()"
29213        " WHERE id = %llu;", quoted_comment, task);
29214   g_free (quoted_comment);
29215 }
29216 
29217 /**
29218  * @brief Create a task from an existing task.
29219  *
29220  * @param[in]  name        Name of new task.  NULL to copy from existing.
29221  * @param[in]  comment     Comment on new task.  NULL to copy from existing.
29222  * @param[in]  task_id     UUID of existing task.
29223  * @param[in]  alterable   Whether the new task will be alterable. < 0 to
29224  *                         to copy from existing.
29225  * @param[out] new_task    New task.
29226  *
29227  * @return 0 success, 2 failed to find existing task, 99 permission denied,
29228  *         -1 error.
29229  */
29230 int
copy_task(const char * name,const char * comment,const char * task_id,int alterable,task_t * new_task)29231 copy_task (const char* name, const char* comment, const char *task_id,
29232            int alterable, task_t* new_task)
29233 {
29234   task_t new, old;
29235   int ret;
29236 
29237   assert (current_credentials.uuid);
29238 
29239   if (task_id == NULL)
29240     return -1;
29241 
29242   sql_begin_immediate ();
29243 
29244   ret = copy_resource_lock ("task", name, comment, task_id,
29245                             "config, target, schedule, schedule_periods,"
29246                             " scanner, schedule_next_time,"
29247                             " config_location, target_location,"
29248                             " schedule_location, scanner_location,"
29249                             " hosts_ordering, usage_type, alterable",
29250                             1, &new, &old);
29251   if (ret)
29252     {
29253       sql_rollback ();
29254       return ret;
29255     }
29256 
29257   if (alterable >= 0)
29258     sql ("UPDATE tasks SET alterable = %i, hidden = 0 WHERE id = %llu;",
29259          alterable,
29260          new);
29261   else
29262     sql ("UPDATE tasks SET hidden = 0 WHERE id = %llu;",
29263          new);
29264 
29265   set_task_run_status (new, TASK_STATUS_NEW);
29266   sql ("INSERT INTO task_preferences (task, name, value)"
29267        " SELECT %llu, name, value FROM task_preferences"
29268        " WHERE task = %llu;",
29269        new,
29270        old);
29271 
29272   sql ("INSERT INTO task_alerts (task, alert, alert_location)"
29273        " SELECT %llu, alert, alert_location FROM task_alerts"
29274        " WHERE task = %llu;",
29275        new,
29276        old);
29277 
29278   sql ("INSERT INTO permissions"
29279        " (uuid, owner, name, comment, resource_type, resource, resource_uuid,"
29280        "  resource_location, subject_type, subject, subject_location,"
29281        "  creation_time, modification_time)"
29282        " SELECT make_uuid (), (SELECT owner FROM tasks WHERE id = %llu),"
29283        "        name, comment, resource_type, %llu,"
29284        "        (SELECT uuid FROM tasks WHERE id = %llu),"
29285        "        resource_location, subject_type, subject, subject_location,"
29286        "        m_now (), m_now ()"
29287        " FROM permissions"
29288        " WHERE owner = (SELECT owner FROM tasks WHERE id = %llu)"
29289        " AND resource_type = 'task'"
29290        " AND resource_location = " G_STRINGIFY (LOCATION_TABLE)
29291        " AND resource = %llu;",
29292        new,
29293        new,
29294        new,
29295        old,
29296        old);
29297 
29298   if (ret)
29299     {
29300       sql_rollback ();
29301       return ret;
29302     }
29303 
29304   cache_permissions_for_resource ("task", new, NULL);
29305 
29306   sql_commit ();
29307   if (new_task) *new_task = new;
29308   return 0;
29309 }
29310 
29311 /**
29312  * @brief Complete deletion of a task.
29313  *
29314  * This sets up a transaction around the delete.
29315  *
29316  * @param[in]  task      The task.
29317  * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
29318  *
29319  * @return 0 on success, 1 if task is hidden, -1 on error.
29320  */
29321 static int
delete_task_lock(task_t task,int ultimate)29322 delete_task_lock (task_t task, int ultimate)
29323 {
29324   int ret;
29325 
29326   g_debug ("   delete task %llu", task);
29327 
29328   sql_begin_immediate ();
29329 
29330   /* This prevents other processes (for example a START_TASK) from getting
29331    * a reference to a report ID or the task ID, and then using that
29332    * reference to try access the deleted report or task.
29333    *
29334    * If the task is already active then delete_report (via delete_task)
29335    * will fail and rollback. */
29336   if (sql_error ("LOCK table reports IN ACCESS EXCLUSIVE MODE;"))
29337     {
29338       sql_rollback ();
29339       return -1;
29340     }
29341 
29342   if (sql_int ("SELECT hidden FROM tasks WHERE id = %llu;", task))
29343     {
29344       sql_rollback ();
29345       return -1;
29346     }
29347 
29348   ret = delete_task (task, ultimate);
29349   if (ret)
29350     sql_rollback ();
29351   else
29352     sql_commit ();
29353   return ret;
29354 }
29355 
29356 /**
29357  * @brief Request deletion of a task.
29358  *
29359  * Stop the task beforehand with \ref stop_task_internal, if it is running.
29360  *
29361  * Used only for CREATE_TASK in gmp.c.  Always ultimate.
29362  *
29363  * @param[in]  task_pointer  A pointer to the task.
29364  *
29365  * @return 0 if deleted, 1 if delete requested, 2 if task is hidden,
29366  *         -1 if error, -5 if scanner is down.
29367  */
29368 int
request_delete_task(task_t * task_pointer)29369 request_delete_task (task_t* task_pointer)
29370 {
29371   task_t task = *task_pointer;
29372   int hidden;
29373 
29374   g_debug ("   request delete task %llu", task);
29375 
29376   hidden = sql_int ("SELECT hidden from tasks WHERE id = %llu;",
29377                     *task_pointer);
29378 
29379   /* Technically the task could be in the trashcan, if someone gets the UUID
29380    * with GET_TASKS before the CREATE_TASK finishes, and removes the task.
29381    * Pretend it was deleted.  There'll be half a task in the trashcan. */
29382   if (hidden == 2)
29383     return 0;
29384 
29385   if (current_credentials.uuid == NULL) return -1;
29386 
29387   switch (stop_task_internal (task))
29388     {
29389       case 0:    /* Stopped. */
29390         return delete_task_lock (task, 1);
29391       case 1:    /* Stop requested. */
29392         set_task_run_status (task, TASK_STATUS_DELETE_ULTIMATE_REQUESTED);
29393         return 1;
29394       default:   /* Programming error. */
29395         assert (0);
29396       case -1:   /* Error. */
29397         return -1;
29398         break;
29399       case -5:   /* Scanner down. */
29400         return -5;
29401         break;
29402     }
29403 
29404   return 0;
29405 }
29406 
29407 /**
29408  * @brief Request deletion of a task.
29409  *
29410  * Stop the task beforehand with \ref stop_task_internal, if it is running.
29411  *
29412  * This is only used for DELETE_TASK in gmp.c.
29413  *
29414  * @param[in]  task_id   UUID of task.
29415  * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
29416  *
29417  * @return 0 deleted, 1 delete requested, 2 task is hidden, 3 failed to find
29418  *         task, 99 permission denied, -1 error, -5 scanner is down, -7 no CA
29419  *         cert.
29420  */
29421 int
request_delete_task_uuid(const char * task_id,int ultimate)29422 request_delete_task_uuid (const char *task_id, int ultimate)
29423 {
29424   task_t task = 0;
29425 
29426   /* Tasks have special handling for the trashcan.  Other resources have trash
29427    * tables, like targets_trash.  Tasks are marked as trash in the tasks table
29428    * by giving the "hidden" field a value of 2.  This means that the results can
29429    * stay in the results table and will still refer to the correct task.  This
29430    * should all work because there is already handling of the hidden flag
29431    * everywhere else. */
29432 
29433   g_debug ("   request delete task %s", task_id);
29434 
29435   sql_begin_immediate ();
29436 
29437   if (acl_user_may ("delete_task") == 0)
29438     {
29439       sql_rollback ();
29440       return 99;
29441     }
29442 
29443   if (find_task_with_permission (task_id, &task, "delete_task"))
29444     {
29445       sql_rollback ();
29446       return -1;
29447     }
29448 
29449   if (task == 0)
29450     {
29451       if (find_trash_task (task_id, &task))
29452         {
29453           sql_rollback ();
29454           return -1;
29455         }
29456       if (task == 0)
29457         {
29458           sql_rollback ();
29459           return 3;
29460         }
29461       if (ultimate == 0)
29462         {
29463           /* It's already in the trashcan. */
29464           sql_commit ();
29465           return 0;
29466         }
29467 
29468       if (delete_reports (task))
29469         {
29470           sql_rollback ();
29471           return -1;
29472         }
29473 
29474       permissions_set_orphans ("task", task, LOCATION_TRASH);
29475       tags_remove_resource ("task", task, LOCATION_TRASH);
29476       tickets_remove_task (task);
29477 
29478       sql ("DELETE FROM results WHERE task = %llu;", task);
29479       sql ("DELETE FROM task_alerts WHERE task = %llu;", task);
29480       sql ("DELETE FROM task_files WHERE task = %llu;", task);
29481       sql ("DELETE FROM task_preferences WHERE task = %llu;", task);
29482       sql ("DELETE FROM tasks WHERE id = %llu;", task);
29483       sql_commit ();
29484       return 0;
29485     }
29486 
29487   if (current_credentials.uuid == NULL)
29488     {
29489       sql_rollback ();
29490       return -1;
29491     }
29492 
29493   switch (stop_task_internal (task))
29494     {
29495       case 0:    /* Stopped. */
29496         {
29497           int ret;
29498 
29499           if (ultimate)
29500             /* This prevents other processes (for example a START_TASK) from
29501              * getting a reference to a report ID or the task ID, and then using
29502              * that reference to try access the deleted report or task.
29503              *
29504              * If the task is running already then delete_task will lead to
29505              * ROLLBACK. */
29506             sql ("LOCK table reports IN ACCESS EXCLUSIVE MODE;");
29507 
29508           ret = delete_task (task, ultimate);
29509           if (ret)
29510             sql_rollback ();
29511           else
29512             sql_commit ();
29513           return ret;
29514         }
29515       case 1:    /* Stop requested. */
29516         if (ultimate)
29517           set_task_run_status (task,
29518                                TASK_STATUS_DELETE_ULTIMATE_REQUESTED);
29519         else
29520           set_task_run_status (task,
29521                                TASK_STATUS_DELETE_REQUESTED);
29522         sql_commit ();
29523         return 1;
29524       default:   /* Programming error. */
29525         assert (0);
29526       case -1:   /* Error. */
29527         sql_rollback ();
29528         return -1;
29529         break;
29530       case -5:   /* Scanner down. */
29531         sql_rollback ();
29532         return -5;
29533         break;
29534       case -7:   /* No CA cert. */
29535         sql_rollback ();
29536         return -5;
29537         break;
29538     }
29539 
29540   sql_commit ();
29541   return 0;
29542 }
29543 
29544 /**
29545  * @brief Complete deletion of a task.
29546  *
29547  * The caller must do the locking, and must do the hidden check.
29548  *
29549  * The caller must handle the case where the task is already in the trashcan.
29550  *
29551  * @param[in]  task      The task.
29552  * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
29553  *
29554  * @return 0 on success, -1 on error.
29555  */
29556 int
delete_task(task_t task,int ultimate)29557 delete_task (task_t task, int ultimate)
29558 {
29559   g_debug ("   delete task %llu", task);
29560 
29561   assert (current_credentials.uuid);
29562 
29563   if (ultimate)
29564     {
29565       if (delete_reports (task))
29566         return -1;
29567 
29568       permissions_set_orphans ("task", task,
29569                                task_in_trash (task)
29570                                 ? LOCATION_TRASH
29571                                 : LOCATION_TABLE);
29572       tags_remove_resource ("task", task,
29573                             task_in_trash (task)
29574                               ? LOCATION_TRASH
29575                               : LOCATION_TABLE);
29576       tickets_remove_task (task);
29577 
29578       sql ("DELETE FROM results_trash WHERE task = %llu;", task);
29579       sql ("DELETE FROM results WHERE task = %llu;", task);
29580       sql ("DELETE FROM task_alerts WHERE task = %llu;", task);
29581       sql ("DELETE FROM task_files WHERE task = %llu;", task);
29582       sql ("DELETE FROM task_preferences WHERE task = %llu;", task);
29583       sql ("DELETE FROM tasks WHERE id = %llu;", task);
29584     }
29585   else
29586     {
29587       permissions_set_locations ("task", task, task, LOCATION_TRASH);
29588       tags_set_locations ("task", task, task, LOCATION_TRASH);
29589 
29590       sql ("UPDATE tag_resources"
29591            " SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
29592            " WHERE resource_type = 'report'"
29593            " AND resource IN (SELECT id FROM reports"
29594            "                  WHERE reports.task = %llu);",
29595            task);
29596       sql ("UPDATE tag_resources"
29597            " SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
29598            " WHERE resource_type = 'result'"
29599            " AND resource IN (SELECT id FROM results"
29600            "                  WHERE results.task = %llu);",
29601            task);
29602 
29603       sql ("UPDATE tag_resources_trash"
29604            " SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
29605            " WHERE resource_type = 'report'"
29606            " AND resource IN (SELECT id FROM reports"
29607            "                  WHERE reports.task = %llu);",
29608            task);
29609       sql ("UPDATE tag_resources_trash"
29610            " SET resource_location = " G_STRINGIFY (LOCATION_TRASH)
29611            " WHERE resource_type = 'result'"
29612            " AND resource IN (SELECT id FROM results"
29613            "                  WHERE results.task = %llu);",
29614            task);
29615 
29616       sql ("INSERT INTO results_trash"
29617            " (uuid, task, host, port, nvt, result_nvt, type, description,"
29618            "  report, nvt_version, severity, qod, qod_type, owner, date,"
29619            "  hostname, path)"
29620            " SELECT uuid, task, host, port, nvt, result_nvt, type,"
29621            "        description, report, nvt_version, severity, qod,"
29622            "         qod_type, owner, date, hostname, path"
29623            " FROM results"
29624            " WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
29625            task);
29626 
29627       tickets_trash_task (task);
29628 
29629       sql ("DELETE FROM results"
29630            " WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
29631            task);
29632 
29633       sql ("DELETE FROM report_counts"
29634            " WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
29635            task);
29636 
29637       sql ("UPDATE tasks SET hidden = 2 WHERE id = %llu;", task);
29638     }
29639 
29640   delete_permissions_cache_for_resource ("task", task);
29641 
29642   return 0;
29643 }
29644 
29645 /**
29646  * @brief Delete all trash tasks.
29647  *
29648  * The caller must do the transaction.
29649  *
29650  * @return 0 on success, -1 on error.
29651  */
29652 static int
delete_trash_tasks()29653 delete_trash_tasks ()
29654 {
29655   iterator_t tasks;
29656 
29657   init_user_task_iterator (&tasks, 1, 1);
29658   while (next (&tasks))
29659     {
29660       task_t task;
29661 
29662       task = get_iterator_resource (&tasks);
29663 
29664       if (delete_reports (task))
29665         {
29666           cleanup_iterator (&tasks);
29667           return -1;
29668         }
29669 
29670       tickets_remove_task (task);
29671 
29672       sql ("DELETE FROM results WHERE task = %llu;", task);
29673       sql ("DELETE FROM task_alerts WHERE task = %llu;", task);
29674       sql ("DELETE FROM task_files WHERE task = %llu;", task);
29675       sql ("DELETE FROM task_preferences WHERE task = %llu;", task);
29676       sql ("DELETE FROM tasks WHERE id = %llu;", task);
29677     }
29678   cleanup_iterator (&tasks);
29679 
29680   return 0;
29681 }
29682 
29683 /**
29684  * @brief Fixes the DST offset in schedule_next_time of tasks.
29685  *
29686  * @return changes  The number of tasks updated.
29687  */
29688 static int
cleanup_schedule_times()29689 cleanup_schedule_times ()
29690 {
29691   sql ("UPDATE tasks"
29692         " SET schedule_next_time"
29693         "   = (SELECT next_time_ical (icalendar, timezone)"
29694         "      FROM schedules"
29695         "      WHERE schedules.id = tasks.schedule)"
29696         " WHERE schedule_next_time != 0"
29697         "   AND schedule_next_time"
29698         "         = (SELECT next_time_ical (icalendar, timezone)"
29699         "              FROM schedules"
29700         "             WHERE schedules.id = tasks.schedule)"
29701         "   AND schedule_next_time"
29702         "         != (SELECT next_time_ical (icalendar, timezone)"
29703         "              FROM schedules"
29704         "             WHERE schedules.id = tasks.schedule);");
29705 
29706   return sql_changes();
29707 }
29708 
29709 /**
29710  * @brief Append text to the comment associated with a task.
29711  *
29712  * @param[in]  task    A pointer to the task.
29713  * @param[in]  text    The text to append.
29714  * @param[in]  length  Length of the text.
29715  */
29716 void
append_to_task_comment(task_t task,const char * text,int length)29717 append_to_task_comment (task_t task, const char* text, /* unused */ int length)
29718 {
29719   append_to_task_string (task, "comment", text);
29720 }
29721 
29722 /**
29723  * @brief Set the ports for a particular host in a scan.
29724  *
29725  * @param[in]  report   Report associated with scan.
29726  * @param[in]  host     Host.
29727  * @param[in]  current  New value for port currently being scanned.
29728  * @param[in]  max      New value for last port to be scanned.
29729  */
29730 void
set_scan_ports(report_t report,const char * host,unsigned int current,unsigned int max)29731 set_scan_ports (report_t report, const char* host, unsigned int current,
29732                 unsigned int max)
29733 {
29734   sql ("UPDATE report_hosts SET current_port = %i, max_port = %i"
29735        " WHERE host = '%s' AND report = %llu;",
29736        current, max, host, report);
29737 }
29738 
29739 /**
29740  * @brief Find a task for a specific permission, given a UUID.
29741  *
29742  * @param[in]   uuid      UUID of task.
29743  * @param[out]  task      Task return, 0 if successfully failed to find task.
29744  * @param[in]   permission  Permission.
29745  *
29746  * @return FALSE on success (including if failed to find task), TRUE on error.
29747  */
29748 gboolean
find_task_with_permission(const char * uuid,task_t * task,const char * permission)29749 find_task_with_permission (const char* uuid, task_t* task,
29750                            const char *permission)
29751 {
29752   return find_resource_with_permission ("task", uuid, task, permission, 0);
29753 }
29754 
29755 /**
29756  * @brief Find a task in the trashcan for a specific permission, given a UUID.
29757  *
29758  * @param[in]   uuid      UUID of task.
29759  * @param[out]  task      Task return, 0 if successfully failed to find task.
29760  * @param[in]   permission  Permission.
29761  *
29762  * @return FALSE on success (including if failed to find task), TRUE on error.
29763  */
29764 gboolean
find_trash_task_with_permission(const char * uuid,task_t * task,const char * permission)29765 find_trash_task_with_permission (const char* uuid, task_t* task,
29766                                  const char *permission)
29767 {
29768   return find_resource_with_permission ("task", uuid, task, permission, 1);
29769 }
29770 
29771 /**
29772  * @brief Find a task in the trashcan, given an identifier.
29773  *
29774  * @param[in]   uuid  A task identifier.
29775  * @param[out]  task  Task return, 0 if successfully failed to find task.
29776  *
29777  * @return FALSE on success (including if failed to find task), TRUE on error.
29778  */
29779 static gboolean
find_trash_task(const char * uuid,task_t * task)29780 find_trash_task (const char* uuid, task_t* task)
29781 {
29782   if (acl_user_owns_uuid ("task", uuid, 1) == 0)
29783     {
29784       *task = 0;
29785       return FALSE;
29786     }
29787   switch (sql_int64 (task,
29788                      "SELECT id FROM tasks WHERE uuid = '%s'"
29789                      " AND hidden = 2;",
29790                      uuid))
29791     {
29792       case 0:
29793         break;
29794       case 1:        /* Too few rows in result of query. */
29795         *task = 0;
29796         break;
29797       default:       /* Programming error. */
29798         assert (0);
29799       case -1:
29800         return TRUE;
29801         break;
29802     }
29803 
29804   return FALSE;
29805 }
29806 
29807 /**
29808  * @brief Find a report for a specific permission, given a UUID.
29809  *
29810  * @param[in]   uuid        UUID of report.
29811  * @param[out]  report      Report return, 0 if successfully failed to find
29812  *                          report.
29813  * @param[in]   permission  Permission.
29814  *
29815  * @return FALSE on success (including if failed to find report), TRUE on error.
29816  */
29817 gboolean
find_report_with_permission(const char * uuid,report_t * report,const char * permission)29818 find_report_with_permission (const char* uuid, report_t* report,
29819                              const char *permission)
29820 {
29821   return find_resource_with_permission ("report", uuid, report, permission, 0);
29822 }
29823 
29824 /**
29825  * @brief Find a report in the trashcan for a specific permission, given a UUID.
29826  *
29827  * @param[in]   uuid        UUID of report.
29828  * @param[out]  report      Report return, 0 if successfully failed to find
29829  *                          report.
29830  * @param[in]   permission  Permission.
29831  *
29832  * @return FALSE on success (including if failed to find report), TRUE on error.
29833  */
29834 static gboolean
find_trash_report_with_permission(const char * uuid,report_t * report,const char * permission)29835 find_trash_report_with_permission (const char *uuid, report_t *report,
29836                                    const char *permission)
29837 {
29838   return find_resource_with_permission ("report", uuid, report, permission, 1);
29839 }
29840 
29841 /**
29842  * @brief Reset all running information for a task.
29843  *
29844  * @param[in]  task  Task.
29845  */
29846 void
reset_task(task_t task)29847 reset_task (task_t task)
29848 {
29849   sql ("UPDATE tasks SET"
29850        " start_time = 0,"
29851        " end_time = 0"
29852        " WHERE id = %llu;",
29853        task);
29854 }
29855 
29856 /**
29857  * @brief Add a file to a task, or update the file on the task.
29858  *
29859  * @param[in]  task_id  Task.
29860  * @param[in]  name     Name of file.
29861  * @param[in]  content  Content for file in base64 encoding.
29862  *
29863  * @return 0 success, 1 failed to find task, -1 error.
29864  */
29865 int
manage_task_update_file(const gchar * task_id,const char * name,const void * content)29866 manage_task_update_file (const gchar *task_id, const char *name,
29867                          const void *content)
29868 {
29869   gchar* quoted_name = sql_quote (name);
29870   gchar* quoted_content = sql_quote (content);
29871   task_t task;
29872 
29873   /** @todo Probably better to save ASCII instead of base64. */
29874 
29875   task = 0;
29876   if (find_task_with_permission (task_id, &task, "modify_task"))
29877     return -1;
29878   else if (task == 0)
29879     return 1;
29880 
29881   if (sql_int ("SELECT count(*) FROM task_files"
29882                " WHERE task = %llu AND name = '%s';",
29883                task,
29884                quoted_name))
29885     {
29886       /* Update the existing file. */
29887 
29888       sql ("UPDATE task_files SET content = '%s'"
29889            " WHERE task = %llu AND name = '%s';",
29890            quoted_content,
29891            task,
29892            quoted_name);
29893     }
29894   else
29895     {
29896       /* Insert the file. */
29897 
29898       sql ("INSERT INTO task_files (task, name, content)"
29899            " VALUES (%llu, '%s', '%s');",
29900            task,
29901            quoted_name,
29902            quoted_content);
29903     }
29904 
29905   sql ("UPDATE tasks SET modification_time = m_now () WHERE id = %llu;",
29906        task);
29907 
29908   g_free (quoted_name);
29909   g_free (quoted_content);
29910 
29911   return 0;
29912 }
29913 
29914 /**
29915  * @brief Remove a file on a task.
29916  *
29917  * @param[in]  task_id  Task.
29918  * @param[in]  name     Name of file.
29919  *
29920  * @return 0 success, 1 failed to find task, -1 error.
29921  */
29922 int
manage_task_remove_file(const gchar * task_id,const char * name)29923 manage_task_remove_file (const gchar *task_id, const char *name)
29924 {
29925   task_t task;
29926 
29927   task = 0;
29928   if (find_task_with_permission (task_id, &task, "modify_task"))
29929     return -1;
29930   else if (task == 0)
29931     return 1;
29932 
29933   if (sql_int ("SELECT count(*) FROM task_files"
29934                " WHERE task = %llu AND name = '%s';",
29935                task))
29936     {
29937       gchar* quoted_name = sql_quote (name);
29938       sql ("DELETE FROM task_files WHERE task = %llu AND name = '%s';",
29939            task,
29940            quoted_name);
29941       sql ("UPDATE tasks SET modification_time = m_now () WHERE id = %llu;",
29942            task);
29943       g_free (quoted_name);
29944       return 0;
29945     }
29946   return -1;
29947 }
29948 
29949 
29950 /**
29951  * @brief Initialise a task file iterator.
29952  *
29953  * @param[in]  iterator  Iterator.
29954  * @param[in]  task      Task.
29955  * @param[in]  file      File name, NULL for all files.
29956  */
29957 void
init_task_file_iterator(iterator_t * iterator,task_t task,const char * file)29958 init_task_file_iterator (iterator_t* iterator, task_t task, const char* file)
29959 {
29960   gchar* sql;
29961   if (file)
29962     {
29963       gchar *quoted_file = sql_nquote (file, strlen (file));
29964       sql = g_strdup_printf ("SELECT name, content, length(content)"
29965                              " FROM task_files"
29966                              " WHERE task = %llu"
29967                              " AND name = '%s';",
29968                              task, quoted_file);
29969       g_free (quoted_file);
29970     }
29971   else
29972     sql = g_strdup_printf ("SELECT name, content, length(content)"
29973                            " FROM task_files"
29974                            " WHERE task = %llu;",
29975                            task);
29976   init_iterator (iterator, "%s", sql);
29977   g_free (sql);
29978 }
29979 
29980 /**
29981  * @brief Get the name of the file from a task file iterator.
29982  *
29983  * @param[in]  iterator  Iterator.
29984  *
29985  * @return Name of the file or NULL if iteration is complete.
29986  */
29987 DEF_ACCESS (task_file_iterator_name, 0);
29988 
29989 /**
29990  * @brief Get the content of the file from a task file iterator.
29991  *
29992  * @param[in]  iterator  Iterator.
29993  *
29994  * @return Content of the file or NULL if iteration is complete.
29995  */
29996 DEF_ACCESS (task_file_iterator_content, 1);
29997 
29998 /**
29999  * @brief Modify a task.
30000  *
30001  * @param[in]  task_id     Task.
30002  * @param[in]  name        Name of file.
30003  * @param[in]  comment     Comment.
30004  * @param[in]  scanner_id  Scanner.
30005  * @param[in]  target_id   Target.
30006  * @param[in]  config_id   Config.
30007  * @param[in]  observers   Observers.
30008  * @param[in]  alerts      Alerts.
30009  * @param[in]  alterable   Alterable.
30010  * @param[in]  groups      Groups.
30011  * @param[in]  schedule_id  Schedule.
30012  * @param[in]  schedule_periods  Period of schedule.
30013  * @param[in]  preferences       Preferences.
30014  * @param[in]  hosts_ordering    Host scan order.
30015  * @param[out] fail_alert_id     Alert when failed to find alert.
30016  * @param[out] fail_group_id     Group when failed to find group.
30017  *
30018  * @return 0 success, 1 failed to find task, 2 status must be new to edit
30019  *         scanner, 3 failed to find scanner, 4 failed to find config, 5 status
30020  *         must be new to edit config, 6 user name validation failed, 7 failed
30021  *         to find user, 8 failed to find alert, 9 task must be new to modify
30022  *         alterable state, 10 failed to find group, 11 failed to find schedule,
30023  *         12 failed to find target, 13 invalid auto_delete value, 14 auto
30024  *         delete count out of range, 15 config and scanner types mismatch,
30025  *         16 status must be new to edit target, 17 for container tasks only
30026  *         certain fields may be edited, -1 error.
30027  */
30028 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)30029 modify_task (const gchar *task_id, const gchar *name,
30030              const gchar *comment, const gchar *scanner_id,
30031              const gchar *target_id, const gchar *config_id,
30032              const gchar *observers, array_t *alerts,
30033              const gchar *alterable, array_t *groups,
30034              const gchar *schedule_id,
30035              const gchar *schedule_periods,
30036              array_t *preferences,
30037              const gchar *hosts_ordering,
30038              gchar **fail_alert_id,
30039              gchar **fail_group_id)
30040 {
30041   task_t task;
30042   int type_of_scanner;
30043   scanner_t scanner;
30044 
30045   /* @todo Probably better to rollback on error. */
30046 
30047   task = 0;
30048   if (find_task_with_permission (task_id, &task, "modify_task"))
30049     return -1;
30050   if (task == 0)
30051     return 1;
30052 
30053   if ((task_target (task) == 0)
30054       && (alerts->len || schedule_id))
30055     return 17;
30056 
30057   switch (modify_task_check_config_scanner (task, config_id, scanner_id))
30058     {
30059       case 0:
30060         break;
30061       case 1:
30062         return 15;
30063       case 2:
30064         return 4;
30065       case 3:
30066         return 3;
30067       default:
30068         assert (0);
30069         /* fallthrough */
30070       case -1:
30071         return -1;
30072     }
30073 
30074   if (name)
30075     set_task_name (task, name);
30076 
30077   if (comment)
30078     set_task_comment (task, comment);
30079 
30080   scanner = 0;
30081   if (scanner_id)
30082     {
30083       if (strcmp (scanner_id, "0") == 0)
30084         {
30085           /* Leave it as is. */
30086         }
30087       else if ((task_run_status (task) != TASK_STATUS_NEW)
30088                && (task_alterable (task) == 0))
30089         return 2;
30090       else if (find_scanner_with_permission (scanner_id,
30091                                              &scanner,
30092                                              "get_scanners"))
30093         return -1;
30094       else if (scanner == 0)
30095         return 3;
30096       else
30097         set_task_scanner (task, scanner);
30098     }
30099 
30100   if (scanner == 0)
30101     type_of_scanner = scanner_type (task_scanner (task));
30102   else
30103     type_of_scanner = scanner_type (scanner);
30104 
30105   if (config_id && (type_of_scanner != SCANNER_TYPE_CVE))
30106     {
30107       config_t config;
30108 
30109       config = 0;
30110       if (strcmp (config_id, "0") == 0)
30111         {
30112           /* Leave it as it is. */
30113         }
30114       else if ((task_run_status (task) != TASK_STATUS_NEW)
30115                && (task_alterable (task) == 0))
30116         return 5;
30117       else if (find_config_with_permission (config_id, &config, "get_configs"))
30118         return -1;
30119       else if (config == 0)
30120         return 4;
30121       else
30122        set_task_config (task, config);
30123     }
30124 
30125   if (observers)
30126     {
30127       switch (set_task_observers (task, observers))
30128         {
30129           case 0:
30130             break;
30131           case 1:
30132             return 6;
30133           case 2:
30134             return 7;
30135             break;
30136           case -1:
30137           default:
30138             return -1;
30139         }
30140     }
30141 
30142   if (alerts->len)
30143     {
30144       switch (set_task_alerts (task, alerts, fail_alert_id))
30145         {
30146           case 0:
30147             break;
30148           case 1:
30149             return 8;
30150           case -1:
30151           default:
30152             return -1;
30153         }
30154     }
30155 
30156   if (alterable && (task_alterable (task) != atoi (alterable)))
30157     {
30158       if (task_run_status (task) != TASK_STATUS_NEW)
30159         return 9;
30160       set_task_alterable (task, strcmp (alterable, "0"));
30161     }
30162 
30163   if (groups->len)
30164     {
30165       switch (set_task_groups (task, groups, fail_group_id))
30166         {
30167           case 0:
30168             break;
30169           case 1:
30170             return 10;
30171           case -1:
30172           default:
30173             return -1;
30174         }
30175     }
30176 
30177   if (schedule_id)
30178     {
30179       schedule_t schedule = 0;
30180       int periods;
30181 
30182       periods = schedule_periods ? atoi (schedule_periods) : 0;
30183 
30184       if (strcmp (schedule_id, "0") == 0)
30185         set_task_schedule (task, 0, periods);
30186       else if (find_schedule_with_permission (schedule_id,
30187                                               &schedule,
30188                                               "get_schedules"))
30189         return -1;
30190       else if (schedule == 0)
30191         return 11;
30192       else if (set_task_schedule (task, schedule, periods))
30193         return -1;
30194     }
30195   else if (schedule_periods && strlen (schedule_periods))
30196     set_task_schedule_periods (task_id,
30197                                atoi (schedule_periods));
30198 
30199   if (target_id)
30200     {
30201       target_t target;
30202 
30203       target = 0;
30204       if (strcmp (target_id, "0") == 0)
30205         {
30206           /* Leave it as it is. */
30207         }
30208       else if ((task_run_status (task) != TASK_STATUS_NEW)
30209                && (task_alterable (task) == 0))
30210         return 16;
30211       else if (find_target_with_permission (target_id,
30212                                             &target,
30213                                             "get_targets"))
30214         return -1;
30215       else if (target == 0)
30216         return 12;
30217       else
30218         set_task_target (task, target);
30219     }
30220 
30221   if (preferences)
30222     switch (set_task_preferences (task, preferences))
30223       {
30224         case 0:
30225           break;
30226         case 1:
30227           return 13;
30228         case 2:
30229           return 14;
30230         default:
30231           return -1;
30232       }
30233 
30234   if (hosts_ordering)
30235     set_task_hosts_ordering (task, hosts_ordering);
30236 
30237   return 0;
30238 }
30239 
30240 
30241 /* Targets. */
30242 
30243 /**
30244  * @brief Get the maximum allowed number of hosts per target.
30245  *
30246  * @return Maximum.
30247  */
30248 int
manage_max_hosts()30249 manage_max_hosts ()
30250 {
30251   return max_hosts;
30252 }
30253 
30254 /**
30255  * @brief Set the maximum allowed number of hosts per target.
30256  *
30257  * @param[in]   new_max   New max_hosts value.
30258  */
30259 static void
manage_set_max_hosts(int new_max)30260 manage_set_max_hosts (int new_max)
30261 {
30262   max_hosts = new_max;
30263 }
30264 
30265 /**
30266  * @brief Find a target for a specific permission, given a UUID.
30267  *
30268  * @param[in]   uuid        UUID of target.
30269  * @param[out]  target      Target return, 0 if successfully failed to find target.
30270  * @param[in]   permission  Permission.
30271  *
30272  * @return FALSE on success (including if failed to find target), TRUE on error.
30273  */
30274 gboolean
find_target_with_permission(const char * uuid,target_t * target,const char * permission)30275 find_target_with_permission (const char* uuid, target_t* target,
30276                              const char *permission)
30277 {
30278   return find_resource_with_permission ("target", uuid, target, permission, 0);
30279 }
30280 
30281 /**
30282  * @brief Return number of hosts described by a hosts string.
30283  *
30284  * @param[in]  given_hosts      String describing hosts.
30285  * @param[in]  exclude_hosts    String describing hosts excluded from given set.
30286  *
30287  * @return Number of hosts, or -1 on error.
30288  */
30289 int
manage_count_hosts(const char * given_hosts,const char * exclude_hosts)30290 manage_count_hosts (const char *given_hosts, const char *exclude_hosts)
30291 {
30292   return manage_count_hosts_max (given_hosts,
30293                                  exclude_hosts,
30294                                  manage_max_hosts ());
30295 }
30296 
30297 /**
30298  * @brief Trim leading and trailing space from a hosts string.
30299  *
30300  * @param[in]  string  String.  May be modified.
30301  *
30302  * @return Either string or some address within string.
30303  */
30304 static gchar *
trim_hosts(gchar * string)30305 trim_hosts (gchar *string)
30306 {
30307   gchar *host, *end;
30308 
30309   /* Trim leading and trailing space. */
30310   host = string;
30311   while ((*host == ' ') || (*host == '\t'))
30312     host++;
30313   end = host;
30314   while (*end)
30315     {
30316       if ((*end == ' ') || (*end == '\t'))
30317         {
30318           *end = '\0';
30319           break;
30320         }
30321       end++;
30322     }
30323   return host;
30324 }
30325 
30326 /**
30327  * @brief Clean a hosts string.
30328  *
30329  * @param[in]  given_hosts  String describing hosts.
30330  * @param[out] max          Max number of hosts, adjusted for duplicates.
30331  *
30332  * @return Freshly allocated new hosts string, or NULL on error.
30333  */
30334 gchar*
clean_hosts(const char * given_hosts,int * max)30335 clean_hosts (const char *given_hosts, int *max)
30336 {
30337   array_t *clean_array;
30338   GString *clean;
30339   gchar **split, **point, *hosts, *hosts_start, *host;
30340   guint index;
30341 
30342   /* Treat newlines like commas. */
30343   hosts = hosts_start = g_strdup (given_hosts);
30344   while (*hosts)
30345     {
30346       if (*hosts == '\n') *hosts = ',';
30347       hosts++;
30348     }
30349 
30350   split = g_strsplit (hosts_start, ",", 0);
30351   g_free (hosts_start);
30352   point = split;
30353 
30354   if ((point == NULL) || (*point == NULL))
30355     {
30356       g_strfreev (split);
30357       return g_strdup ("");
30358     }
30359 
30360   clean_array = make_array ();
30361   while (*point)
30362     {
30363       host = trim_hosts (*point);
30364 
30365       if (*host)
30366         {
30367           /* Prevent simple duplicates. */
30368           if (array_find_string (clean_array, host) == NULL)
30369             array_add (clean_array, host);
30370           else if (max)
30371             (*max)--;
30372         }
30373 
30374       point += 1;
30375     }
30376 
30377   clean = g_string_new ("");
30378 
30379   host = (gchar*) g_ptr_array_index (clean_array, 0);
30380   if (host)
30381     g_string_append_printf (clean, "%s", host);
30382 
30383   for (index = 1; index < clean_array->len; index++)
30384     {
30385       host = (gchar*) g_ptr_array_index (clean_array, index);
30386       if (host)
30387         g_string_append_printf (clean, ", %s", host);
30388     }
30389 
30390   return g_string_free (clean, FALSE);
30391 }
30392 
30393 /**
30394  * @brief Start a new IMMEDIATE transaction.
30395  */
30396 void
manage_transaction_start()30397 manage_transaction_start ()
30398 {
30399   if (!in_transaction)
30400     {
30401       sql_begin_immediate ();
30402       in_transaction = TRUE;
30403     }
30404   gettimeofday (&last_msg, NULL);
30405 }
30406 
30407 /**
30408  * @brief Commit the current transaction, if any.
30409  *
30410  * The algorithm is extremely naive (time elapsed since the last message
30411  * was received) but delivers good enough performances when facing
30412  * bursts of messages.
30413  *
30414  * @param[in] force_commit  Force committing the pending transaction.
30415  */
30416 void
manage_transaction_stop(gboolean force_commit)30417 manage_transaction_stop (gboolean force_commit)
30418 {
30419   struct timeval now;
30420 
30421   if (!in_transaction)
30422     return;
30423 
30424   gettimeofday (&now, NULL);
30425   if (force_commit || TIMEVAL_SUBTRACT_MS (now, last_msg) >= 500)
30426     {
30427       sql_commit ();
30428       in_transaction = FALSE;
30429     }
30430 }
30431 
30432 /**
30433  * @brief Validate a single port.
30434  *
30435  * @param[in]   port      A port.
30436  *
30437  * @return 0 success, 1 failed.
30438  */
30439 static int
validate_port(const char * port)30440 validate_port (const char *port)
30441 {
30442   const char *first;
30443 
30444   while (*port && isblank (*port)) port++;
30445   if (*port == '\0')
30446     return 1;
30447 
30448   first = port;
30449   while (*first && isdigit (*first)) first++;
30450   if (first == port)
30451     return 1;
30452 
30453   while (*first && isblank (*first)) first++;
30454   if (*first == '\0')
30455     {
30456       long int number;
30457       number = strtol (port, NULL, 10);
30458       if (number <= 0)
30459         return 1;
30460       if (number > 65535)
30461         return 1;
30462       return 0;
30463     }
30464   return 1;
30465 }
30466 
30467 /**
30468  * @brief Validate a single port, for use in override or note.
30469  *
30470  * @param[in]  port  A port.
30471  *
30472  * @return 0 success, 1 failed.
30473  */
30474 static int
validate_results_port(const char * port)30475 validate_results_port (const char *port)
30476 {
30477   long int num;
30478   char *end;
30479 
30480   if (!port)
30481     return 1;
30482 
30483   /* "cpe:abc", "general/tcp", "20/udp"
30484    *
30485    * We keep the "general/tcp" case pretty open because it is not clearly
30486    * restricted anywhere, and is already used with non-alphanumerics in
30487    * "general/Host_Details".  We exclude whitespace, ',' and ';' to prevent
30488    * users from entering lists of ports.
30489    *
30490    * Similarly, the CPE case forbids whitespace, but allows ',' and ';' as
30491    * these may occur in valid CPEs. */
30492   if (g_regex_match_simple
30493        ("^(cpe:[^\\s]+|general/[^\\s,;]+|[0-9]+/[[:alnum:]]+)$",
30494         port, 0, 0)
30495       == FALSE)
30496     return 1;
30497 
30498   if (g_str_has_prefix (port, "cpe:")
30499       || g_str_has_prefix (port, "general/"))
30500     return 0;
30501 
30502   num = strtol (port, &end, 10);
30503   if (*end != '/')
30504     return 1;
30505   if (num > 0 && num <= 65535)
30506     return 0;
30507   return 1;
30508 }
30509 
30510 /**
30511  * @brief Convert alive test name to alive test bitfield.
30512  *
30513  * @param[in]  alive_tests  Name of alive test.
30514  *
30515  * @return Alive test, or -1 on error.
30516  */
30517 static int
alive_test_from_string(const char * alive_tests)30518 alive_test_from_string (const char* alive_tests)
30519 {
30520   alive_test_t alive_test;
30521   if (alive_tests == NULL
30522       || strcmp (alive_tests, "") == 0
30523       || strcmp (alive_tests, "Scan Config Default") == 0)
30524     alive_test = 0;
30525   else if (strcmp (alive_tests, "ICMP, TCP-ACK Service & ARP Ping") == 0)
30526     alive_test = ALIVE_TEST_TCP_ACK_SERVICE | ALIVE_TEST_ICMP | ALIVE_TEST_ARP;
30527   else if (strcmp (alive_tests, "TCP-ACK Service & ARP Ping") == 0)
30528     alive_test = ALIVE_TEST_TCP_ACK_SERVICE | ALIVE_TEST_ARP;
30529   else if (strcmp (alive_tests, "ICMP & ARP Ping") == 0)
30530     alive_test = ALIVE_TEST_ICMP | ALIVE_TEST_ARP;
30531   else if (strcmp (alive_tests, "ICMP & TCP-ACK Service Ping") == 0)
30532     alive_test = ALIVE_TEST_ICMP | ALIVE_TEST_TCP_ACK_SERVICE;
30533   else if (strcmp (alive_tests, "ARP Ping") == 0)
30534     alive_test = ALIVE_TEST_ARP;
30535   else if (strcmp (alive_tests, "TCP-ACK Service Ping") == 0)
30536     alive_test = ALIVE_TEST_TCP_ACK_SERVICE;
30537   else if (strcmp (alive_tests, "TCP-SYN Service Ping") == 0)
30538     alive_test = ALIVE_TEST_TCP_SYN_SERVICE;
30539   else if (strcmp (alive_tests, "ICMP Ping") == 0)
30540     alive_test = ALIVE_TEST_ICMP;
30541   else if (strcmp (alive_tests, "Consider Alive") == 0)
30542     alive_test = ALIVE_TEST_CONSIDER_ALIVE;
30543   else
30544     return -1;
30545   return alive_test;
30546 }
30547 
30548 /**
30549  * @brief Set login data for a target.
30550  *
30551  * @param[in]  target         The target.
30552  * @param[in]  type           The credential type (e.g. "ssh" or "smb").
30553  * @param[in]  credential     The credential or 0 to remove.
30554  * @param[in]  port           The port to authenticate at with credential.
30555  *
30556  * @return  0 on success, -1 on error, 1 target not found, 99 permission denied.
30557  */
30558 static int
set_target_login_data(target_t target,const char * type,credential_t credential,int port)30559 set_target_login_data (target_t target, const char* type,
30560                        credential_t credential, int port)
30561 {
30562   gchar *quoted_type;
30563 
30564   if (current_credentials.uuid
30565       && (acl_user_may ("modify_target") == 0))
30566     return 99;
30567 
30568   if (type == NULL)
30569     return -1;
30570 
30571   if (target == 0)
30572     return 1;
30573 
30574   quoted_type = sql_quote (type);
30575 
30576   if (sql_int ("SELECT count (*) FROM targets_login_data"
30577                " WHERE target = %llu AND type = '%s';",
30578                target, quoted_type))
30579     {
30580       if (credential == 0)
30581         {
30582           sql ("DELETE FROM targets_login_data"
30583                " WHERE target = '%llu' AND type = '%s';",
30584                target, quoted_type);
30585         }
30586       else
30587         {
30588           sql ("UPDATE targets_login_data"
30589                " SET credential = %llu, port = %d"
30590                " WHERE target = %llu AND type = '%s';",
30591                credential, port, target, quoted_type);
30592         }
30593     }
30594   else if (credential)
30595     {
30596       sql ("INSERT INTO targets_login_data (target, type, credential, port)"
30597             " VALUES (%llu, '%s', %llu, %i)",
30598             target, quoted_type, credential, port);
30599     }
30600 
30601   g_free (quoted_type);
30602   return 0;
30603 }
30604 
30605 /**
30606  * @brief Get a credential from a target.
30607  *
30608  * @param[in]  target         The target.
30609  * @param[in]  type           The credential type (e.g. "ssh" or "smb").
30610  *
30611  * @return  0 on success, -1 on error, 1 credential not found, 99 permission
30612  *          denied.
30613  */
30614 credential_t
target_credential(target_t target,const char * type)30615 target_credential (target_t target, const char* type)
30616 {
30617   gchar *quoted_type;
30618   credential_t credential;
30619 
30620   if (target == 0 || type == NULL)
30621     return 0;
30622 
30623   quoted_type = sql_quote (type);
30624 
30625   if (sql_int ("SELECT NOT EXISTS"
30626                " (SELECT * FROM targets_login_data"
30627                "  WHERE target = %llu and type = '%s');",
30628                target, quoted_type))
30629     {
30630       g_free (quoted_type);
30631       return 0;
30632     }
30633 
30634   sql_int64 (&credential,
30635              "SELECT credential FROM targets_login_data"
30636              " WHERE target = %llu AND type = '%s';",
30637              target, quoted_type);
30638 
30639   g_free (quoted_type);
30640 
30641   return credential;
30642 }
30643 
30644 /**
30645  * @brief Get a login port from a target.
30646  *
30647  * @param[in]  target         The target.
30648  * @param[in]  type           The credential type (e.g. "ssh" or "smb").
30649  *
30650  * @return  0 on success, -1 on error, 1 credential not found, 99 permission
30651  *          denied.
30652  */
30653 int
target_login_port(target_t target,const char * type)30654 target_login_port (target_t target, const char* type)
30655 {
30656   gchar *quoted_type;
30657   int port;
30658 
30659   if (target == 0 || type == NULL)
30660     return 0;
30661 
30662   quoted_type = sql_quote (type);
30663 
30664   if (sql_int ("SELECT NOT EXISTS"
30665                " (SELECT * FROM targets_login_data"
30666                "  WHERE target = %llu and type = '%s');",
30667                target, quoted_type))
30668     {
30669       g_free (quoted_type);
30670       return 0;
30671     }
30672 
30673   port = sql_int ("SELECT port FROM targets_login_data"
30674                   " WHERE target = %llu AND type = '%s';",
30675                   target, quoted_type);
30676 
30677   g_free (quoted_type);
30678 
30679   return port;
30680 }
30681 
30682 /**
30683  * @brief Create a target.
30684  *
30685  * @param[in]   name            Name of target.
30686  * @param[in]   asset_hosts_filter  Asset host filter to select hosts.
30687  *                                  Overrides \p hosts and \p exclude_hosts.
30688  * @param[in]   hosts           Host list of target.
30689  * @param[in]   exclude_hosts   List of hosts to exclude from \p hosts.
30690  * @param[in]   comment         Comment on target.
30691  * @param[in]   port_list_id    Port list of target (overrides \p port_range).
30692  * @param[in]   port_range      Port range of target.
30693  * @param[in]   ssh_credential  SSH credential.
30694  * @param[in]   ssh_elevate_credential  SSH previlige escalation credential.
30695  * @param[in]   ssh_port        Port for SSH login.
30696  * @param[in]   smb_credential  SMB credential.
30697  * @param[in]   esxi_credential ESXi credential.
30698  * @param[in]   snmp_credential SNMP credential.
30699  * @param[in]   reverse_lookup_only   Scanner preference reverse_lookup_only.
30700  * @param[in]   reverse_lookup_unify  Scanner preference reverse_lookup_unify.
30701  * @param[in]   alive_tests     Alive tests.
30702  * @param[in]   allow_simultaneous_ips  Scanner preference allow_simultaneous_ips.
30703  * @param[out]  target          Created target.
30704  *
30705  * @return 0 success, 1 target exists already, 2 error in host specification,
30706  *         3 too many hosts, 4 error in port range, 5 error in SSH port,
30707  *         6 failed to find port list, 7 error in alive tests,
30708  *         8 invalid SSH credential type, 9 invalid SSH elevate credential type,
30709  *         10 invalid SMB credential type, 11 invalid ESXi credential type,
30710  *         12 invalid SNMP credential type, 13 port range or port list required,
30711  *         14 SSH elevate credential without an SSH credential,
30712  *         99 permission denied, -1 error.
30713  */
30714 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)30715 create_target (const char* name, const char* asset_hosts_filter,
30716                const char* hosts, const char* exclude_hosts,
30717                const char* comment, const char* port_list_id,
30718                const char* port_range, credential_t ssh_credential,
30719                credential_t ssh_elevate_credential,
30720                const char* ssh_port, credential_t smb_credential,
30721                credential_t esxi_credential, credential_t snmp_credential,
30722                const char *reverse_lookup_only,
30723                const char *reverse_lookup_unify, const char *alive_tests,
30724                const char *allow_simultaneous_ips,
30725                target_t* target)
30726 {
30727   gchar *quoted_name, *quoted_hosts, *quoted_exclude_hosts, *quoted_comment;
30728   gchar *port_list_comment, *quoted_ssh_port, *clean, *clean_exclude;
30729   gchar *chosen_hosts;
30730   port_list_t port_list;
30731   int ret, alive_test, max;
30732   target_t new_target;
30733 
30734   assert (current_credentials.uuid);
30735 
30736   if (port_range && validate_port_range (port_range))
30737     return 4;
30738 
30739   if (ssh_port && validate_port (ssh_port))
30740     return 5;
30741 
30742   alive_test = alive_test_from_string (alive_tests);
30743   if (alive_test <= -1)
30744     return 7;
30745 
30746   if (ssh_elevate_credential && (!ssh_credential))
30747     return 14;
30748 
30749   if (ssh_credential && (ssh_elevate_credential == ssh_credential))
30750     return 15;
30751 
30752   sql_begin_immediate ();
30753 
30754   if (acl_user_may ("create_target") == 0)
30755     {
30756       sql_rollback ();
30757       return 99;
30758     }
30759 
30760   if (resource_with_name_exists (name, "target", 0))
30761     {
30762       sql_rollback ();
30763       return 1;
30764     }
30765 
30766   if (port_list_id)
30767     {
30768       if (find_port_list_with_permission (port_list_id, &port_list,
30769                                           "get_port_lists")
30770           || (port_list == 0))
30771         {
30772           sql_rollback ();
30773           return 6;
30774         }
30775     }
30776   else if (port_range == NULL)
30777     {
30778       sql_rollback ();
30779       return 13;
30780     }
30781   else
30782     {
30783       port_list_comment = g_strdup_printf ("Autogenerated for target %s.", name);
30784       ret = create_port_list_unique (name, port_list_comment, port_range,
30785                                      &port_list);
30786       g_free (port_list_comment);
30787       if (ret)
30788         {
30789           sql_rollback ();
30790           return ret;
30791         }
30792     }
30793 
30794   if (asset_hosts_filter)
30795     {
30796       iterator_t asset_hosts;
30797       int previous;
30798       get_data_t get;
30799       GString *buffer;
30800 
30801       memset (&get, 0, sizeof (get));
30802       get.filter = g_strdup (asset_hosts_filter);
30803       init_asset_host_iterator (&asset_hosts, &get);
30804       g_free (get.filter);
30805       previous = 0;
30806       buffer = g_string_new ("");
30807       while (next (&asset_hosts))
30808         {
30809           g_string_append_printf (buffer,
30810                                   "%s%s",
30811                                   previous ? ", " : "",
30812                                   get_iterator_name (&asset_hosts));
30813           previous = 1;
30814         }
30815       cleanup_iterator (&asset_hosts);
30816       chosen_hosts = g_string_free (buffer, FALSE);
30817 
30818       g_debug ("asset chosen_hosts: %s", chosen_hosts);
30819     }
30820   else
30821     {
30822       chosen_hosts = g_strdup (hosts);
30823       g_debug ("manual chosen_hosts: %s", chosen_hosts);
30824     }
30825 
30826 
30827   clean = clean_hosts (chosen_hosts, &max);
30828   g_free (chosen_hosts);
30829   if (exclude_hosts)
30830     clean_exclude = clean_hosts (exclude_hosts, NULL);
30831   else
30832     clean_exclude = g_strdup ("");
30833 
30834   max = manage_count_hosts (clean, clean_exclude);
30835   if (max <= 0)
30836     {
30837       g_free (clean);
30838       g_free (clean_exclude);
30839       sql_rollback ();
30840       return 2;
30841     }
30842   if (max > max_hosts)
30843     {
30844       g_free (clean);
30845       g_free (clean_exclude);
30846       sql_rollback ();
30847       return 3;
30848     }
30849   quoted_hosts = sql_quote (clean);
30850   quoted_exclude_hosts = sql_quote (clean_exclude);
30851   g_free (clean);
30852   g_free (clean_exclude);
30853 
30854   if (ssh_credential)
30855     quoted_ssh_port = sql_insert (ssh_port ? ssh_port : "22");
30856   else
30857     quoted_ssh_port = g_strdup ("NULL");
30858 
30859   if (reverse_lookup_only == NULL || strcmp (reverse_lookup_only, "0") == 0)
30860     reverse_lookup_only = "0";
30861   else
30862     reverse_lookup_only = "1";
30863   if (reverse_lookup_unify == NULL || strcmp (reverse_lookup_unify, "0") == 0)
30864     reverse_lookup_unify = "0";
30865   else
30866     reverse_lookup_unify = "1";
30867   if (allow_simultaneous_ips
30868       && strcmp (allow_simultaneous_ips, "0") == 0)
30869     allow_simultaneous_ips = "0";
30870   else
30871     allow_simultaneous_ips = "1";
30872 
30873   quoted_name = sql_quote (name ?: "");
30874 
30875   if (comment)
30876     quoted_comment = sql_quote (comment);
30877   else
30878     quoted_comment = sql_quote ("");
30879 
30880   sql ("INSERT INTO targets"
30881        " (uuid, name, owner, hosts, exclude_hosts, comment, "
30882        "  port_list, reverse_lookup_only, reverse_lookup_unify, alive_test,"
30883        "  allow_simultaneous_ips,"
30884        "  creation_time, modification_time)"
30885        " VALUES (make_uuid (), '%s',"
30886        " (SELECT id FROM users WHERE users.uuid = '%s'),"
30887        " '%s', '%s', '%s', %llu, '%s', '%s', %i,"
30888        " %s,"
30889        " m_now (), m_now ());",
30890         quoted_name, current_credentials.uuid,
30891         quoted_hosts, quoted_exclude_hosts, quoted_comment, port_list,
30892         reverse_lookup_only, reverse_lookup_unify, alive_test,
30893         allow_simultaneous_ips);
30894 
30895   new_target = sql_last_insert_id ();
30896   if (target)
30897     *target = new_target;
30898 
30899   g_free (quoted_comment);
30900   g_free (quoted_name);
30901   g_free (quoted_hosts);
30902   g_free (quoted_exclude_hosts);
30903 
30904   if (ssh_credential)
30905     {
30906       gchar *type = credential_type (ssh_credential);
30907       if (strcmp (type, "usk") && strcmp (type, "up"))
30908         {
30909           sql_rollback ();
30910           g_free (quoted_ssh_port);
30911           return 8;
30912         }
30913       g_free (type);
30914 
30915       sql ("INSERT INTO targets_login_data"
30916            " (target, type, credential, port)"
30917            " VALUES (%llu, 'ssh', %llu, %s);",
30918            new_target, ssh_credential, quoted_ssh_port);
30919     }
30920   g_free (quoted_ssh_port);
30921 
30922   if (ssh_elevate_credential)
30923     {
30924       gchar *type = credential_type (ssh_elevate_credential);
30925       if (strcmp (type, "up"))
30926         {
30927           sql_rollback ();
30928           return 9;
30929         }
30930       g_free (type);
30931 
30932       sql ("INSERT INTO targets_login_data"
30933            " (target, type, credential, port)"
30934            " VALUES (%llu, 'elevate', %llu, %s);",
30935            new_target, ssh_elevate_credential, "0");
30936     }
30937 
30938   if (smb_credential)
30939     {
30940       gchar *type = credential_type (smb_credential);
30941       if (strcmp (type, "up"))
30942         {
30943           sql_rollback ();
30944           return 10;
30945         }
30946       g_free (type);
30947 
30948       sql ("INSERT INTO targets_login_data"
30949            " (target, type, credential, port)"
30950            " VALUES (%llu, 'smb', %llu, %s);",
30951            new_target, smb_credential, "0");
30952     }
30953 
30954   if (esxi_credential)
30955     {
30956       gchar *type = credential_type (esxi_credential);
30957       if (strcmp (type, "up"))
30958         {
30959           sql_rollback ();
30960           return 11;
30961         }
30962       g_free (type);
30963 
30964       sql ("INSERT INTO targets_login_data"
30965            " (target, type, credential, port)"
30966            " VALUES (%llu, 'esxi', %llu, %s);",
30967            new_target, esxi_credential, "0");
30968     }
30969 
30970   if (snmp_credential)
30971     {
30972       gchar *type = credential_type (snmp_credential);
30973       if (strcmp (type, "snmp"))
30974         {
30975           sql_rollback ();
30976           return 12;
30977         }
30978       g_free (type);
30979 
30980       sql ("INSERT INTO targets_login_data"
30981            " (target, type, credential, port)"
30982            " VALUES (%llu, 'snmp', %llu, %s);",
30983            new_target, snmp_credential, "0");
30984     }
30985 
30986   sql_commit ();
30987 
30988   return 0;
30989 }
30990 
30991 /**
30992  * @brief Create a target from an existing target.
30993  *
30994  * @param[in]  name        Name of new target.  NULL to copy from existing.
30995  * @param[in]  comment     Comment on new target.  NULL to copy from existing.
30996  * @param[in]  target_id   UUID of existing target.
30997  * @param[out] new_target  New target.
30998  *
30999  * @return 0 success, 1 target exists already, 2 failed to find existing
31000  *         target, 99 permission denied, -1 error.
31001  */
31002 int
copy_target(const char * name,const char * comment,const char * target_id,target_t * new_target)31003 copy_target (const char* name, const char* comment, const char *target_id,
31004              target_t* new_target)
31005 {
31006   int ret;
31007   target_t old_target;
31008 
31009   assert (new_target);
31010 
31011   ret = copy_resource ("target", name, comment, target_id,
31012                        "hosts, exclude_hosts, port_list, reverse_lookup_only,"
31013                        " reverse_lookup_unify, alive_test,"
31014                        " allow_simultaneous_ips",
31015                        1, new_target, &old_target);
31016   if (ret)
31017     return ret;
31018 
31019   sql ("INSERT INTO targets_login_data (target, type, credential, port)"
31020        " SELECT %llu, type, credential, port"
31021        "   FROM targets_login_data"
31022        "  WHERE target = %llu;",
31023        *new_target, old_target);
31024 
31025   return 0;
31026 }
31027 
31028 /**
31029  * @brief Delete a target.
31030  *
31031  * @param[in]  target_id  UUID of target.
31032  * @param[in]  ultimate   Whether to remove entirely, or to trashcan.
31033  *
31034  * @return 0 success, 1 fail because a task refers to the target, 2 failed
31035  *         to find target, 99 permission denied, -1 error.
31036  */
31037 int
delete_target(const char * target_id,int ultimate)31038 delete_target (const char *target_id, int ultimate)
31039 {
31040   target_t target = 0;
31041   target_t trash_target;
31042 
31043   sql_begin_immediate ();
31044 
31045   if (acl_user_may ("delete_target") == 0)
31046     {
31047       sql_rollback ();
31048       return 99;
31049     }
31050 
31051   if (find_target_with_permission (target_id, &target, "delete_target"))
31052     {
31053       sql_rollback ();
31054       return -1;
31055     }
31056 
31057   if (target == 0)
31058     {
31059       if (find_trash ("target", target_id, &target))
31060         {
31061           sql_rollback ();
31062           return -1;
31063         }
31064       if (target == 0)
31065         {
31066           sql_rollback ();
31067           return 2;
31068         }
31069       if (ultimate == 0)
31070         {
31071           /* It's already in the trashcan. */
31072           sql_commit ();
31073           return 0;
31074         }
31075 
31076       /* Check if it's in use by a task in the trashcan. */
31077       if (sql_int ("SELECT count(*) FROM tasks"
31078                    " WHERE target = %llu"
31079                    " AND target_location = " G_STRINGIFY (LOCATION_TRASH) ";",
31080                    target))
31081         {
31082           sql_rollback ();
31083           return 1;
31084         }
31085 
31086       permissions_set_orphans ("target", target, LOCATION_TRASH);
31087       tags_remove_resource ("target", target, LOCATION_TRASH);
31088 
31089       sql ("DELETE FROM targets_trash_login_data WHERE target = %llu;", target);
31090       sql ("DELETE FROM targets_trash WHERE id = %llu;", target);
31091       sql_commit ();
31092       return 0;
31093     }
31094 
31095   if (ultimate == 0)
31096     {
31097       if (sql_int ("SELECT count(*) FROM tasks"
31098                    " WHERE target = %llu"
31099                    " AND target_location = " G_STRINGIFY (LOCATION_TABLE)
31100                    " AND hidden = 0;",
31101                    target))
31102         {
31103           sql_rollback ();
31104           return 1;
31105         }
31106 
31107       sql ("INSERT INTO targets_trash"
31108            " (uuid, owner, name, hosts, exclude_hosts, comment,"
31109            "  port_list, port_list_location,"
31110            "  reverse_lookup_only, reverse_lookup_unify, alive_test,"
31111            "  allow_simultaneous_ips,"
31112            "  creation_time, modification_time)"
31113            " SELECT uuid, owner, name, hosts, exclude_hosts, comment,"
31114            "        port_list, " G_STRINGIFY (LOCATION_TABLE) ","
31115            "        reverse_lookup_only, reverse_lookup_unify, alive_test,"
31116            "        allow_simultaneous_ips,"
31117            "        creation_time, modification_time"
31118            " FROM targets WHERE id = %llu;",
31119            target);
31120 
31121       trash_target = sql_last_insert_id ();
31122 
31123       /* Copy login data */
31124       sql ("INSERT INTO targets_trash_login_data"
31125            " (target, type, credential, port, credential_location)"
31126            " SELECT %llu, type, credential, port, "
31127            G_STRINGIFY (LOCATION_TABLE)
31128            "   FROM targets_login_data WHERE target = %llu;",
31129            trash_target, target);
31130 
31131       /* Update the location of the target in any trashcan tasks. */
31132       sql ("UPDATE tasks"
31133            " SET target = %llu,"
31134            "     target_location = " G_STRINGIFY (LOCATION_TRASH)
31135            " WHERE target = %llu"
31136            " AND target_location = " G_STRINGIFY (LOCATION_TABLE) ";",
31137            sql_last_insert_id (),
31138            target);
31139 
31140       permissions_set_locations ("target", target,
31141                                  sql_last_insert_id (),
31142                                  LOCATION_TRASH);
31143       tags_set_locations ("target", target,
31144                           sql_last_insert_id (),
31145                           LOCATION_TRASH);
31146     }
31147   else if (sql_int ("SELECT count(*) FROM tasks"
31148                     " WHERE target = %llu"
31149                     " AND target_location = " G_STRINGIFY (LOCATION_TABLE),
31150                     target))
31151     {
31152       sql_rollback ();
31153       return 1;
31154     }
31155   else
31156     {
31157       permissions_set_orphans ("target", target, LOCATION_TABLE);
31158       tags_remove_resource ("target", target, LOCATION_TABLE);
31159     }
31160 
31161   sql ("DELETE FROM targets_login_data WHERE target = %llu;", target);
31162   sql ("DELETE FROM targets WHERE id = %llu;", target);
31163 
31164   sql_commit ();
31165   return 0;
31166 }
31167 
31168 /**
31169  * @brief Modify a target.
31170  *
31171  * @param[in]   target_id       UUID of target.
31172  * @param[in]   name            Name of target.
31173  * @param[in]   hosts           Host list of target.
31174  * @param[in]   exclude_hosts   List of hosts to exclude from \p hosts.
31175  * @param[in]   comment         Comment on target.
31176  * @param[in]   port_list_id    Port list of target (overrides \p port_range).
31177  * @param[in]   ssh_credential_id  SSH credential.
31178  * @param[in]   ssh_elevate_credential_id  SSH previlige escalation credential.
31179  * @param[in]   ssh_port        Port for SSH login.
31180  * @param[in]   smb_credential_id  SMB credential.
31181  * @param[in]   esxi_credential_id  ESXi credential.
31182  * @param[in]   snmp_credential_id  SNMP credential.
31183  * @param[in]   reverse_lookup_only   Scanner preference reverse_lookup_only.
31184  * @param[in]   reverse_lookup_unify  Scanner preference reverse_lookup_unify.
31185  * @param[in]   alive_tests     Alive tests.
31186  * @param[in]   allow_simultaneous_ips Scanner preference allow_simultaneous_ips.
31187  *
31188  * @return 0 success, 1 target exists already, 2 error in host specification,
31189  *         3 too many hosts, 4 error in port range, 5 error in SSH port,
31190  *         6 failed to find port list, 7 failed to find SSH cred, 8 failed to
31191  *         find SMB cred, 9 failed to find target, 10 error in alive tests,
31192  *         11 zero length name, 12 exclude hosts requires hosts
31193  *         13 hosts requires exclude hosts,
31194  *         14 hosts must be at least one character, 15 target is in use,
31195  *         16 failed to find ESXi cred, 17 failed to find SNMP cred,
31196  *         18 invalid SSH credential type, 19 invalid SMB credential type,
31197  *         20 invalid ESXi credential type, 21 invalid SNMP credential type,
31198  *         22 failed to find SSH elevate cred, 23 invalid SSH elevate
31199  *         credential type, 24 SSH elevate credential without SSH credential,
31200  *         25 SSH elevate credential equals SSH credential,
31201  *         99 permission denied, -1 error.
31202  */
31203 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)31204 modify_target (const char *target_id, const char *name, const char *hosts,
31205                const char *exclude_hosts, const char *comment,
31206                const char *port_list_id, const char *ssh_credential_id,
31207                const char *ssh_elevate_credential_id,
31208                const char *ssh_port, const char *smb_credential_id,
31209                const char *esxi_credential_id, const char* snmp_credential_id,
31210                const char *reverse_lookup_only,
31211                const char *reverse_lookup_unify, const char *alive_tests,
31212                const char *allow_simultaneous_ips)
31213 {
31214   target_t target;
31215   credential_t ssh_credential = 0;
31216   credential_t ssh_elevate_credential = 0;
31217 
31218   assert (target_id);
31219 
31220   sql_begin_immediate ();
31221 
31222   assert (current_credentials.uuid);
31223 
31224   if (acl_user_may ("modify_target") == 0)
31225     {
31226       sql_rollback ();
31227       return 99;
31228     }
31229 
31230   if (hosts && (exclude_hosts == NULL))
31231     {
31232       sql_rollback ();
31233       return 13;
31234     }
31235 
31236   target = 0;
31237   if (find_target_with_permission (target_id, &target, "modify_target"))
31238     {
31239       sql_rollback ();
31240       return -1;
31241     }
31242 
31243   if (target == 0)
31244     {
31245       sql_rollback ();
31246       return 9;
31247     }
31248 
31249   if (name)
31250     {
31251       gchar *quoted_name;
31252 
31253       if (strlen (name) == 0)
31254         {
31255           sql_rollback ();
31256           return 11;
31257         }
31258       if (resource_with_name_exists (name, "target", target))
31259         {
31260           sql_rollback ();
31261           return 1;
31262         }
31263 
31264       quoted_name = sql_quote (name);
31265       sql ("UPDATE targets SET"
31266            " name = '%s',"
31267            " modification_time = m_now ()"
31268            " WHERE id = %llu;",
31269            quoted_name,
31270            target);
31271 
31272       g_free (quoted_name);
31273     }
31274 
31275   if (comment)
31276     {
31277       gchar *quoted_comment;
31278       quoted_comment = sql_quote (comment);
31279       sql ("UPDATE targets SET"
31280            " comment = '%s',"
31281            " modification_time = m_now ()"
31282            " WHERE id = %llu;",
31283            quoted_comment,
31284            target);
31285       g_free (quoted_comment);
31286     }
31287 
31288   if (allow_simultaneous_ips)
31289     {
31290       if (target_in_use (target))
31291         {
31292           sql_rollback ();
31293           return 15;
31294         }
31295 
31296       sql ("UPDATE targets SET"
31297            " allow_simultaneous_ips = '%i',"
31298            " modification_time = m_now ()"
31299            " WHERE id = %llu;",
31300            strcmp (allow_simultaneous_ips, "0") ? 1 : 0,
31301            target);
31302     }
31303 
31304   if (alive_tests)
31305     {
31306       int alive_test;
31307 
31308       alive_test = alive_test_from_string (alive_tests);
31309       if (alive_test <= -1)
31310         {
31311           sql_rollback ();
31312           return 10;
31313         }
31314       sql ("UPDATE targets SET"
31315            " alive_test = '%i',"
31316            " modification_time = m_now ()"
31317            " WHERE id = %llu;",
31318            alive_test,
31319            target);
31320     }
31321 
31322   if (port_list_id)
31323     {
31324       port_list_t port_list;
31325 
31326       if (target_in_use (target))
31327         {
31328           sql_rollback ();
31329           return 15;
31330         }
31331 
31332       port_list = 0;
31333       if (find_port_list_with_permission (port_list_id, &port_list,
31334                                           "get_port_lists"))
31335         {
31336           sql_rollback ();
31337           return -1;
31338         }
31339 
31340       if (port_list == 0)
31341         {
31342           sql_rollback ();
31343           return 6;
31344         }
31345 
31346       sql ("UPDATE targets SET"
31347            " port_list = %llu,"
31348            " modification_time = m_now ()"
31349            " WHERE id = %llu;",
31350            port_list,
31351            target);
31352     }
31353 
31354   if (ssh_credential_id)
31355     {
31356       if (target_in_use (target))
31357         {
31358           sql_rollback ();
31359           return 15;
31360         }
31361 
31362       ssh_credential = 0;
31363       if (strcmp (ssh_credential_id, "0"))
31364         {
31365           int port_int;
31366           gchar *type;
31367 
31368           if (find_credential_with_permission (ssh_credential_id,
31369                                                &ssh_credential,
31370                                                "get_credentials"))
31371             {
31372               sql_rollback ();
31373               return -1;
31374             }
31375 
31376           if (ssh_credential == 0)
31377             {
31378               sql_rollback ();
31379               return 7;
31380             }
31381 
31382           if (ssh_port && strcmp (ssh_port, "0") && strcmp (ssh_port, ""))
31383             {
31384               if (validate_port (ssh_port))
31385                 {
31386                   sql_rollback ();
31387                   return 5;
31388                 }
31389               port_int = atoi (ssh_port);
31390             }
31391           else
31392             port_int = 22;
31393 
31394           type = credential_type (ssh_credential);
31395           if (strcmp (type, "up") && strcmp (type, "usk"))
31396             {
31397               sql_rollback ();
31398               return 18;
31399             }
31400           g_free (type);
31401 
31402           set_target_login_data (target, "ssh", ssh_credential, port_int);
31403         }
31404       else
31405         set_target_login_data (target, "ssh", 0, 0);
31406     }
31407 
31408   if (ssh_elevate_credential_id)
31409     {
31410       if (target_in_use (target))
31411         {
31412           sql_rollback ();
31413           return 15;
31414         }
31415 
31416       ssh_elevate_credential = 0;
31417       if (strcmp (ssh_elevate_credential_id, "0"))
31418         {
31419           gchar *type;
31420           if (find_credential_with_permission (ssh_elevate_credential_id,
31421                                                &ssh_elevate_credential,
31422                                                "get_credentials"))
31423             {
31424               sql_rollback ();
31425               return -1;
31426             }
31427 
31428           if (ssh_elevate_credential == 0)
31429             {
31430               sql_rollback ();
31431               return 22;
31432             }
31433 
31434           type = credential_type (ssh_elevate_credential);
31435           if (strcmp (type, "up"))
31436             {
31437               sql_rollback ();
31438               return 23;
31439             }
31440           g_free (type);
31441 
31442           set_target_login_data (target, "elevate", ssh_elevate_credential, 0);
31443         }
31444       else
31445         set_target_login_data (target, "elevate", 0, 0);
31446     }
31447 
31448   if (smb_credential_id)
31449     {
31450       credential_t smb_credential;
31451 
31452       if (target_in_use (target))
31453         {
31454           sql_rollback ();
31455           return 15;
31456         }
31457 
31458       smb_credential = 0;
31459       if (strcmp (smb_credential_id, "0"))
31460         {
31461           gchar *type;
31462           if (find_credential_with_permission (smb_credential_id,
31463                                                &smb_credential,
31464                                                "get_credentials"))
31465             {
31466               sql_rollback ();
31467               return -1;
31468             }
31469 
31470           if (smb_credential == 0)
31471             {
31472               sql_rollback ();
31473               return 7;
31474             }
31475 
31476           type = credential_type (smb_credential);
31477           if (strcmp (type, "up"))
31478             {
31479               sql_rollback ();
31480               return 19;
31481             }
31482           g_free (type);
31483 
31484           set_target_login_data (target, "smb", smb_credential, 0);
31485         }
31486       else
31487         set_target_login_data (target, "smb", 0, 0);
31488     }
31489 
31490   if (esxi_credential_id)
31491     {
31492       credential_t esxi_credential;
31493 
31494       if (target_in_use (target))
31495         {
31496           sql_rollback ();
31497           return 15;
31498         }
31499 
31500       esxi_credential = 0;
31501       if (strcmp (esxi_credential_id, "0"))
31502         {
31503           gchar *type;
31504           if (find_credential_with_permission (esxi_credential_id,
31505                                                &esxi_credential,
31506                                                "get_credentials"))
31507             {
31508               sql_rollback ();
31509               return -1;
31510             }
31511 
31512           if (esxi_credential == 0)
31513             {
31514               sql_rollback ();
31515               return 16;
31516             }
31517 
31518           type = credential_type (esxi_credential);
31519           if (strcmp (type, "up"))
31520             {
31521               sql_rollback ();
31522               return 20;
31523             }
31524           g_free (type);
31525 
31526           set_target_login_data (target, "esxi", esxi_credential, 0);
31527         }
31528       else
31529         set_target_login_data (target, "esxi", 0, 0);
31530     }
31531 
31532   if (snmp_credential_id)
31533     {
31534       credential_t snmp_credential;
31535 
31536       if (target_in_use (target))
31537         {
31538           sql_rollback ();
31539           return 15;
31540         }
31541 
31542       snmp_credential = 0;
31543       if (strcmp (snmp_credential_id, "0"))
31544         {
31545           gchar *type;
31546           if (find_credential_with_permission (snmp_credential_id,
31547                                                &snmp_credential,
31548                                                "get_credentials"))
31549             {
31550               sql_rollback ();
31551               return -1;
31552             }
31553 
31554           if (snmp_credential == 0)
31555             {
31556               sql_rollback ();
31557               return 17;
31558             }
31559 
31560           type = credential_type (snmp_credential);
31561           if (strcmp (type, "snmp"))
31562             {
31563               sql_rollback ();
31564               return 21;
31565             }
31566           g_free (type);
31567 
31568           set_target_login_data (target, "snmp", snmp_credential, 0);
31569         }
31570       else
31571         set_target_login_data (target, "snmp", 0, 0);
31572     }
31573 
31574   if (ssh_credential_id || ssh_elevate_credential_id)
31575     {
31576       if (!ssh_credential_id)
31577         ssh_credential = target_ssh_credential (target);
31578       if (!ssh_elevate_credential_id)
31579         ssh_elevate_credential = target_ssh_elevate_credential (target);
31580 
31581       if (ssh_elevate_credential && !ssh_credential)
31582         {
31583           sql_rollback ();
31584           return 24;
31585         }
31586       if (ssh_credential && (ssh_credential == ssh_elevate_credential))
31587         {
31588           sql_rollback ();
31589           return 25;
31590         }
31591     }
31592 
31593   if (exclude_hosts)
31594     {
31595       gchar *quoted_exclude_hosts, *quoted_hosts, *clean, *clean_exclude;
31596       int max;
31597 
31598       if (target_in_use (target))
31599         {
31600           sql_rollback ();
31601           return 15;
31602         }
31603 
31604       if (hosts == NULL)
31605         {
31606           sql_rollback ();
31607           return 12;
31608         }
31609 
31610       if (strlen (hosts) == 0)
31611         {
31612           sql_rollback ();
31613           return 14;
31614         }
31615 
31616       clean = clean_hosts (hosts, &max);
31617       clean_exclude = clean_hosts (exclude_hosts, NULL);
31618 
31619       max = manage_count_hosts (clean, clean_exclude);
31620       if (max <= 0)
31621         {
31622           g_free (clean);
31623           g_free (clean_exclude);
31624           sql_rollback ();
31625           return 2;
31626         }
31627 
31628       if (max > max_hosts)
31629         {
31630           g_free (clean);
31631           g_free (clean_exclude);
31632           sql_rollback ();
31633           return 3;
31634         }
31635       quoted_hosts = sql_quote (clean);
31636       quoted_exclude_hosts = sql_quote (clean_exclude);
31637       g_free (clean);
31638       g_free (clean_exclude);
31639 
31640       sql ("UPDATE targets SET"
31641            " hosts = '%s',"
31642            " exclude_hosts = '%s',"
31643            " modification_time = m_now ()"
31644            " WHERE id = %llu;",
31645            quoted_hosts,
31646            quoted_exclude_hosts,
31647            target);
31648 
31649       g_free (quoted_hosts);
31650       g_free (quoted_exclude_hosts);
31651     }
31652 
31653   if (reverse_lookup_only)
31654     {
31655       if (target_in_use (target))
31656         {
31657           sql_rollback ();
31658           return 15;
31659         }
31660 
31661       sql ("UPDATE targets SET"
31662            " reverse_lookup_only = '%i',"
31663            " modification_time = m_now ()"
31664            " WHERE id = %llu;",
31665            strcmp (reverse_lookup_only, "0") ? 1 : 0,
31666            target);
31667     }
31668 
31669   if (reverse_lookup_unify)
31670     {
31671       if (target_in_use (target))
31672         {
31673           sql_rollback ();
31674           return 15;
31675         }
31676 
31677       sql ("UPDATE targets SET"
31678            " reverse_lookup_unify = '%i',"
31679            " modification_time = m_now ()"
31680            " WHERE id = %llu;",
31681            strcmp (reverse_lookup_unify, "0") ? 1 : 0,
31682            target);
31683     }
31684 
31685   sql_commit ();
31686 
31687   return 0;
31688 }
31689 
31690 /**
31691  * @brief Filter columns for target iterator.
31692  */
31693 #define TARGET_ITERATOR_FILTER_COLUMNS                                         \
31694  { GET_ITERATOR_FILTER_COLUMNS, "hosts", "exclude_hosts", "ips", "port_list",  \
31695    "ssh_credential", "smb_credential", "esxi_credential", "snmp_credential",   \
31696    "ssh_elevate_credential", NULL }
31697 
31698 /**
31699  * @brief Target iterator columns.
31700  */
31701 #define TARGET_ITERATOR_COLUMNS                                \
31702  {                                                             \
31703    GET_ITERATOR_COLUMNS (targets),                             \
31704    { "hosts", NULL, KEYWORD_TYPE_STRING },                     \
31705    { "target_credential (id, 0, CAST ('ssh' AS text))",        \
31706      NULL,                                                     \
31707      KEYWORD_TYPE_INTEGER },                                   \
31708    { "target_login_port (id, 0, CAST ('ssh' AS text))",        \
31709      "ssh_port",                                               \
31710      KEYWORD_TYPE_INTEGER },                                   \
31711    { "target_credential (id, 0, CAST ('smb' AS text))",        \
31712      NULL,                                                     \
31713      KEYWORD_TYPE_INTEGER },                                   \
31714    { "port_list", NULL, KEYWORD_TYPE_INTEGER },                \
31715    { "0", NULL, KEYWORD_TYPE_INTEGER },                        \
31716    { "0", NULL, KEYWORD_TYPE_INTEGER },                        \
31717    {                                                           \
31718      "(SELECT uuid FROM port_lists"                            \
31719      " WHERE port_lists.id = port_list)",                      \
31720      NULL,                                                     \
31721      KEYWORD_TYPE_STRING                                       \
31722    },                                                          \
31723    {                                                           \
31724      "(SELECT name FROM port_lists"                            \
31725      " WHERE port_lists.id = port_list)",                      \
31726      "port_list",                                              \
31727      KEYWORD_TYPE_STRING                                       \
31728    },                                                          \
31729    { "0", NULL, KEYWORD_TYPE_INTEGER },                        \
31730    { "exclude_hosts", NULL, KEYWORD_TYPE_STRING },             \
31731    { "reverse_lookup_only", NULL, KEYWORD_TYPE_INTEGER },      \
31732    { "reverse_lookup_unify", NULL, KEYWORD_TYPE_INTEGER },     \
31733    { "alive_test", NULL, KEYWORD_TYPE_INTEGER },               \
31734    { "target_credential (id, 0, CAST ('esxi' AS text))",       \
31735      NULL,                                                     \
31736      KEYWORD_TYPE_INTEGER },                                   \
31737    { "0", NULL, KEYWORD_TYPE_INTEGER },                        \
31738    { "target_credential (id, 0, CAST ('snmp' AS text))",       \
31739      NULL,                                                     \
31740      KEYWORD_TYPE_INTEGER },                                   \
31741    { "0", NULL, KEYWORD_TYPE_INTEGER },                        \
31742    { "target_credential (id, 0, CAST ('elevate' AS text))",    \
31743      NULL,                                                     \
31744      KEYWORD_TYPE_INTEGER },                                   \
31745    { "0", NULL, KEYWORD_TYPE_INTEGER },                        \
31746    { "allow_simultaneous_ips",                                 \
31747      NULL,                                                     \
31748      KEYWORD_TYPE_INTEGER },                                   \
31749    {                                                           \
31750      "(SELECT name FROM credentials"                           \
31751      " WHERE credentials.id"                                   \
31752      "       = target_credential (targets.id, 0,"              \
31753      "                            CAST ('ssh' AS text)))",     \
31754      "ssh_credential",                                         \
31755      KEYWORD_TYPE_STRING                                       \
31756    },                                                          \
31757    {                                                           \
31758      "(SELECT name FROM credentials"                           \
31759      " WHERE credentials.id"                                   \
31760      "       = target_credential (targets.id, 0,"              \
31761      "                            CAST ('smb' AS text)))",     \
31762      "smb_credential",                                         \
31763      KEYWORD_TYPE_STRING                                       \
31764    },                                                          \
31765    {                                                           \
31766      "(SELECT name FROM credentials"                           \
31767      " WHERE credentials.id"                                   \
31768      "       = target_credential (targets.id, 0,"              \
31769      "                            CAST ('esxi' AS text)))",    \
31770      "esxi_credential",                                        \
31771      KEYWORD_TYPE_STRING                                       \
31772    },                                                          \
31773    {                                                           \
31774      "(SELECT name FROM credentials"                           \
31775      " WHERE credentials.id"                                   \
31776      "       = target_credential (targets.id, 0,"              \
31777      "                            CAST ('snmp' AS text)))",    \
31778      "snmp_credential",                                        \
31779      KEYWORD_TYPE_STRING                                       \
31780    },                                                          \
31781    {                                                           \
31782      "(SELECT name FROM credentials"                           \
31783      " WHERE credentials.id"                                   \
31784      "       = target_credential (targets.id, 0,"              \
31785      "                            CAST ('elevate' AS text)))", \
31786      "ssh_elevate_credential",                                 \
31787      KEYWORD_TYPE_STRING                                       \
31788    },                                                          \
31789    { "hosts", NULL, KEYWORD_TYPE_STRING },                     \
31790    { "max_hosts (hosts, exclude_hosts)",                       \
31791      "ips",                                                    \
31792      KEYWORD_TYPE_INTEGER },                                   \
31793    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                        \
31794  }
31795 
31796 /**
31797  * @brief Target iterator columns for trash case.
31798  */
31799 #define TARGET_ITERATOR_TRASH_COLUMNS                                   \
31800  {                                                                      \
31801    GET_ITERATOR_COLUMNS (targets_trash),                                \
31802    { "hosts", NULL, KEYWORD_TYPE_STRING },                              \
31803    { "target_credential (id, 1, CAST ('ssh' AS text))",                 \
31804      NULL,                                                              \
31805      KEYWORD_TYPE_INTEGER },                                            \
31806    { "target_login_port (id, 1, CAST ('ssh' AS text))",                 \
31807      "ssh_port",                                                        \
31808      KEYWORD_TYPE_INTEGER },                                            \
31809    { "target_credential (id, 1, CAST ('smb' AS text))",                 \
31810      NULL,                                                              \
31811      KEYWORD_TYPE_INTEGER },                                            \
31812    { "port_list", NULL, KEYWORD_TYPE_INTEGER },                         \
31813    { "trash_target_credential_location (id, CAST ('ssh' AS text))",     \
31814      NULL,                                                              \
31815      KEYWORD_TYPE_INTEGER },                                            \
31816    { "trash_target_credential_location (id, CAST ('smb' AS text))",     \
31817      NULL,                                                              \
31818      KEYWORD_TYPE_INTEGER },                                            \
31819    {                                                                    \
31820      "(CASE"                                                            \
31821      " WHEN port_list_location = " G_STRINGIFY (LOCATION_TRASH)         \
31822      " THEN (SELECT uuid FROM port_lists_trash"                         \
31823      "       WHERE port_lists_trash.id = port_list)"                    \
31824      " ELSE (SELECT uuid FROM port_lists"                               \
31825      "       WHERE port_lists.id = port_list)"                          \
31826      " END)",                                                           \
31827      NULL,                                                              \
31828      KEYWORD_TYPE_STRING                                                \
31829    },                                                                   \
31830    {                                                                    \
31831      "(CASE"                                                            \
31832      " WHEN port_list_location = " G_STRINGIFY (LOCATION_TRASH)         \
31833      " THEN (SELECT name FROM port_lists_trash"                         \
31834      "       WHERE port_lists_trash.id = port_list)"                    \
31835      " ELSE (SELECT name FROM port_lists"                               \
31836      "       WHERE port_lists.id = port_list)"                          \
31837      " END)",                                                           \
31838      NULL,                                                              \
31839      KEYWORD_TYPE_STRING                                                \
31840    },                                                                   \
31841    { "port_list_location = " G_STRINGIFY (LOCATION_TRASH),              \
31842      NULL,                                                              \
31843      KEYWORD_TYPE_STRING },                                             \
31844    { "exclude_hosts", NULL, KEYWORD_TYPE_STRING },                      \
31845    { "reverse_lookup_only", NULL, KEYWORD_TYPE_INTEGER },               \
31846    { "reverse_lookup_unify", NULL, KEYWORD_TYPE_INTEGER },              \
31847    { "alive_test", NULL, KEYWORD_TYPE_INTEGER },                        \
31848    { "target_credential (id, 1, CAST ('esxi' AS text))",                \
31849      NULL,                                                              \
31850      KEYWORD_TYPE_INTEGER },                                            \
31851    { "trash_target_credential_location (id, CAST ('esxi' AS text))",    \
31852      NULL,                                                              \
31853      KEYWORD_TYPE_INTEGER },                                            \
31854    { "target_credential (id, 1, CAST ('snmp' AS text))",                \
31855      NULL,                                                              \
31856      KEYWORD_TYPE_INTEGER },                                            \
31857    { "trash_target_credential_location (id, CAST ('snmp' AS text))",    \
31858      NULL,                                                              \
31859      KEYWORD_TYPE_INTEGER },                                            \
31860    { "target_credential (id, 1, CAST ('elevate' AS text))",             \
31861      NULL,                                                              \
31862      KEYWORD_TYPE_INTEGER },                                            \
31863    { "trash_target_credential_location (id, CAST ('elevate' AS text))", \
31864      NULL,                                                              \
31865      KEYWORD_TYPE_INTEGER },                                            \
31866    { "allow_simultaneous_ips",                                          \
31867      NULL,                                                              \
31868      KEYWORD_TYPE_INTEGER },                                            \
31869    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                 \
31870  }
31871 
31872 /**
31873  * @brief Count number of targets.
31874  *
31875  * @param[in]  get  GET params.
31876  *
31877  * @return Total number of targets in filtered set.
31878  */
31879 int
target_count(const get_data_t * get)31880 target_count (const get_data_t *get)
31881 {
31882   static const char *extra_columns[] = TARGET_ITERATOR_FILTER_COLUMNS;
31883   static column_t columns[] = TARGET_ITERATOR_COLUMNS;
31884   static column_t trash_columns[] = TARGET_ITERATOR_TRASH_COLUMNS;
31885   return count ("target", get, columns, trash_columns, extra_columns, 0, 0, 0,
31886                 TRUE);
31887 }
31888 
31889 /**
31890  * @brief Initialise a target iterator, given a single target.
31891  *
31892  * @param[in]  iterator   Iterator.
31893  * @param[in]  target     Single target to iterate.
31894  */
31895 void
init_target_iterator_one(iterator_t * iterator,target_t target)31896 init_target_iterator_one (iterator_t* iterator, target_t target)
31897 {
31898   get_data_t get;
31899 
31900   assert (target);
31901 
31902   memset (&get, '\0', sizeof (get));
31903   get.id = target_uuid (target);
31904   get.filter = "owner=any permission=get_targets";
31905 
31906   /* We could pass the return up to the caller, but we don't pass in
31907    * a filter id and the callers are all in situations where the
31908    * target cannot disappear, so it's safe to ignore the return. */
31909   init_target_iterator (iterator, &get);
31910 }
31911 
31912 /**
31913  * @brief Initialise a target iterator, including observed targets.
31914  *
31915  * @param[in]  iterator    Iterator.
31916  * @param[in]  get         GET data.
31917  *
31918  * @return 0 success, 1 failed to find target, 2 failed to find filter,
31919  *         -1 error.
31920  */
31921 int
init_target_iterator(iterator_t * iterator,const get_data_t * get)31922 init_target_iterator (iterator_t* iterator, const get_data_t *get)
31923 {
31924   static const char *filter_columns[] = TARGET_ITERATOR_FILTER_COLUMNS;
31925   static column_t columns[] = TARGET_ITERATOR_COLUMNS;
31926   static column_t trash_columns[] = TARGET_ITERATOR_TRASH_COLUMNS;
31927 
31928   return init_get_iterator (iterator,
31929                             "target",
31930                             get,
31931                             columns,
31932                             trash_columns,
31933                             filter_columns,
31934                             0,
31935                             NULL,
31936                             NULL,
31937                             TRUE);
31938 }
31939 
31940 /**
31941  * @brief Get the hosts of the target from a target iterator.
31942  *
31943  * @param[in]  iterator  Iterator.
31944  *
31945  * @return Hosts of the target or NULL if iteration is complete.
31946  */
31947 DEF_ACCESS (target_iterator_hosts, GET_ITERATOR_COLUMN_COUNT);
31948 
31949 /**
31950  * @brief Get the SSH LSC credential from a target iterator.
31951  *
31952  * @param[in]  iterator  Iterator.
31953  *
31954  * @return SSH LSC credential.
31955  */
31956 int
target_iterator_ssh_credential(iterator_t * iterator)31957 target_iterator_ssh_credential (iterator_t* iterator)
31958 {
31959   int ret;
31960   if (iterator->done) return -1;
31961   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
31962   return ret;
31963 }
31964 
31965 /**
31966  * @brief Get the SSH LSC port of the target from a target iterator.
31967  *
31968  * @param[in]  iterator  Iterator.
31969  *
31970  * @return SSH LSC port of the target or NULL if iteration is complete.
31971  */
31972 DEF_ACCESS (target_iterator_ssh_port, GET_ITERATOR_COLUMN_COUNT + 2);
31973 
31974 /**
31975  * @brief Get the SMB LSC credential from a target iterator.
31976  *
31977  * @param[in]  iterator  Iterator.
31978  *
31979  * @return SMB LSC credential.
31980  */
31981 int
target_iterator_smb_credential(iterator_t * iterator)31982 target_iterator_smb_credential (iterator_t* iterator)
31983 {
31984   int ret;
31985   if (iterator->done) return -1;
31986   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
31987   return ret;
31988 }
31989 
31990 /**
31991  * @brief Get the location of the SSH LSC credential from a target iterator.
31992  *
31993  * @param[in]  iterator  Iterator.
31994  *
31995  * @return 0 in table, 1 in trash
31996  */
31997 int
target_iterator_ssh_trash(iterator_t * iterator)31998 target_iterator_ssh_trash (iterator_t* iterator)
31999 {
32000   int ret;
32001   if (iterator->done) return -1;
32002   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
32003   return ret;
32004 }
32005 
32006 /**
32007  * @brief Get the location of the SMB LSC credential from a target iterator.
32008  *
32009  * @param[in]  iterator  Iterator.
32010  *
32011  * @return 0 in table, 1 in trash
32012  */
32013 int
target_iterator_smb_trash(iterator_t * iterator)32014 target_iterator_smb_trash (iterator_t* iterator)
32015 {
32016   int ret;
32017   if (iterator->done) return -1;
32018   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
32019   return ret;
32020 }
32021 
32022 /**
32023  * @brief Get the port list uuid of the target from a target iterator.
32024  *
32025  * @param[in]  iterator  Iterator.
32026  *
32027  * @return UUID of the target port list or NULL if iteration is complete.
32028  */
32029 DEF_ACCESS (target_iterator_port_list_uuid, GET_ITERATOR_COLUMN_COUNT + 7);
32030 
32031 /**
32032  * @brief Get the port list name of the target from a target iterator.
32033  *
32034  * @param[in]  iterator  Iterator.
32035  *
32036  * @return Name of the target port list or NULL if iteration is complete.
32037  */
32038 DEF_ACCESS (target_iterator_port_list_name, GET_ITERATOR_COLUMN_COUNT + 8);
32039 
32040 /**
32041  * @brief Get the location of the port list from a target iterator.
32042  *
32043  * @param[in]  iterator  Iterator.
32044  *
32045  * @return 0 in table, 1 in trash.
32046  */
32047 int
target_iterator_port_list_trash(iterator_t * iterator)32048 target_iterator_port_list_trash (iterator_t* iterator)
32049 {
32050   int ret;
32051   if (iterator->done) return -1;
32052   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
32053   return ret;
32054 }
32055 
32056 /**
32057  * @brief Get the excluded hosts of the target from a target iterator.
32058  *
32059  * @param[in]  iterator  Iterator.
32060  *
32061  * @return Excluded hosts of the target or NULL if iteration is complete.
32062  */
32063 DEF_ACCESS (target_iterator_exclude_hosts, GET_ITERATOR_COLUMN_COUNT + 10);
32064 
32065 /**
32066  * @brief Get the reverse lookup only value from a target iterator.
32067  *
32068  * @param[in]  iterator  Iterator.
32069  *
32070  * @return Reverse lookup only of the target or NULL if iteration is complete.
32071  */
32072 DEF_ACCESS (target_iterator_reverse_lookup_only,
32073             GET_ITERATOR_COLUMN_COUNT + 11);
32074 
32075 /**
32076  * @brief Get the reverse lookup unify value from a target iterator.
32077  *
32078  * @param[in]  iterator  Iterator.
32079  *
32080  * @return Reverse lookup unify of the target or NULL if iteration is complete.
32081  */
32082 DEF_ACCESS (target_iterator_reverse_lookup_unify,
32083             GET_ITERATOR_COLUMN_COUNT + 12);
32084 
32085 /**
32086  * @brief Get the alive test description from a target iterator.
32087  *
32088  * @param[in]  iterator  Iterator.
32089  *
32090  * @return Reverse lookup unify of the target or NULL if iteration is complete.
32091  */
32092 const char*
target_iterator_alive_tests(iterator_t * iterator)32093 target_iterator_alive_tests (iterator_t* iterator)
32094 {
32095   int tests;
32096   if (iterator->done) return "";
32097   tests = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 13);
32098   if ((tests & ALIVE_TEST_TCP_ACK_SERVICE)
32099       && (tests & ALIVE_TEST_ICMP)
32100       && (tests & ALIVE_TEST_ARP))
32101     return "ICMP, TCP-ACK Service & ARP Ping";
32102   if ((tests & ALIVE_TEST_TCP_ACK_SERVICE)
32103       && (tests & ALIVE_TEST_ARP))
32104     return "TCP-ACK Service & ARP Ping";
32105   if ((tests & ALIVE_TEST_ICMP)
32106       && (tests & ALIVE_TEST_ARP))
32107     return "ICMP & ARP Ping";
32108   if ((tests & ALIVE_TEST_ICMP)
32109       && (tests & ALIVE_TEST_TCP_ACK_SERVICE))
32110     return "ICMP & TCP-ACK Service Ping";
32111   if (tests & ALIVE_TEST_ARP)
32112     return "ARP Ping";
32113   if (tests & ALIVE_TEST_TCP_ACK_SERVICE)
32114     return "TCP-ACK Service Ping";
32115   if (tests & ALIVE_TEST_TCP_SYN_SERVICE)
32116     return "TCP-SYN Service Ping";
32117   if (tests & ALIVE_TEST_ICMP)
32118     return "ICMP Ping";
32119   if (tests & ALIVE_TEST_CONSIDER_ALIVE)
32120     return "Consider Alive";
32121   return "Scan Config Default";
32122 }
32123 
32124 /**
32125  * @brief Get the ESXi LSC credential from a target iterator.
32126  *
32127  * @param[in]  iterator  Iterator.
32128  *
32129  * @return ESXi LSC credential.
32130  */
32131 int
target_iterator_esxi_credential(iterator_t * iterator)32132 target_iterator_esxi_credential (iterator_t* iterator)
32133 {
32134   int ret;
32135   if (iterator->done) return -1;
32136   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 14);
32137   return ret;
32138 }
32139 
32140 /**
32141  * @brief Get the ESXi LSC credential from a target iterator.
32142  *
32143  * @param[in]  iterator  Iterator.
32144  *
32145  * @return ESXi LSC credential.
32146  */
32147 int
target_iterator_esxi_trash(iterator_t * iterator)32148 target_iterator_esxi_trash (iterator_t* iterator)
32149 {
32150   int ret;
32151   if (iterator->done) return -1;
32152   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 15);
32153   return ret;
32154 }
32155 
32156 /**
32157  * @brief Get the SNMP LSC credential from a target iterator.
32158  *
32159  * @param[in]  iterator  Iterator.
32160  *
32161  * @return ESXi LSC credential.
32162  */
32163 int
target_iterator_snmp_credential(iterator_t * iterator)32164 target_iterator_snmp_credential (iterator_t* iterator)
32165 {
32166   int ret;
32167   if (iterator->done) return -1;
32168   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 16);
32169   return ret;
32170 }
32171 
32172 /**
32173  * @brief Get the SNMP LSC credential location from a target iterator.
32174  *
32175  * @param[in]  iterator  Iterator.
32176  *
32177  * @return ESXi LSC credential.
32178  */
32179 int
target_iterator_snmp_trash(iterator_t * iterator)32180 target_iterator_snmp_trash (iterator_t* iterator)
32181 {
32182   int ret;
32183   if (iterator->done) return -1;
32184   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 17);
32185   return ret;
32186 }
32187 
32188 /**
32189  * @brief Get the ELEVATE LSC credential from a target iterator.
32190  *
32191  * @param[in]  iterator  Iterator.
32192  *
32193  * @return ELEVATE LSC credential.
32194  */
32195 int
target_iterator_ssh_elevate_credential(iterator_t * iterator)32196 target_iterator_ssh_elevate_credential (iterator_t* iterator)
32197 {
32198   int ret;
32199   if (iterator->done) return -1;
32200   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 18);
32201   return ret;
32202 }
32203 
32204 /**
32205  * @brief Get the ELEVATE LSC credential location from a target iterator.
32206  *
32207  * @param[in]  iterator  Iterator.
32208  *
32209  * @return ELEVATE LSC credential.
32210  */
32211 int
target_iterator_ssh_elevate_trash(iterator_t * iterator)32212 target_iterator_ssh_elevate_trash (iterator_t* iterator)
32213 {
32214   int ret;
32215   if (iterator->done) return -1;
32216   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 19);
32217   return ret;
32218 }
32219 
32220 /**
32221  * @brief Get the allow_simultaneous_ips value from a target iterator.
32222  *
32223  * @param[in]  iterator  Iterator.
32224  *
32225  * @return allow_simult_ips_same_host or NULL if iteration is complete.
32226  */
32227 DEF_ACCESS (target_iterator_allow_simultaneous_ips,
32228             GET_ITERATOR_COLUMN_COUNT + 20);
32229 
32230 /**
32231  * @brief Return the UUID of a tag.
32232  *
32233  * @param[in]  tag  Tag.
32234  *
32235  * @return Newly allocated UUID if available, else NULL.
32236  */
32237 char*
tag_uuid(tag_t tag)32238 tag_uuid (tag_t tag)
32239 {
32240   return sql_string ("SELECT uuid FROM tags WHERE id = %llu;",
32241                      tag);
32242 }
32243 
32244 /**
32245  * @brief Return the UUID of a target.
32246  *
32247  * @param[in]  target  Target.
32248  *
32249  * @return Newly allocated UUID if available, else NULL.
32250  */
32251 char*
target_uuid(target_t target)32252 target_uuid (target_t target)
32253 {
32254   return sql_string ("SELECT uuid FROM targets WHERE id = %llu;",
32255                      target);
32256 }
32257 
32258 /**
32259  * @brief Return the UUID of a trashcan target.
32260  *
32261  * @param[in]  target  Target.
32262  *
32263  * @return Newly allocated UUID if available, else NULL.
32264  */
32265 char*
trash_target_uuid(target_t target)32266 trash_target_uuid (target_t target)
32267 {
32268   return sql_string ("SELECT uuid FROM targets_trash WHERE id = %llu;",
32269                      target);
32270 }
32271 
32272 /**
32273  * @brief Return the name of a target.
32274  *
32275  * @param[in]  target  Target.
32276  *
32277  * @return Newly allocated name if available, else NULL.
32278  */
32279 char*
target_name(target_t target)32280 target_name (target_t target)
32281 {
32282   return sql_string ("SELECT name FROM targets WHERE id = %llu;",
32283                      target);
32284 }
32285 
32286 /**
32287  * @brief Return the name of a trashcan target.
32288  *
32289  * @param[in]  target  Target.
32290  *
32291  * @return Newly allocated name if available, else NULL.
32292  */
32293 char*
trash_target_name(target_t target)32294 trash_target_name (target_t target)
32295 {
32296   return sql_string ("SELECT name FROM targets_trash WHERE id = %llu;",
32297                      target);
32298 }
32299 
32300 /**
32301  * @brief Return the comment of a target.
32302  *
32303  * @param[in]  target  Target.
32304  *
32305  * @return Newly allocated name if available, else NULL.
32306  */
32307 static char*
target_comment(target_t target)32308 target_comment (target_t target)
32309 {
32310   return sql_string ("SELECT comment FROM targets WHERE id = %llu;",
32311                      target);
32312 }
32313 
32314 /**
32315  * @brief Return the comment of a trashcan target.
32316  *
32317  * @param[in]  target  Target.
32318  *
32319  * @return Newly allocated name if available, else NULL.
32320  */
32321 static char*
trash_target_comment(target_t target)32322 trash_target_comment (target_t target)
32323 {
32324   return sql_string ("SELECT comment FROM targets_trash WHERE id = %llu;",
32325                      target);
32326 }
32327 
32328 /**
32329  * @brief Return whether a trashcan target is readable.
32330  *
32331  * @param[in]  target  Target.
32332  *
32333  * @return 1 if readable, else 0.
32334  */
32335 int
trash_target_readable(target_t target)32336 trash_target_readable (target_t target)
32337 {
32338   char *uuid;
32339   target_t found = 0;
32340 
32341   if (target == 0)
32342     return 0;
32343   uuid = target_uuid (target);
32344   if (find_trash ("target", uuid, &found))
32345     {
32346       g_free (uuid);
32347       return 0;
32348     }
32349   g_free (uuid);
32350   return found > 0;
32351 }
32352 
32353 /**
32354  * @brief Return the hosts associated with a target.
32355  *
32356  * @param[in]  target  Target.
32357  *
32358  * @return Newly allocated comma separated list of hosts if available,
32359  *         else NULL.
32360  */
32361 char*
target_hosts(target_t target)32362 target_hosts (target_t target)
32363 {
32364   return sql_string ("SELECT hosts FROM targets WHERE id = %llu;",
32365                      target);
32366 }
32367 
32368 /**
32369  * @brief Return the excluded hosts associated with a target.
32370  *
32371  * @param[in]  target  Target.
32372  *
32373  * @return Newly allocated comma separated list of excluded hosts if available,
32374  *         else NULL.
32375  */
32376 char*
target_exclude_hosts(target_t target)32377 target_exclude_hosts (target_t target)
32378 {
32379   return sql_string ("SELECT exclude_hosts FROM targets WHERE id = %llu;",
32380                      target);
32381 }
32382 
32383 /**
32384  * @brief Return the reverse_lookup_only value of a target.
32385  *
32386  * @param[in]  target  Target.
32387  *
32388  * @return Reverse lookup only value if available, else NULL.
32389  */
32390 char*
target_reverse_lookup_only(target_t target)32391 target_reverse_lookup_only (target_t target)
32392 {
32393   return sql_string ("SELECT reverse_lookup_only FROM targets"
32394                      " WHERE id = %llu;", target);
32395 }
32396 
32397 /**
32398  * @brief Return the reverse_lookup_unify value of a target.
32399  *
32400  * @param[in]  target  Target.
32401  *
32402  * @return Reverse lookup unify value if available, else NULL.
32403  */
32404 char*
target_reverse_lookup_unify(target_t target)32405 target_reverse_lookup_unify (target_t target)
32406 {
32407   return sql_string ("SELECT reverse_lookup_unify FROM targets"
32408                      " WHERE id = %llu;", target);
32409 }
32410 
32411 /**
32412  * @brief Return the allow_simultaneous_ips value of a target.
32413  *
32414  * @param[in]  target  Target.
32415  *
32416  * @return The allow_simultaneous_ips value if available, else NULL.
32417  */
32418 char*
target_allow_simultaneous_ips(target_t target)32419 target_allow_simultaneous_ips (target_t target)
32420 {
32421   return sql_string ("SELECT allow_simultaneous_ips FROM targets"
32422                      " WHERE id = %llu;", target);
32423 }
32424 
32425 /**
32426  * @brief Return the SSH LSC port of a target.
32427  *
32428  * @param[in]  target  Target.
32429  *
32430  * @return Newly allocated port if available, else NULL.
32431  */
32432 char*
target_ssh_port(target_t target)32433 target_ssh_port (target_t target)
32434 {
32435   int port = target_login_port (target, "ssh");
32436   return port ? g_strdup_printf ("%d", port) : NULL;
32437 }
32438 
32439 /**
32440  * @brief Return the SSH credential associated with a target, if any.
32441  *
32442  * @param[in]  target  Target.
32443  *
32444  * @return SSH credential if any, else 0.
32445  */
32446 credential_t
target_ssh_credential(target_t target)32447 target_ssh_credential (target_t target)
32448 {
32449   return target_credential (target, "ssh");
32450 }
32451 
32452 /**
32453  * @brief Return the SMB credential associated with a target, if any.
32454  *
32455  * @param[in]  target  Target.
32456  *
32457  * @return SMB credential if any, else 0.
32458  */
32459 credential_t
target_smb_credential(target_t target)32460 target_smb_credential (target_t target)
32461 {
32462   return target_credential (target, "smb");
32463 }
32464 
32465 /**
32466  * @brief Return the ESXi credential associated with a target, if any.
32467  *
32468  * @param[in]  target  Target.
32469  *
32470  * @return ESXi credential if any, else 0.
32471  */
32472 credential_t
target_esxi_credential(target_t target)32473 target_esxi_credential (target_t target)
32474 {
32475   return target_credential (target, "esxi");
32476 }
32477 
32478 /**
32479  * @brief Return the ELEVATE credential associated with a target, if any.
32480  *
32481  * @param[in]  target  Target.
32482  *
32483  * @return ELEVATE credential if any, else 0.
32484  */
32485 credential_t
target_ssh_elevate_credential(target_t target)32486 target_ssh_elevate_credential (target_t target)
32487 {
32488   return target_credential (target, "elevate");
32489 }
32490 
32491 /**
32492  * @brief Return the port list associated with a target, if any.
32493  *
32494  * @param[in]  target  Target.
32495  *
32496  * @return Port list
32497  */
32498 port_list_t
target_port_list(target_t target)32499 target_port_list (target_t target)
32500 {
32501   port_list_t port_list;
32502 
32503   switch (sql_int64 (&port_list,
32504                      "SELECT port_list FROM targets"
32505                      " WHERE id = %llu;",
32506                      target))
32507     {
32508       case 0:
32509         break;
32510       case 1:        /* Too few rows in result of query. */
32511         return 0;
32512         break;
32513       default:       /* Programming error. */
32514         assert (0);
32515       case -1:
32516         /** @todo Move return to arg; return -1. */
32517         return 0;
32518         break;
32519     }
32520   return port_list;
32521 }
32522 
32523 /**
32524  * @brief Return the port range of a target, in GMP port range list format.
32525  *
32526  * For "OpenVAS Default", return the explicit port ranges instead of "default".
32527  *
32528  * @param[in]  target  Target.
32529  *
32530  * @return Newly allocated port range if available, else NULL.
32531  */
32532 char*
target_port_range(target_t target)32533 target_port_range (target_t target)
32534 {
32535   GString *range;
32536   iterator_t ranges;
32537   range = g_string_new ("");
32538   init_port_range_iterator (&ranges, target_port_list (target), 0, 1,
32539                             "type, CAST (start AS INTEGER)");
32540   if (next (&ranges))
32541     {
32542       const char *start, *end;
32543       int type;
32544 
32545       start = port_range_iterator_start (&ranges);
32546       end = port_range_iterator_end (&ranges);
32547       type = port_range_iterator_type_int (&ranges);
32548 
32549       /* Scanner can only handle: T:1-3,5-6,9,U:1-2 */
32550 
32551       if (end && strcmp (end, "0") && strcmp (end, start))
32552         g_string_append_printf (range, "%s%s-%s",
32553                                 (type == PORT_PROTOCOL_UDP ? "U:" : "T:"),
32554                                 start, end);
32555       else
32556         g_string_append_printf (range, "%s%s",
32557                                 (type == PORT_PROTOCOL_UDP ? "U:" : "T:"),
32558                                 start);
32559       while (next (&ranges))
32560         {
32561           int tcp;
32562 
32563           start = port_range_iterator_start (&ranges);
32564           end = port_range_iterator_end (&ranges);
32565           tcp = (type == PORT_PROTOCOL_TCP);
32566           type = port_range_iterator_type_int (&ranges);
32567 
32568           if (end && strcmp (end, "0") && strcmp (end, start))
32569             g_string_append_printf (range, ",%s%s-%s",
32570                                     (tcp && type == PORT_PROTOCOL_UDP ? "U:" : ""),
32571                                     start, end);
32572           else
32573             g_string_append_printf (range, ",%s%s",
32574                                     (tcp && type == PORT_PROTOCOL_UDP ? "U:" : ""),
32575                                     start);
32576         }
32577     }
32578   cleanup_iterator (&ranges);
32579   return g_string_free (range, FALSE);
32580 }
32581 
32582 /**
32583  * @brief Return a target's alive tests.
32584  *
32585  * @param[in]  target  Target.
32586  *
32587  * @return Alive test bitfield.
32588  */
32589 alive_test_t
target_alive_tests(target_t target)32590 target_alive_tests (target_t target)
32591 {
32592   return sql_int ("SELECT alive_test FROM targets WHERE id = %llu;",
32593                   target);
32594 }
32595 
32596 /**
32597  * @brief Return whether a target is in use by a task.
32598  *
32599  * @param[in]  target  Target.
32600  *
32601  * @return 1 if in use, else 0.
32602  */
32603 int
target_in_use(target_t target)32604 target_in_use (target_t target)
32605 {
32606   return !!sql_int ("SELECT count(*) FROM tasks"
32607                     " WHERE target = %llu"
32608                     " AND target_location = " G_STRINGIFY (LOCATION_TABLE)
32609                     " AND hidden = 0;",
32610                     target);
32611 }
32612 
32613 /**
32614  * @brief Return whether a trashcan target is referenced by a task.
32615  *
32616  * @param[in]  target  Target.
32617  *
32618  * @return 1 if in use, else 0.
32619  */
32620 int
trash_target_in_use(target_t target)32621 trash_target_in_use (target_t target)
32622 {
32623   return !!sql_int ("SELECT count(*) FROM tasks"
32624                     " WHERE target = %llu"
32625                     " AND target_location = " G_STRINGIFY (LOCATION_TRASH),
32626                     target);
32627 }
32628 
32629 /**
32630  * @brief Return whether a target is writable.
32631  *
32632  * @param[in]  target  Target.
32633  *
32634  * @return 1 if writable, else 0.
32635  */
32636 int
target_writable(target_t target)32637 target_writable (target_t target)
32638 {
32639   return 1;
32640 }
32641 
32642 /**
32643  * @brief Return whether a trashcan target is writable.
32644  *
32645  * @param[in]  target  Target.
32646  *
32647  * @return 1 if writable, else 0.
32648  */
32649 int
trash_target_writable(target_t target)32650 trash_target_writable (target_t target)
32651 {
32652   return trash_target_in_use (target) == 0;
32653 }
32654 
32655 /**
32656  * @brief Initialise a target task iterator.
32657  *
32658  * Iterates over all tasks that use the target.
32659  *
32660  * @param[in]  iterator   Iterator.
32661  * @param[in]  target     Target.
32662  */
32663 void
init_target_task_iterator(iterator_t * iterator,target_t target)32664 init_target_task_iterator (iterator_t* iterator, target_t target)
32665 {
32666   gchar *available, *with_clause;
32667   get_data_t get;
32668   array_t *permissions;
32669 
32670   assert (target);
32671 
32672   get.trash = 0;
32673   permissions = make_array ();
32674   array_add (permissions, g_strdup ("get_tasks"));
32675   available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
32676                                &with_clause);
32677   array_free (permissions);
32678 
32679   init_iterator (iterator,
32680                  "%s"
32681                  " SELECT name, uuid, %s FROM tasks"
32682                  " WHERE target = %llu"
32683                  " AND hidden = 0"
32684                  " ORDER BY name ASC;",
32685                  with_clause ? with_clause : "",
32686                  available,
32687                  target);
32688 
32689   g_free (with_clause);
32690   g_free (available);
32691 }
32692 
32693 /**
32694  * @brief Get the name from a target_task iterator.
32695  *
32696  * @param[in]  iterator  Iterator.
32697  *
32698  * @return The name of the host, or NULL if iteration is complete.  Freed by
32699  *         cleanup_iterator.
32700  */
32701 DEF_ACCESS (target_task_iterator_name, 0);
32702 
32703 /**
32704  * @brief Get the uuid from a target_task iterator.
32705  *
32706  * @param[in]  iterator  Iterator.
32707  *
32708  * @return The uuid of the host, or NULL if iteration is complete.  Freed by
32709  *         cleanup_iterator.
32710  */
32711 DEF_ACCESS (target_task_iterator_uuid, 1);
32712 
32713 /**
32714  * @brief Get the read permission status from a GET iterator.
32715  *
32716  * @param[in]  iterator  Iterator.
32717  *
32718  * @return 1 if may read, else 0.
32719  */
32720 int
target_task_iterator_readable(iterator_t * iterator)32721 target_task_iterator_readable (iterator_t* iterator)
32722 {
32723   if (iterator->done) return 0;
32724   return iterator_int (iterator, 2);
32725 }
32726 
32727 
32728 /* SecInfo Alerts. */
32729 
32730 /**
32731  * @brief Check for new SCAP SecInfo after an update.
32732  */
32733 static void
check_for_new_scap()32734 check_for_new_scap ()
32735 {
32736   if (manage_scap_loaded ())
32737     {
32738       if (sql_int ("SELECT EXISTS"
32739                    " (SELECT * FROM scap.cves"
32740                    "  WHERE creation_time"
32741                    "        > coalesce (CAST ((SELECT value FROM meta"
32742                    "                           WHERE name"
32743                    "                                 = 'scap_check_time')"
32744                    "                          AS INTEGER),"
32745                    "                    0));"))
32746         event (EVENT_NEW_SECINFO, "cve", 0, 0);
32747 
32748       if (sql_int ("SELECT EXISTS"
32749                    " (SELECT * FROM scap.cpes"
32750                    "  WHERE creation_time"
32751                    "        > coalesce (CAST ((SELECT value FROM meta"
32752                    "                           WHERE name"
32753                    "                                 = 'scap_check_time')"
32754                    "                          AS INTEGER),"
32755                    "                    0));"))
32756         event (EVENT_NEW_SECINFO, "cpe", 0, 0);
32757 
32758       if (sql_int ("SELECT EXISTS"
32759                    " (SELECT * FROM scap.ovaldefs"
32760                    "  WHERE creation_time"
32761                    "        > coalesce (CAST ((SELECT value FROM meta"
32762                    "                           WHERE name"
32763                    "                                 = 'scap_check_time')"
32764                    "                          AS INTEGER),"
32765                    "                    0));"))
32766         event (EVENT_NEW_SECINFO, "ovaldef", 0, 0);
32767     }
32768 }
32769 
32770 /**
32771  * @brief Check for new CERT SecInfo after an update.
32772  */
32773 static void
check_for_new_cert()32774 check_for_new_cert ()
32775 {
32776   if (manage_cert_loaded ())
32777     {
32778       if (sql_int ("SELECT EXISTS"
32779                    " (SELECT * FROM cert.cert_bund_advs"
32780                    "  WHERE creation_time"
32781                    "        > coalesce (CAST ((SELECT value FROM meta"
32782                    "                           WHERE name"
32783                    "                                 = 'cert_check_time')"
32784                    "                          AS INTEGER),"
32785                    "                    0));"))
32786         event (EVENT_NEW_SECINFO, "cert_bund_adv", 0, 0);
32787 
32788       if (sql_int ("SELECT EXISTS"
32789                    " (SELECT * FROM cert.dfn_cert_advs"
32790                    "  WHERE creation_time"
32791                    "        > coalesce (CAST ((SELECT value FROM meta"
32792                    "                           WHERE name"
32793                    "                                 = 'cert_check_time')"
32794                    "                          AS INTEGER),"
32795                    "                    0));"))
32796         event (EVENT_NEW_SECINFO, "dfn_cert_adv", 0, 0);
32797     }
32798 }
32799 
32800 /**
32801  * @brief Print an URL for a New NVTs alert.
32802  *
32803  * @param[in]  url      Format string for url.
32804  * @param[in]  oid      SecInfo ID.
32805  * @param[in]  type     SecInfo Type.
32806  *
32807  * @return Freshly allocated url.
32808  */
32809 static gchar *
alert_url_print(const gchar * url,const gchar * oid,const gchar * type)32810 alert_url_print (const gchar *url, const gchar *oid, const gchar *type)
32811 {
32812   int formatting;
32813   const gchar *point, *end;
32814   GString *new_url;
32815 
32816   assert (url);
32817 
32818   new_url = g_string_new ("");
32819   for (formatting = 0, point = url, end = (url + strlen (url));
32820        point < end;
32821        point++)
32822     if (formatting)
32823       {
32824         switch (*point)
32825           {
32826             case '$':
32827               g_string_append_c (new_url, '$');
32828               break;
32829             case 'o':
32830               {
32831                 g_string_append (new_url, oid);
32832                 break;
32833               }
32834             case 't':
32835               {
32836                 g_string_append (new_url, type);
32837                 break;
32838               }
32839             default:
32840               g_string_append_c (new_url, '$');
32841               g_string_append_c (new_url, *point);
32842               break;
32843           }
32844         formatting = 0;
32845       }
32846     else if (*point == '$')
32847       formatting = 1;
32848     else
32849       g_string_append_c (new_url, *point);
32850 
32851   return g_string_free (new_url, FALSE);
32852 }
32853 
32854 /**
32855  * @brief Create list for New NVTs event.
32856  *
32857  * @param[in]  event         Event.
32858  * @param[in]  event_data    Event type specific details.
32859  * @param[in]  alert         Alert.
32860  * @param[in]  example       Whether the message is an example only.
32861  * @param[out] count_return  NULL, or address for row count.
32862  *
32863  * @return Freshly allocated list.
32864  */
32865 static gchar *
new_nvts_list(event_t event,const void * event_data,alert_t alert,int example,int * count_return)32866 new_nvts_list (event_t event, const void* event_data, alert_t alert,
32867                int example, int *count_return)
32868 {
32869   iterator_t rows;
32870   GString *buffer;
32871   int count;
32872   char *details_url;
32873   const gchar *type;
32874   time_t feed_version_epoch;
32875 
32876   feed_version_epoch = nvts_feed_version_epoch();
32877 
32878   details_url = alert_data (alert, "method", "details_url");
32879   type = (gchar*) event_data;
32880 
32881   if (details_url && strlen (details_url))
32882     buffer = g_string_new (NEW_NVTS_HEADER);
32883   else
32884     buffer = g_string_new (NEW_NVTS_HEADER_OID);
32885 
32886   count = 0;
32887   // TODO This should use an iterator provided by manage_sql_nvts.c.
32888   if (example)
32889     init_iterator (&rows,
32890                    "SELECT oid, name, solution_type, cvss_base, qod FROM nvts"
32891                    " LIMIT 4;");
32892   else if (event == EVENT_NEW_SECINFO)
32893     init_iterator (&rows,
32894                    "SELECT oid, name, solution_type, cvss_base, qod FROM nvts"
32895                    " WHERE creation_time > %ld"
32896                    " ORDER BY creation_time DESC;",
32897                    feed_version_epoch);
32898   else
32899     init_iterator (&rows,
32900                    "SELECT oid, name, solution_type, cvss_base, qod FROM nvts"
32901                    " WHERE modification_time > %ld"
32902                    "   AND creation_time <= %ld"
32903                    " ORDER BY modification_time DESC;",
32904                    feed_version_epoch,
32905                    feed_version_epoch);
32906 
32907   while (next (&rows))
32908     {
32909       gchar *url;
32910       const char *name;
32911 
32912       name = iterator_string (&rows, 1);
32913       if (details_url && strlen (details_url))
32914         url = alert_url_print (details_url, iterator_string (&rows, 0), type);
32915       else
32916         url = NULL;
32917       g_string_append_printf (buffer,
32918                               "%-57.57s%-3s  %13s  %8s %3s%%%s%s%s",
32919                               name,
32920                               strlen (name) > 60
32921                                ? "..."
32922                                : (strlen (name) > 57 ? name + 57 : "   "),
32923                               iterator_string (&rows, 2),
32924                               iterator_string (&rows, 3),
32925                               iterator_string (&rows, 4),
32926                               url ? "\n  " : "  ",
32927                               url ? url : iterator_string (&rows, 0),
32928                               url ? "\n\n" : "\n");
32929       g_free (url);
32930       count++;
32931     }
32932   cleanup_iterator (&rows);
32933 
32934   if (count_return)
32935     *count_return = count;
32936 
32937   return g_string_free (buffer, FALSE);
32938 }
32939 
32940 /**
32941  * @brief Create list for New CVEs event.
32942  *
32943  * @param[in]  event         Event.
32944  * @param[in]  event_data    Event type specific details.
32945  * @param[in]  alert         Alert.
32946  * @param[in]  example       Whether the message is an example only.
32947  * @param[out] count_return  NULL, or address for row count.
32948  *
32949  * @return Freshly allocated message.
32950  */
32951 static gchar *
new_cves_list(event_t event,const void * event_data,alert_t alert,int example,int * count_return)32952 new_cves_list (event_t event, const void* event_data, alert_t alert,
32953                int example, int *count_return)
32954 {
32955   iterator_t rows;
32956   GString *buffer;
32957   int count;
32958   char *details_url;
32959   const gchar *type;
32960 
32961   details_url = alert_data (alert, "method", "details_url");
32962   type = (gchar*) event_data;
32963 
32964   buffer = g_string_new (NEW_CVES_HEADER);
32965 
32966   count = 0;
32967   if (example)
32968     init_iterator (&rows,
32969                    "SELECT uuid, name, cvss, description FROM cves"
32970                    " LIMIT 4;");
32971   else if (event == EVENT_NEW_SECINFO)
32972     init_iterator (&rows,
32973                    "SELECT uuid, name, cvss, description FROM cves"
32974                    " WHERE creation_time"
32975                    "       > coalesce (CAST ((SELECT value FROM meta"
32976                    "                          WHERE name"
32977                    "                                = 'scap_check_time')"
32978                    "                         AS INTEGER),"
32979                    "                   0)"
32980                    " ORDER BY creation_time DESC;");
32981   else
32982     init_iterator (&rows,
32983                    "SELECT uuid, name, cvss, description FROM cves"
32984                    " WHERE modification_time"
32985                    "       > coalesce (CAST ((SELECT value FROM meta"
32986                    "                          WHERE name"
32987                    "                                = 'scap_check_time')"
32988                    "                         AS INTEGER),"
32989                    "                   0)"
32990                    " AND creation_time"
32991                    "     <= coalesce (CAST ((SELECT value FROM meta"
32992                    "                         WHERE name"
32993                    "                               = 'scap_check_time')"
32994                    "                        AS INTEGER),"
32995                    "                  0)"
32996                    " ORDER BY modification_time DESC;");
32997 
32998   while (next (&rows))
32999     {
33000       gchar *url;
33001       const char *name, *desc;
33002 
33003       name = iterator_string (&rows, 1);
33004       if (details_url && strlen (details_url))
33005         url = alert_url_print (details_url, iterator_string (&rows, 0), type);
33006       else
33007         url = NULL;
33008       desc = iterator_string (&rows, 3);
33009       g_string_append_printf (buffer,
33010                               "%-15.15s  %8s  %50.50s%s%s%s%s",
33011                               name,
33012                               iterator_string (&rows, 2),
33013                               desc,
33014                               strlen (desc) > 53
33015                                ? "..."
33016                                : (strlen (desc) > 50 ? desc + 50 : "   "),
33017                               url ? "\n  " : "",
33018                               url ? url : "",
33019                               url ? "\n\n" : "\n");
33020       g_free (url);
33021       count++;
33022     }
33023   cleanup_iterator (&rows);
33024 
33025   if (count_return)
33026     *count_return = count;
33027 
33028   return g_string_free (buffer, FALSE);
33029 }
33030 
33031 /**
33032  * @brief Create list for New CPEs event.
33033  *
33034  * @param[in]  event         Event.
33035  * @param[in]  event_data    Event type specific details.
33036  * @param[in]  alert         Alert.
33037  * @param[in]  example       Whether the message is an example only.
33038  * @param[out] count_return  NULL, or address for row count.
33039  *
33040  * @return Freshly allocated list.
33041  */
33042 static gchar *
new_cpes_list(event_t event,const void * event_data,alert_t alert,int example,int * count_return)33043 new_cpes_list (event_t event, const void* event_data, alert_t alert,
33044                int example, int *count_return)
33045 {
33046   iterator_t rows;
33047   GString *buffer;
33048   int count;
33049   char *details_url;
33050   const gchar *type;
33051 
33052   details_url = alert_data (alert, "method", "details_url");
33053   type = (gchar*) event_data;
33054 
33055   buffer = g_string_new (NEW_CPES_HEADER);
33056 
33057   count = 0;
33058   if (example)
33059     init_iterator (&rows,
33060                    "SELECT uuid, name, title FROM cpes"
33061                    " LIMIT 4;");
33062   else if (event == EVENT_NEW_SECINFO)
33063     init_iterator (&rows,
33064                    "SELECT uuid, name, title FROM cpes"
33065                    " WHERE creation_time"
33066                    "       > coalesce (CAST ((SELECT value FROM meta"
33067                    "                          WHERE name"
33068                    "                                = 'scap_check_time')"
33069                    "                         AS INTEGER),"
33070                    "                   0)"
33071                    " ORDER BY creation_time DESC;");
33072   else
33073     init_iterator (&rows,
33074                    "SELECT uuid, name, title FROM cpes"
33075                    " WHERE modification_time"
33076                    "       > coalesce (CAST ((SELECT value FROM meta"
33077                    "                          WHERE name"
33078                    "                                = 'scap_check_time')"
33079                    "                         AS INTEGER),"
33080                    "                   0)"
33081                    " AND creation_time"
33082                    "     <= coalesce (CAST ((SELECT value FROM meta"
33083                    "                         WHERE name"
33084                    "                               = 'scap_check_time')"
33085                    "                        AS INTEGER),"
33086                    "                  0)"
33087                    " ORDER BY modification_time DESC;");
33088 
33089   while (next (&rows))
33090     {
33091       gchar *url;
33092       const char *name, *title;
33093 
33094       name = iterator_string (&rows, 1);
33095       title = iterator_string (&rows, 2);
33096       if (details_url && strlen (details_url))
33097         url = alert_url_print (details_url, iterator_string (&rows, 0), type);
33098       else
33099         url = NULL;
33100       g_string_append_printf (buffer,
33101                               "%-57.57s%-3s  %-s%s%s%s",
33102                               name,
33103                               strlen (name) > 60
33104                                ? "..."
33105                                : (strlen (name) > 57 ? name + 57 : "   "),
33106                               title,
33107                               url ? "\n  " : "",
33108                               url ? url : "",
33109                               url ? "\n\n" : "\n");
33110       g_free (url);
33111       count++;
33112     }
33113   cleanup_iterator (&rows);
33114 
33115   if (count_return)
33116     *count_return = count;
33117 
33118   return g_string_free (buffer, FALSE);
33119 }
33120 
33121 /**
33122  * @brief Create list for "New CERT-Bund Advisories" event message.
33123  *
33124  * @param[in]  event         Event.
33125  * @param[in]  event_data    Event data.
33126  * @param[in]  alert         Alert.
33127  * @param[in]  example       Whether the message is an example only.
33128  * @param[out] count_return  NULL, or address for row count.
33129  *
33130  * @return Freshly allocated string.
33131  */
33132 static gchar *
new_cert_bunds_list(event_t event,const void * event_data,alert_t alert,int example,int * count_return)33133 new_cert_bunds_list (event_t event, const void* event_data, alert_t alert,
33134                      int example, int *count_return)
33135 {
33136   iterator_t rows;
33137   GString *buffer;
33138   int count;
33139   char *details_url;
33140   const gchar *type;
33141 
33142   details_url = alert_data (alert, "method", "details_url");
33143   type = (gchar*) event_data;
33144 
33145   buffer = g_string_new (NEW_CERT_BUNDS_HEADER);
33146 
33147   count = 0;
33148   if (example)
33149     init_iterator (&rows,
33150                    "SELECT uuid, name, title FROM cert_bund_advs"
33151                    " LIMIT 4;");
33152   else if (event == EVENT_NEW_SECINFO)
33153     init_iterator (&rows,
33154                    "SELECT uuid, name, title FROM cert_bund_advs"
33155                    " WHERE creation_time"
33156                    "       > coalesce (CAST ((SELECT value FROM meta"
33157                    "                          WHERE name"
33158                    "                                = 'cert_check_time')"
33159                    "                         AS INTEGER),"
33160                    "                   0)"
33161                    " ORDER BY creation_time DESC;");
33162   else
33163     init_iterator (&rows,
33164                    "SELECT uuid, name, title FROM cert_bund_advs"
33165                    " WHERE modification_time"
33166                    "       > coalesce (CAST ((SELECT value FROM meta"
33167                    "                          WHERE name"
33168                    "                                = 'cert_check_time')"
33169                    "                         AS INTEGER),"
33170                    "                   0)"
33171                    " AND creation_time"
33172                    "     <= coalesce (CAST ((SELECT value FROM meta"
33173                    "                         WHERE name"
33174                    "                               = 'cert_check_time')"
33175                    "                        AS INTEGER),"
33176                    "                  0)"
33177                    " ORDER BY modification_time DESC;");
33178 
33179   while (next (&rows))
33180     {
33181       gchar *url;
33182       const char *name, *title;
33183 
33184       name = iterator_string (&rows, 1);
33185       title = iterator_string (&rows, 2);
33186       if (details_url && strlen (details_url))
33187         url = alert_url_print (details_url, iterator_string (&rows, 0), type);
33188       else
33189         url = NULL;
33190       g_string_append_printf (buffer,
33191                               "%-11s  %-s%s%s%s",
33192                               name,
33193                               title,
33194                               url ? "\n  " : "",
33195                               url ? url : "",
33196                               url ? "\n\n" : "\n");
33197       g_free (url);
33198       count++;
33199     }
33200   cleanup_iterator (&rows);
33201 
33202   if (count_return)
33203     *count_return = count;
33204 
33205   return g_string_free (buffer, FALSE);
33206 }
33207 
33208 /**
33209  * @brief Create list for "New DFN-CERT Advisories" event message.
33210  *
33211  * @param[in]  event         Event.
33212  * @param[in]  event_data    Event type specific details.
33213  * @param[in]  alert         Alert.
33214  * @param[in]  example       Whether the message is an example only.
33215  * @param[out] count_return  NULL, or address for row count.
33216  *
33217  * @return Freshly allocated string.
33218  */
33219 static gchar *
new_dfn_certs_list(event_t event,const void * event_data,alert_t alert,int example,int * count_return)33220 new_dfn_certs_list (event_t event, const void* event_data, alert_t alert,
33221                     int example, int *count_return)
33222 {
33223   iterator_t rows;
33224   GString *buffer;
33225   int count;
33226   char *details_url;
33227   const gchar *type;
33228 
33229   details_url = alert_data (alert, "method", "details_url");
33230   type = (gchar*) event_data;
33231 
33232   buffer = g_string_new (NEW_DFN_CERTS_HEADER);
33233 
33234   count = 0;
33235   if (example)
33236     init_iterator (&rows,
33237                    "SELECT uuid, name, title FROM dfn_cert_advs"
33238                    " LIMIT 4;");
33239   else if (event == EVENT_NEW_SECINFO)
33240     init_iterator (&rows,
33241                    "SELECT uuid, name, title FROM dfn_cert_advs"
33242                    " WHERE creation_time"
33243                    "       > coalesce (CAST ((SELECT value FROM meta"
33244                    "                          WHERE name"
33245                    "                                = 'cert_check_time')"
33246                    "                         AS INTEGER),"
33247                    "                   0)"
33248                    " ORDER BY creation_time DESC;");
33249   else
33250     init_iterator (&rows,
33251                    "SELECT uuid, name, title FROM dfn_cert_advs"
33252                    " WHERE modification_time"
33253                    "       > coalesce (CAST ((SELECT value FROM meta"
33254                    "                          WHERE name"
33255                    "                                = 'cert_check_time')"
33256                    "                         AS INTEGER),"
33257                    "                   0)"
33258                    " AND creation_time"
33259                    "     <= coalesce (CAST ((SELECT value FROM meta"
33260                    "                         WHERE name"
33261                    "                               = 'cert_check_time')"
33262                    "                        AS INTEGER),"
33263                    "                  0)"
33264                    " ORDER BY modification_time DESC;");
33265 
33266   while (next (&rows))
33267     {
33268       gchar *url;
33269       const char *name, *title;
33270 
33271       name = iterator_string (&rows, 1);
33272       title = iterator_string (&rows, 2);
33273       if (details_url && strlen (details_url))
33274         url = alert_url_print (details_url, iterator_string (&rows, 0), type);
33275       else
33276         url = NULL;
33277       g_string_append_printf (buffer,
33278                               "%-18s  %-s%s%s%s",
33279                               name,
33280                               title,
33281                               url ? "\n  " : "",
33282                               url ? url : "",
33283                               url ? "\n\n" : "\n");
33284       g_free (url);
33285       count++;
33286     }
33287   cleanup_iterator (&rows);
33288 
33289   if (count_return)
33290     *count_return = count;
33291 
33292   return g_string_free (buffer, FALSE);
33293 }
33294 
33295 /**
33296  * @brief Create list for "New OVAL Definitions" event.
33297  *
33298  * @param[in]  event         Event.
33299  * @param[in]  event_data    Event type specific details.
33300  * @param[in]  alert         Alert.
33301  * @param[in]  example       Whether the message is an example only.
33302  * @param[out] count_return  NULL, or address for row count.
33303  *
33304  * @return Freshly allocated list.
33305  */
33306 static gchar *
new_oval_defs_list(event_t event,const void * event_data,alert_t alert,int example,int * count_return)33307 new_oval_defs_list (event_t event, const void* event_data, alert_t alert,
33308                     int example, int *count_return)
33309 {
33310   iterator_t rows;
33311   GString *buffer;
33312   int count;
33313   char *details_url;
33314   const gchar *type;
33315 
33316   details_url = alert_data (alert, "method", "details_url");
33317   type = (gchar*) event_data;
33318 
33319   buffer = g_string_new (NEW_OVAL_DEFS_HEADER);
33320 
33321   count = 0;
33322   if (example)
33323     init_iterator (&rows,
33324                    "SELECT uuid, name, title FROM ovaldefs"
33325                    " LIMIT 4;");
33326   else if (event == EVENT_NEW_SECINFO)
33327     init_iterator (&rows,
33328                    "SELECT uuid, name, title FROM ovaldefs"
33329                    " WHERE creation_time"
33330                    "       > coalesce (CAST ((SELECT value FROM meta"
33331                    "                          WHERE name"
33332                    "                                = 'scap_check_time')"
33333                    "                         AS INTEGER),"
33334                    "                   0)"
33335                    " ORDER BY creation_time DESC;");
33336   else
33337     init_iterator (&rows,
33338                    "SELECT uuid, name, title FROM ovaldefs"
33339                    " WHERE modification_time"
33340                    "       > coalesce (CAST ((SELECT value FROM meta"
33341                    "                          WHERE name"
33342                    "                                = 'scap_check_time')"
33343                    "                         AS INTEGER),"
33344                    "                   0)"
33345                    " AND creation_time"
33346                    "     <= coalesce (CAST ((SELECT value FROM meta"
33347                    "                         WHERE name"
33348                    "                               = 'scap_check_time')"
33349                    "                        AS INTEGER),"
33350                    "                  0)"
33351                    " ORDER BY modification_time DESC;");
33352 
33353   while (next (&rows))
33354     {
33355       gchar *url;
33356       const char *name, *title;
33357 
33358       name = iterator_string (&rows, 1);
33359       title = iterator_string (&rows, 2);
33360       if (details_url && strlen (details_url))
33361         url = alert_url_print (details_url, iterator_string (&rows, 0), type);
33362       else
33363         url = NULL;
33364       g_string_append_printf (buffer,
33365                               "%-30s  %-s%s%s%s",
33366                               name,
33367                               title,
33368                               url ? "\n  " : "",
33369                               url ? url : "",
33370                               url ? "\n\n" : "\n");
33371       g_free (url);
33372       count++;
33373     }
33374   cleanup_iterator (&rows);
33375 
33376   if (count_return)
33377     *count_return = count;
33378 
33379   return g_string_free (buffer, FALSE);
33380 }
33381 
33382 /**
33383  * @brief Create message for New NVTs event.
33384  *
33385  * @param[in]  event       Event.
33386  * @param[in]  event_data  Event data.
33387  * @param[in]  alert       Alert.
33388  * @param[out] count_return  NULL, or address for row count.
33389  *
33390  * @return Freshly allocated list.
33391  */
33392 static gchar *
new_secinfo_list(event_t event,const void * event_data,alert_t alert,int * count_return)33393 new_secinfo_list (event_t event, const void* event_data, alert_t alert,
33394                   int *count_return)
33395 {
33396   g_debug ("%s: event_data: %s", __func__, (gchar*) event_data);
33397 
33398   if (strcasecmp (event_data, "nvt_example") == 0)
33399     return new_nvts_list (event, "nvt", alert, 1, count_return);
33400   if (strcasecmp (event_data, "nvt") == 0)
33401     return new_nvts_list (event, "nvt", alert, 0, count_return);
33402 
33403   if (strcasecmp (event_data, "cve_example") == 0)
33404     return new_cves_list (event, "cve", alert, 1, count_return);
33405   if (strcasecmp (event_data, "cve") == 0)
33406     return new_cves_list (event, "cve", alert, 0, count_return);
33407 
33408   if (strcasecmp (event_data, "cpe_example") == 0)
33409     return new_cpes_list (event, "cpe", alert, 1, count_return);
33410   if (strcasecmp (event_data, "cpe") == 0)
33411     return new_cpes_list (event, "cpe", alert, 0, count_return);
33412 
33413   if (strcasecmp (event_data, "cert_bund_adv_example") == 0)
33414     return new_cert_bunds_list (event, "cert_bund_adv", alert, 1, count_return);
33415   if (strcasecmp (event_data, "cert_bund_adv") == 0)
33416     return new_cert_bunds_list (event, "cert_bund_adv", alert, 0, count_return);
33417 
33418   if (strcasecmp (event_data, "dfn_cert_adv_example") == 0)
33419     return new_dfn_certs_list (event, "dfn_cert_adv", alert, 1, count_return);
33420   if (strcasecmp (event_data, "dfn_cert_adv") == 0)
33421     return new_dfn_certs_list (event, "dfn_cert_adv", alert, 0, count_return);
33422 
33423   if (strcasecmp (event_data, "ovaldef_example") == 0)
33424     return new_oval_defs_list (event, "ovaldef", alert, 1, count_return);
33425   if (strcasecmp (event_data, "ovaldef") == 0)
33426     return new_oval_defs_list (event, "ovaldef", alert, 0, count_return);
33427 
33428   if (count_return)
33429     {
33430       g_warning ("%s: Type error: %s", __func__, (char *) event_data);
33431       *count_return = 0;
33432     }
33433 
33434   return g_strdup ("ERROR generating list");
33435 }
33436 
33437 /**
33438  * @brief Create message for New NVTs event.
33439  *
33440  * @param[in]  event       Event.
33441  * @param[in]  event_data  Event type specific details.
33442  * @param[in]  alert       Alert.
33443  *
33444  * @return Freshly allocated message.
33445  */
33446 static gchar *
new_secinfo_message(event_t event,const void * event_data,alert_t alert)33447 new_secinfo_message (event_t event, const void* event_data, alert_t alert)
33448 {
33449   gchar *type, *list, *message, *name, *point;
33450   int count, example;
33451 
33452   list = new_secinfo_list (event, event_data, alert, &count);
33453 
33454   assert (count > 0);
33455 
33456   type = g_strdup (event_data);
33457   if (type && (point = strstr (type, "_example")))
33458     {
33459       example = 1;
33460       point[0] = '\0';
33461     }
33462   else
33463     example = 0;
33464 
33465   name = alert_name (alert);
33466   message = g_strdup_printf ("%s%i%s%s%s%s, according to the\nalert \"%s\":\n\n%s",
33467                              example
33468                               ? "Warning: This is an example alert only.\n\n"
33469                               : "",
33470                              count,
33471                              event == EVENT_NEW_SECINFO
33472                               ? " new "
33473                               : " ",
33474                              count == 1
33475                               ? type_name (type)
33476                               : type_name_plural (type),
33477                              event == EVENT_NEW_SECINFO
33478                               ? ""
33479                               : (count == 1 ? " was" : " were"),
33480                              event == EVENT_NEW_SECINFO
33481                               ? " appeared in the feed"
33482                               : " updated in the feed",
33483                              name,
33484                              list);
33485   free (name);
33486   g_free (list);
33487   return message;
33488 }
33489 
33490 /**
33491  * @brief Check for updated SCAP SecInfo after an update.
33492  */
33493 static void
check_for_updated_scap()33494 check_for_updated_scap ()
33495 {
33496   if (manage_scap_loaded ())
33497     {
33498       if (sql_int ("SELECT EXISTS"
33499                    " (SELECT * FROM scap.cves"
33500                    "  WHERE modification_time"
33501                    "        > coalesce (CAST ((SELECT value FROM meta"
33502                    "                           WHERE name"
33503                    "                                 = 'scap_check_time')"
33504                    "                          AS INTEGER),"
33505                    "                    0)"
33506                    "  AND creation_time"
33507                    "      <= coalesce (CAST ((SELECT value FROM meta"
33508                    "                          WHERE name"
33509                    "                                = 'scap_check_time')"
33510                    "                         AS INTEGER),"
33511                    "                   0));"))
33512         event (EVENT_UPDATED_SECINFO, "cve", 0, 0);
33513 
33514       if (sql_int ("SELECT EXISTS"
33515                    " (SELECT * FROM scap.cpes"
33516                    "  WHERE modification_time"
33517                    "        > coalesce (CAST ((SELECT value FROM meta"
33518                    "                           WHERE name"
33519                    "                                 = 'scap_check_time')"
33520                    "                          AS INTEGER),"
33521                    "                    0)"
33522                    "  AND creation_time"
33523                    "      <= coalesce (CAST ((SELECT value FROM meta"
33524                    "                          WHERE name"
33525                    "                                = 'scap_check_time')"
33526                    "                         AS INTEGER),"
33527                    "                   0));"))
33528         event (EVENT_UPDATED_SECINFO, "cpe", 0, 0);
33529 
33530       if (sql_int ("SELECT EXISTS"
33531                    " (SELECT * FROM scap.ovaldefs"
33532                    "  WHERE modification_time"
33533                    "        > coalesce (CAST ((SELECT value FROM meta"
33534                    "                           WHERE name"
33535                    "                                 = 'scap_check_time')"
33536                    "                          AS INTEGER),"
33537                    "                    0)"
33538                    "  AND creation_time"
33539                    "      <= coalesce (CAST ((SELECT value FROM meta"
33540                    "                          WHERE name"
33541                    "                                = 'scap_check_time')"
33542                    "                         AS INTEGER),"
33543                    "                   0));"))
33544         event (EVENT_UPDATED_SECINFO, "ovaldef", 0, 0);
33545     }
33546 }
33547 
33548 /**
33549  * @brief Check for updated CERT SecInfo after an update.
33550  */
33551 static void
check_for_updated_cert()33552 check_for_updated_cert ()
33553 {
33554   if (manage_cert_loaded ())
33555     {
33556       if (sql_int ("SELECT EXISTS"
33557                    " (SELECT * FROM cert.cert_bund_advs"
33558                    "  WHERE modification_time"
33559                    "        > coalesce (CAST ((SELECT value FROM meta"
33560                    "                           WHERE name"
33561                    "                                 = 'cert_check_time')"
33562                    "                          AS INTEGER),"
33563                    "                    0)"
33564                    "  AND creation_time"
33565                    "      <= coalesce (CAST ((SELECT value FROM meta"
33566                    "                          WHERE name"
33567                    "                                = 'cert_check_time')"
33568                    "                         AS INTEGER),"
33569                    "                   0));"))
33570         event (EVENT_UPDATED_SECINFO, "cert_bund_adv", 0, 0);
33571 
33572       if (sql_int ("SELECT EXISTS"
33573                    " (SELECT * FROM cert.dfn_cert_advs"
33574                    "  WHERE modification_time"
33575                    "        > coalesce (CAST ((SELECT value FROM meta"
33576                    "                           WHERE name"
33577                    "                                 = 'cert_check_time')"
33578                    "                          AS INTEGER),"
33579                    "                    0)"
33580                    "  AND creation_time"
33581                    "      <= coalesce (CAST ((SELECT value FROM meta"
33582                    "                          WHERE name"
33583                    "                                = 'cert_check_time')"
33584                    "                         AS INTEGER),"
33585                    "                   0));"))
33586         event (EVENT_UPDATED_SECINFO, "dfn_cert_adv", 0, 0);
33587     }
33588 }
33589 
33590 
33591 /* Credentials. */
33592 
33593 /**
33594  * @brief Ensure that there is an encryption key.
33595  *
33596  * This prevents contention problems that can happen when the key is
33597  * created on the fly during a GMP operation.
33598  *
33599  * Up to caller to create transaction.
33600  *
33601  * @return 0 success, -1 error.
33602  */
33603 static int
check_db_encryption_key()33604 check_db_encryption_key ()
33605 {
33606   lsc_crypt_ctx_t crypt_ctx;
33607   gchar *secret;
33608 
33609   crypt_ctx = lsc_crypt_new ();
33610   /* The encryption layer creates the key if it does not exist. */
33611   secret = lsc_crypt_encrypt (crypt_ctx, "dummy", "dummy", NULL);
33612   lsc_crypt_release (crypt_ctx);
33613   if (secret == NULL)
33614     return -1;
33615   g_free (secret);
33616   return 0;
33617 }
33618 
33619 /**
33620  * @brief Check that a string represents a valid Private Key.
33621  *
33622  * @param[in]  key_str      Private Key string.
33623  * @param[in]  key_phrase   Private Key passphrase.
33624  *
33625  * @return 0 if valid, 1 otherwise.
33626  */
33627 int
check_private_key(const char * key_str,const char * key_phrase)33628 check_private_key (const char *key_str, const char *key_phrase)
33629 {
33630   gnutls_x509_privkey_t key;
33631   gnutls_datum_t data;
33632   int ret;
33633 
33634   assert (key_str);
33635   if (gnutls_x509_privkey_init (&key))
33636     return 1;
33637   data.size = strlen (key_str);
33638   data.data = (void *) g_strdup (key_str);
33639   ret = gnutls_x509_privkey_import2 (key, &data, GNUTLS_X509_FMT_PEM,
33640                                      key_phrase, 0);
33641   if (ret)
33642     {
33643       gchar *public_key;
33644       public_key = gvm_ssh_public_from_private (key_str, key_phrase);
33645 
33646       if (public_key == NULL)
33647         {
33648           gnutls_x509_privkey_deinit (key);
33649           g_free (data.data);
33650           g_message ("%s: import failed: %s",
33651                      __func__, gnutls_strerror (ret));
33652           return 1;
33653         }
33654       g_free (public_key);
33655     }
33656   g_free (data.data);
33657   gnutls_x509_privkey_deinit (key);
33658   return 0;
33659 }
33660 
33661 /**
33662  * @brief Find a credential for a specific permission, given a UUID.
33663  *
33664  * @param[in]   uuid            UUID of credential.
33665  * @param[out]  credential      Credential return, 0 if successfully failed
33666  *                              to find Credential.
33667  * @param[in]   permission      Permission.
33668  *
33669  * @return FALSE on success (including if failed to find credential), TRUE
33670  *         on error.
33671  */
33672 gboolean
find_credential_with_permission(const char * uuid,credential_t * credential,const char * permission)33673 find_credential_with_permission (const char* uuid,
33674                                  credential_t* credential,
33675                                  const char *permission)
33676 {
33677   return find_resource_with_permission ("credential", uuid, credential,
33678                                         permission, 0);
33679 }
33680 
33681 /**
33682  * @brief Set data for a credential.
33683  *
33684  * @param[in]  credential     The credential.
33685  * @param[in]  type           The data type (e.g. "username" or "secret").
33686  * @param[in]  value          The value to set or NULL to remove data entry.
33687  *
33688  * @return  0 on success, -1 on error, 1 credential not found, 99 permission
33689  *          denied.
33690  */
33691 static int
set_credential_data(credential_t credential,const char * type,const char * value)33692 set_credential_data (credential_t credential,
33693                      const char* type,
33694                      const char* value)
33695 {
33696   gchar *quoted_type;
33697 
33698   if (current_credentials.uuid
33699       && (acl_user_may ("modify_credential") == 0))
33700     return 99;
33701 
33702   if (type == NULL)
33703     return -1;
33704 
33705   if (credential == 0)
33706     return 1;
33707 
33708   quoted_type = sql_quote (type);
33709 
33710   if (sql_int ("SELECT count (*) FROM credentials_data"
33711                " WHERE credential = '%llu' AND type = '%s';",
33712                credential, quoted_type))
33713     {
33714       if (value == NULL)
33715         {
33716           sql ("DELETE FROM credentials_data"
33717                " WHERE credential = '%llu' AND type = '%s';",
33718                credential, quoted_type);
33719         }
33720       else
33721         {
33722           gchar *quoted_value;
33723           quoted_value = sql_quote (value);
33724           sql ("UPDATE credentials_data SET value = '%s'"
33725               " WHERE credential = %llu AND type = '%s';",
33726                 quoted_value, credential, quoted_type);
33727           g_free (quoted_value);
33728         }
33729     }
33730   else if (value != NULL)
33731     {
33732       gchar *quoted_value;
33733       quoted_value = sql_quote (value);
33734       sql ("INSERT INTO credentials_data (credential, type, value)"
33735             " VALUES (%llu, '%s', '%s')",
33736             credential, quoted_type, quoted_value);
33737       g_free (quoted_value);
33738     }
33739 
33740   g_free (quoted_type);
33741   return 0;
33742 }
33743 
33744 /**
33745  * @brief Test if a username is valid to use in a credential.
33746  *
33747  * Valid usernames may only contain alphanumeric characters and a few
33748  *  special ones to avoid problems with installer package generation.
33749  *
33750  * @param[in]  username  The username string to test.
33751  *
33752  * @return Whether the username is valid.
33753  */
33754 static int
validate_credential_username(const gchar * username)33755 validate_credential_username (const gchar *username)
33756 {
33757   const char *s;
33758   s = username;
33759   while (*s)
33760     if (isalnum (*s)
33761         || strchr ("-_\\.@", *s))
33762       s++;
33763     else
33764       return 0;
33765 
33766   return 1;
33767 }
33768 
33769 /**
33770  * @brief Test if a username is valid for a credential export format.
33771  *
33772  * @param[in]  username  The username string to test.
33773  * @param[in]  format    The credential format to validate for.
33774  *
33775  * @return Whether the username is valid.
33776  */
33777 static gboolean
validate_credential_username_for_format(const gchar * username,credential_format_t format)33778 validate_credential_username_for_format (const gchar *username,
33779                                          credential_format_t format)
33780 {
33781   const char *s, *name_characters;
33782 
33783   name_characters = NULL;
33784   switch (format)
33785     {
33786       case CREDENTIAL_FORMAT_NONE:
33787       case CREDENTIAL_FORMAT_KEY:
33788       case CREDENTIAL_FORMAT_PEM:
33789         // No validation required
33790         break;
33791       case CREDENTIAL_FORMAT_RPM:
33792       case CREDENTIAL_FORMAT_DEB:
33793         name_characters = "-_";
33794         break;
33795       case CREDENTIAL_FORMAT_EXE:
33796         name_characters = "-_\\.@";
33797         break;
33798       case CREDENTIAL_FORMAT_ERROR:
33799         return 0;
33800     }
33801 
33802   if (name_characters)
33803     {
33804       s = username;
33805       while (*s)
33806         if (isalnum (*s)
33807             || strchr (name_characters, *s))
33808           s++;
33809         else
33810           return FALSE;
33811     }
33812 
33813   return TRUE;
33814 }
33815 
33816 /**
33817  * @brief Length of password generated in create_credential.
33818  */
33819 #define PASSWORD_LENGTH 10
33820 
33821 /**
33822  * @brief Create a Credential.
33823  *
33824  * @param[in]  name            Name of LSC credential.  Must be at least one
33825  *                             character long.
33826  * @param[in]  comment         Comment on LSC credential.
33827  * @param[in]  login           Name of LSC credential user.  Must be at least
33828  *                             one character long.
33829  * @param[in]  given_password  Password for password-only credential, NULL to
33830  *                             generate credentials.
33831  * @param[in]  key_private     Private key, or NULL.
33832  * @param[in]  key_public      Public key, or NULL.
33833  * @param[in]  certificate     Certificate, or NULL.
33834  * @param[in]  community          SNMP community string, or NULL.
33835  * @param[in]  auth_algorithm     SNMP authentication algorithm, or NULL.
33836  * @param[in]  privacy_password   SNMP privacy password.
33837  * @param[in]  privacy_algorithm  SNMP privacy algorithm.
33838  * @param[in]  given_type      Credential type or NULL.
33839  * @param[in]  allow_insecure  Whether to allow insecure uses.
33840  * @param[out] credential      Created Credential.
33841  *
33842  * @return 0 success, 1 LSC credential exists already, 2 invalid username,
33843  *         3 Failed to create public key from private key/password,
33844  *         4 Invalid credential type, 5 login username missing,
33845  *         6 password missing, 7 private key missing, 8 certificate missing,
33846  *         9 public key missing, 10 autogenerate not supported,
33847  *         11 community missing, 12 auth algorithm missing,
33848  *         14 privacy algorithm missing,
33849  *         15 invalid auth algorithm, 16 invalid privacy algorithm,
33850  *         17 invalid certificate, 99 permission denied, -1 error.
33851  */
33852 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)33853 create_credential (const char* name, const char* comment, const char* login,
33854                    const char* given_password,
33855                    const char* key_private, const char* key_public,
33856                    const char* certificate, const char* community,
33857                    const char* auth_algorithm, const char* privacy_password,
33858                    const char* privacy_algorithm,
33859                    const char* given_type, const char* allow_insecure,
33860                    credential_t *credential)
33861 {
33862   gchar *quoted_name, *quoted_comment, *quoted_type;
33863   int i;
33864   GRand *rand;
33865   gchar generated_password[PASSWORD_LENGTH];
33866   gchar *generated_private_key;
33867   credential_t new_credential;
33868   int auto_generate, allow_insecure_int;
33869   int using_snmp_v3;
33870   int ret;
33871 
33872   assert (name && strlen (name) > 0);
33873   assert (current_credentials.uuid);
33874   assert (comment);
33875 
33876   sql_begin_immediate ();
33877 
33878   if (acl_user_may ("create_credential") == 0)
33879     {
33880       sql_rollback ();
33881       return 99;
33882     }
33883 
33884   if (resource_with_name_exists (name, "credential", 0))
33885     {
33886       sql_rollback ();
33887       return 1;
33888     }
33889 
33890   if (allow_insecure
33891       && strcmp (allow_insecure, "")
33892       && strcmp (allow_insecure, "0"))
33893     allow_insecure_int = 1;
33894   else
33895     allow_insecure_int = 0;
33896 
33897   if (given_type && strcmp (given_type, ""))
33898     {
33899       if (strcmp (given_type, "cc")
33900           && strcmp (given_type, "pgp")
33901           && strcmp (given_type, "pw")
33902           && strcmp (given_type, "snmp")
33903           && strcmp (given_type, "smime")
33904           && strcmp (given_type, "up")
33905           && strcmp (given_type, "usk"))
33906         {
33907           sql_rollback ();
33908           return 4;
33909         }
33910       else
33911         quoted_type = g_strdup (given_type);
33912     }
33913   else if (community
33914            || (login && given_password && auth_algorithm))
33915     quoted_type = g_strdup ("snmp");
33916   else if (certificate && key_private)
33917     quoted_type = g_strdup ("cc");
33918   else if (login && key_private)
33919     quoted_type = g_strdup ("usk");
33920   else if (login && given_password)
33921     quoted_type = g_strdup ("up");
33922   else if (login && key_private == NULL && given_password == NULL)
33923     quoted_type = g_strdup ("usk"); /* auto-generate */
33924   else
33925     {
33926       g_warning ("%s: Cannot determine type of new credential", __func__);
33927       return -1;
33928     }
33929 
33930   /* Validate credential data */
33931   auto_generate = ((given_password == NULL) && (key_private == NULL)
33932                    && (key_public == NULL) && (certificate == NULL)
33933                    && (community == NULL));
33934   ret = 0;
33935 
33936   if (auto_generate
33937       && (strcmp (quoted_type, "cc") == 0
33938           || strcmp (quoted_type, "pgp") == 0
33939           || strcmp (quoted_type, "smime") == 0
33940           || strcmp (quoted_type, "snmp") == 0))
33941     ret = 10; // Type does not support autogenerate
33942 
33943   using_snmp_v3 = 0;
33944 
33945   if (login == NULL
33946       && strcmp (quoted_type, "cc")
33947       && strcmp (quoted_type, "pgp")
33948       && strcmp (quoted_type, "pw")
33949       && strcmp (quoted_type, "smime")
33950       && strcmp (quoted_type, "snmp"))
33951     ret = 5;
33952   else if (given_password == NULL && auto_generate == 0
33953            && (strcmp (quoted_type, "up") == 0
33954                || strcmp (quoted_type, "pw") == 0))
33955       // (username) password requires a password
33956     ret = 6;
33957   else if (key_private == NULL && auto_generate == 0
33958            && (strcmp (quoted_type, "cc") == 0
33959                || strcmp (quoted_type, "usk") == 0))
33960     ret = 7;
33961   else if (certificate == NULL && auto_generate == 0
33962            && (strcmp (quoted_type, "cc") == 0
33963                || strcmp (quoted_type, "smime") == 0))
33964     ret = 8;
33965   else if (key_public == NULL && auto_generate == 0
33966            && strcmp (quoted_type, "pgp") == 0)
33967     ret = 9;
33968   else if (strcmp (quoted_type, "snmp") == 0)
33969     {
33970       if (login || given_password || auth_algorithm
33971           || privacy_password || privacy_algorithm)
33972         using_snmp_v3 = 1;
33973 
33974       if (community == NULL)
33975         {
33976           if (login == NULL || given_password == NULL)
33977             ret = 11;
33978         }
33979       else if (auth_algorithm == NULL && using_snmp_v3)
33980         ret = 12;
33981       else if ((privacy_algorithm == NULL
33982                 || strcmp (privacy_algorithm, "") == 0)
33983                && privacy_password
33984                && strcmp (privacy_password, ""))
33985         ret = 14;
33986       else if (auth_algorithm
33987                && strcmp (auth_algorithm, "md5")
33988                && strcmp (auth_algorithm, "sha1"))
33989         ret = 15;
33990       else if (privacy_algorithm
33991                && strcmp (privacy_algorithm, "")
33992                && strcmp (privacy_algorithm, "aes")
33993                && strcmp (privacy_algorithm, "des"))
33994         ret = 16;
33995     }
33996 
33997   if (ret)
33998     {
33999       g_free (quoted_type);
34000       sql_rollback ();
34001       return ret;
34002     }
34003 
34004   /* Create base credential */
34005   quoted_name = sql_quote (name);
34006   quoted_comment = sql_quote (comment ? comment : "");
34007 
34008   sql ("INSERT INTO credentials"
34009        " (uuid, name, owner, comment, creation_time, modification_time,"
34010        "  type, allow_insecure)"
34011        " VALUES"
34012        " (make_uuid (), '%s',"
34013        "  (SELECT id FROM users WHERE users.uuid = '%s'),"
34014        "   '%s', m_now (), m_now (), '%s', %d);",
34015        quoted_name,
34016        current_credentials.uuid,
34017        quoted_comment,
34018        quoted_type,
34019        allow_insecure_int);
34020 
34021   g_free (quoted_name);
34022   g_free (quoted_comment);
34023 
34024   new_credential = sql_last_insert_id ();
34025 
34026   /* Add non-secret data */
34027   if (login)
34028     {
34029       /*
34030        * Ensure the login does not contain characters that cause problems
34031        *  with package generation.
34032        */
34033       if (validate_credential_username (login) == 0)
34034         {
34035           sql_rollback ();
34036           return 2;
34037         }
34038 
34039       set_credential_data (new_credential,
34040                            "username", login);
34041     }
34042 
34043   if (key_public)
34044     set_credential_data (new_credential, "public_key", key_public);
34045 
34046   if (certificate)
34047     {
34048       gchar *certificate_truncated;
34049       certificate_truncated = truncate_certificate (certificate);
34050       if (certificate_truncated)
34051         set_credential_data (new_credential,
34052                              "certificate", certificate_truncated);
34053       else
34054         {
34055           sql_rollback();
34056           return 17;
34057         }
34058       g_free (certificate_truncated);
34059     }
34060   if (auth_algorithm)
34061     set_credential_data (new_credential,
34062                          "auth_algorithm", auth_algorithm);
34063   if (privacy_algorithm)
34064     set_credential_data (new_credential,
34065                          "privacy_algorithm", privacy_algorithm);
34066 
34067   g_free (quoted_type);
34068 
34069   /* Add secret data like passwords and private keys */
34070   /* Private key with optional passphrase */
34071   if (key_private)
34072     {
34073       lsc_crypt_ctx_t crypt_ctx;
34074       gchar *key_private_truncated, *generated_key_public;
34075 
34076       /* Try truncate the private key, but if that fails try get the public
34077        * key anyway, in case it's a key type that truncate_private_key does
34078        * not understand. */
34079 
34080       if (key_private)
34081         key_private_truncated = truncate_private_key (key_private);
34082       else
34083         return 3;
34084 
34085       generated_key_public = gvm_ssh_public_from_private
34086                                 (key_private_truncated
34087                                     ? key_private_truncated
34088                                     : key_private,
34089                                  given_password);
34090       if (generated_key_public == NULL)
34091         {
34092           g_free (key_private_truncated);
34093           return 3;
34094         }
34095       g_free (generated_key_public);
34096 
34097       /* Encrypt password and private key.  Note that we do not need
34098          to call sql_quote because the result of the encryption is
34099          base64 encoded and does not contain apostrophes.  */
34100       if (!disable_encrypted_credentials)
34101         {
34102           gchar *secret;
34103           crypt_ctx = lsc_crypt_new ();
34104           secret = lsc_crypt_encrypt (crypt_ctx,
34105                                       "password", given_password,
34106                                       "private_key", key_private, NULL);
34107           if (!secret)
34108             {
34109               lsc_crypt_release (crypt_ctx);
34110               sql_rollback ();
34111               return -1;
34112             }
34113           set_credential_data (new_credential, "secret", secret);
34114           g_free (secret);
34115         }
34116       else
34117         {
34118           crypt_ctx = NULL;
34119           set_credential_data (new_credential, "password", given_password);
34120           set_credential_data (new_credential, "private_key", key_private);
34121         }
34122       lsc_crypt_release (crypt_ctx);
34123 
34124       if (credential)
34125         *credential = new_credential;
34126 
34127       sql_commit ();
34128       return 0;
34129     }
34130 
34131   /* SNMP passwords */
34132   if (community || using_snmp_v3)
34133     {
34134       lsc_crypt_ctx_t crypt_ctx;
34135 
34136       /* Encrypt passwords.  Note that we do not need
34137          to call sql_quote because the result of the encryption is
34138          base64 encoded and does not contain apostrophes.  */
34139       if (!disable_encrypted_credentials)
34140         {
34141           gchar *secret;
34142           crypt_ctx = lsc_crypt_new ();
34143           secret = lsc_crypt_encrypt (crypt_ctx,
34144                                       "community", community,
34145                                       "password", given_password,
34146                                       "privacy_password",
34147                                       privacy_password ? privacy_password : "",
34148                                       NULL);
34149           if (!secret)
34150             {
34151               lsc_crypt_release (crypt_ctx);
34152               sql_rollback ();
34153               return -1;
34154             }
34155           set_credential_data (new_credential, "secret", secret);
34156           g_free (secret);
34157         }
34158       else
34159         {
34160           crypt_ctx = NULL;
34161           set_credential_data (new_credential, "community", community);
34162           set_credential_data (new_credential, "password", given_password);
34163           set_credential_data (new_credential, "privacy_password",
34164                                privacy_password ? privacy_password : "");
34165         }
34166       lsc_crypt_release (crypt_ctx);
34167 
34168       if (credential)
34169         *credential = new_credential;
34170 
34171       sql_commit ();
34172       return 0;
34173     }
34174 
34175   /* Password only */
34176   if (given_password)
34177     {
34178       lsc_crypt_ctx_t crypt_ctx;
34179 
34180       /* Password-only credential. */
34181 
34182       if (!disable_encrypted_credentials)
34183         {
34184           crypt_ctx = lsc_crypt_new ();
34185           gchar *secret = lsc_crypt_encrypt (crypt_ctx,
34186                                              "password", given_password,
34187                                              NULL);
34188           if (!secret)
34189             {
34190               lsc_crypt_release (crypt_ctx);
34191               sql_rollback ();
34192               return -1;
34193             }
34194           set_credential_data (new_credential, "secret", secret);
34195           g_free (secret);
34196         }
34197       else
34198         {
34199           crypt_ctx = NULL;
34200           set_credential_data (new_credential, "password", given_password);
34201         }
34202 
34203       if (credential)
34204         *credential = new_credential;
34205 
34206       sql_commit ();
34207       return 0;
34208     }
34209 
34210   /*
34211    * Auto-generate credential
34212    */
34213 
34214   /* Create the keys and packages. */
34215 
34216   rand = g_rand_new ();
34217   for (i = 0; i < PASSWORD_LENGTH - 1; i++)
34218     {
34219       generated_password[i] = (gchar) g_rand_int_range (rand, '0', 'z');
34220       if (generated_password[i] == '\\')
34221         generated_password[i] = '{';
34222     }
34223   generated_password[PASSWORD_LENGTH - 1] = '\0';
34224   g_rand_free (rand);
34225 
34226   if (given_type == NULL || strcmp (given_type, "usk") == 0)
34227     {
34228       if (lsc_user_keys_create (generated_password, &generated_private_key))
34229         {
34230           sql_rollback ();
34231           return -1;
34232         }
34233     }
34234   else
34235     generated_private_key = NULL;
34236 
34237   {
34238     lsc_crypt_ctx_t crypt_ctx;
34239 
34240     /* Generated key credential. */
34241 
34242     if (!disable_encrypted_credentials)
34243       {
34244         gchar *secret;
34245         crypt_ctx = lsc_crypt_new ();
34246         if (generated_private_key)
34247           secret = lsc_crypt_encrypt (crypt_ctx,
34248                                       "password", generated_password,
34249                                       "private_key", generated_private_key,
34250                                       NULL);
34251         else
34252           secret = lsc_crypt_encrypt (crypt_ctx,
34253                                       "password", generated_password,
34254                                       NULL);
34255 
34256         if (!secret)
34257           {
34258             lsc_crypt_release (crypt_ctx);
34259             sql_rollback ();
34260             return -1;
34261           }
34262         set_credential_data (new_credential, "secret", secret);
34263         g_free (secret);
34264       }
34265     else
34266       {
34267         set_credential_data (new_credential, "password", generated_password);
34268         if (generated_private_key)
34269           set_credential_data (new_credential,
34270                                "private_key", generated_private_key);
34271         crypt_ctx = NULL;
34272       }
34273     lsc_crypt_release (crypt_ctx);
34274     g_free (generated_private_key);
34275   }
34276 
34277   if (credential)
34278     *credential = new_credential;
34279 
34280   sql_commit ();
34281 
34282   return 0;
34283 }
34284 
34285 /**
34286  * @brief Create an LSC Credential from an existing one.
34287  *
34288  * @param[in]  name                 Name of new Credential. NULL to copy
34289  *                                  from existing.
34290  * @param[in]  comment              Comment on new Credential. NULL to copy
34291  *                                  from existing.
34292  * @param[in]  credential_id        UUID of existing Credential.
34293  * @param[out] new_credential       New Credential.
34294  *
34295  * @return 0 success, 1 Credential exists already, 2 failed to find
34296  *         existing Credential, -1 error.
34297  */
34298 int
copy_credential(const char * name,const char * comment,const char * credential_id,credential_t * new_credential)34299 copy_credential (const char* name, const char* comment,
34300                  const char *credential_id,
34301                  credential_t* new_credential)
34302 {
34303   int ret;
34304   credential_t credential;
34305 
34306   assert (new_credential);
34307 
34308   ret = copy_resource ("credential", name, comment, credential_id,
34309                        "type", 1, new_credential, &credential);
34310   if (ret)
34311     return ret;
34312 
34313   sql ("INSERT INTO credentials_data (credential, type, value)"
34314        " SELECT %llu, type, value FROM credentials_data"
34315        " WHERE credential = %llu;",
34316        *new_credential, credential);
34317 
34318   return 0;
34319 }
34320 
34321 /**
34322  * @brief Modify a Credential.
34323  *
34324  * @param[in]   credential_id       UUID of Credential.
34325  * @param[in]   name                Name of Credential.
34326  * @param[in]   comment             Comment on Credential.
34327  * @param[in]   login               Login of Credential.
34328  * @param[in]   password            Password or passphrase of Credential.
34329  * @param[in]   key_private         Private key of Credential.
34330  * @param[in]   key_public          Public key of Credential.
34331  * @param[in]   certificate         Certificate of Credential.
34332  * @param[in]   community           SNMP Community of Credential.
34333  * @param[in]   auth_algorithm      Authentication algorithm of Credential.
34334  * @param[in]   privacy_password    Privacy password of Credential.
34335  * @param[in]   privacy_algorithm   Privacy algorithm of Credential.
34336  * @param[in]   allow_insecure      Whether to allow insecure use.
34337  *
34338  * @return 0 success, 1 failed to find credential, 2 credential with new name
34339  *         exists, 3 credential_id required, 4 invalid login name,
34340  *         5 invalid certificate, 6 invalid auth_algorithm,
34341  *         7 invalid privacy_algorithm, 8 invalid private key,
34342  *         9 invalid public key,
34343  *         10 privacy password must be empty if algorithm is empty
34344  *         99 permission denied,
34345  *         -1 internal error.
34346  */
34347 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)34348 modify_credential (const char *credential_id,
34349                    const char *name, const char *comment,
34350                    const char *login, const char* password,
34351                    const char* key_private, const char* key_public,
34352                    const char* certificate,
34353                    const char* community, const char* auth_algorithm,
34354                    const char* privacy_password, const char* privacy_algorithm,
34355                    const char* allow_insecure)
34356 {
34357   credential_t credential;
34358   iterator_t iterator;
34359   int ret;
34360 
34361   if (credential_id == NULL)
34362     return 3;
34363 
34364   sql_begin_immediate ();
34365 
34366   assert (current_credentials.uuid);
34367 
34368   if (acl_user_may ("modify_credential") == 0)
34369     {
34370       sql_rollback ();
34371       return 99;
34372     }
34373 
34374   credential = 0;
34375   if (find_credential_with_permission (credential_id, &credential,
34376                                        "modify_credential"))
34377     {
34378       sql_rollback ();
34379       return -1;
34380     }
34381 
34382   if (credential == 0)
34383     {
34384       sql_rollback ();
34385       return 1;
34386     }
34387 
34388   /* Check whether a credential with the same name exists already. */
34389   if (name)
34390     {
34391       if (resource_with_name_exists (name, "credential", credential))
34392         {
34393           sql_rollback ();
34394           return 2;
34395         }
34396     }
34397 
34398   /* Update values */
34399 
34400   if (name)
34401     set_credential_name (credential, name);
34402 
34403   if (comment)
34404     set_credential_comment (credential, comment);
34405 
34406   if (allow_insecure)
34407     {
34408       if (strcmp (allow_insecure, "") && strcmp (allow_insecure, "0"))
34409         sql ("UPDATE credentials SET allow_insecure = 1 WHERE id = %llu;",
34410              credential);
34411       else
34412         sql ("UPDATE credentials SET allow_insecure = 0 WHERE id = %llu;",
34413              credential);
34414     }
34415 
34416   ret = 0;
34417 
34418   if (login && ret == 0)
34419     {
34420       /*
34421        * Ensure the login is not empty and does not contain characters that
34422        *  cause problems with package generation.
34423        */
34424       if (strcmp (login, "") == 0
34425           || validate_credential_username (login) == 0)
34426         ret = 4;
34427 
34428       if (ret == 0)
34429         set_credential_login (credential, login);
34430     }
34431 
34432   if (certificate && ret == 0)
34433     {
34434       // Truncate certificate which also validates it.
34435       gchar *certificate_truncated;
34436       certificate_truncated = truncate_certificate (certificate);
34437       if (certificate_truncated)
34438         {
34439           set_credential_certificate (credential, certificate_truncated);
34440           g_free (certificate_truncated);
34441         }
34442       else
34443         ret = 5;
34444     }
34445 
34446   if (auth_algorithm && ret == 0)
34447     {
34448       if (strcmp (auth_algorithm, "md5")
34449           && strcmp (auth_algorithm, "sha1"))
34450         ret = 6;
34451       else
34452         set_credential_auth_algorithm (credential, auth_algorithm);
34453     }
34454 
34455   if (privacy_algorithm && ret == 0)
34456     {
34457       if (strcmp (privacy_algorithm, "aes")
34458           && strcmp (privacy_algorithm, "des")
34459           && strcmp (privacy_algorithm, ""))
34460         ret = 7;
34461       else
34462         {
34463           iterator_t credential_iterator;
34464           const char *used_password;
34465 
34466           init_credential_iterator_one (&credential_iterator, credential);
34467 
34468           if (privacy_password)
34469             used_password = privacy_password;
34470           else
34471             {
34472               next (&credential_iterator);
34473               used_password
34474                 = credential_iterator_privacy_password (&credential_iterator);
34475               if (used_password == NULL)
34476                 used_password = "";
34477             }
34478 
34479           // Privacy password must be empty if algorithm is empty
34480           if (strcmp (privacy_algorithm, "") == 0
34481               && strcmp (used_password, ""))
34482             ret = 10;
34483           else
34484             set_credential_privacy_algorithm (credential, privacy_algorithm);
34485 
34486           cleanup_iterator (&credential_iterator);
34487         }
34488     }
34489 
34490   if (key_public && ret == 0)
34491     {
34492       set_credential_public_key (credential, key_public);
34493     }
34494 
34495   if (ret)
34496     {
34497       sql_rollback ();
34498       return ret;
34499     }
34500 
34501   init_credential_iterator_one (&iterator, credential);
34502   if (next (&iterator))
34503     {
34504       gchar *key_private_truncated = NULL;
34505       const char *key_private_to_use;
34506       const char *type = credential_iterator_type (&iterator);
34507 
34508       if (key_private)
34509         {
34510           char *generated_key_public = NULL;
34511           /* Try truncate the private key, but if that fails try get the
34512            * public key anyway, in case it's a key type that
34513            * truncate_private_key does not understand. */
34514           key_private_truncated = truncate_private_key (key_private);
34515           key_private_to_use = key_private_truncated ? key_private_truncated
34516                                                      : key_private;
34517 
34518           if (strcmp (type, "cc") == 0)
34519             {
34520               generated_key_public
34521                   = gvm_ssh_public_from_private
34522                               (key_private_to_use,
34523                                NULL);
34524             }
34525           else if (strcmp (type, "usk") == 0)
34526             {
34527               generated_key_public
34528                   = gvm_ssh_public_from_private
34529                               (key_private_to_use,
34530                                password
34531                                 ? password
34532                                 : credential_iterator_password (&iterator));
34533             }
34534 
34535           if (generated_key_public == NULL)
34536             {
34537               sql_rollback ();
34538               cleanup_iterator (&iterator);
34539               g_free (generated_key_public);
34540               return 8;
34541             }
34542           g_free (generated_key_public);
34543         }
34544       else
34545         key_private_to_use = NULL;
34546 
34547       if (strcmp (type, "cc") == 0)
34548         {
34549           if (key_private_to_use)
34550             set_credential_private_key (credential,
34551                                         key_private_to_use,
34552                                         NULL);
34553         }
34554       else if (strcmp (type, "up") == 0
34555                || strcmp (type, "pw") == 0)
34556         {
34557           if (password)
34558             set_credential_password (credential, password);
34559         }
34560       else if (strcmp (type, "usk") == 0)
34561         {
34562           if (key_private_to_use || password)
34563             {
34564               if (check_private_key (key_private_truncated
34565                                       ? key_private_to_use
34566                                       : credential_iterator_private_key
34567                                          (&iterator),
34568                                      password
34569                                       ? password
34570                                       : credential_iterator_password
34571                                          (&iterator)))
34572                 {
34573                   sql_rollback ();
34574                   cleanup_iterator (&iterator);
34575                   return 8;
34576                 }
34577 
34578               set_credential_private_key
34579                 (credential,
34580                  key_private_truncated
34581                   ? key_private_to_use
34582                   : credential_iterator_private_key (&iterator),
34583                  password
34584                   ? password
34585                   : credential_iterator_password (&iterator));
34586             }
34587         }
34588       else if (strcmp (type, "snmp") == 0)
34589         {
34590           if (privacy_password)
34591             {
34592               const char *used_algorithm;
34593 
34594               // Privacy_algorithm should be set above if given
34595               used_algorithm
34596                 = credential_iterator_privacy_algorithm (&iterator);
34597               if (used_algorithm == NULL)
34598                 used_algorithm = "";
34599 
34600               // privacy password must be empty if algorithm is empty
34601               if (strcmp (used_algorithm, "") == 0
34602                   && strcmp (privacy_password, ""))
34603                 {
34604                   sql_rollback ();
34605                   cleanup_iterator (&iterator);
34606                   return 10;
34607                 }
34608             }
34609 
34610           if (community || password || privacy_password)
34611             {
34612               set_credential_snmp_secret
34613                 (credential,
34614                  community
34615                   ? community
34616                   : credential_iterator_community (&iterator),
34617                  password
34618                   ? password
34619                   : credential_iterator_password (&iterator),
34620                  privacy_password
34621                   ? privacy_password
34622                   : credential_iterator_privacy_password (&iterator));
34623             }
34624         }
34625       else if (strcmp (type, "pgp") == 0
34626                || strcmp (type, "smime") == 0)
34627         {
34628           set_credential_data (credential, "secret", "");
34629         }
34630       else
34631         {
34632           g_warning ("%s: Unknown credential type: %s", __func__, type);
34633           sql_rollback ();
34634           cleanup_iterator (&iterator);
34635           g_free (key_private_truncated);
34636           return -1;
34637         }
34638 
34639       g_free (key_private_truncated);
34640     }
34641   else
34642     {
34643       g_warning ("%s: credential iterator next() failed", __func__);
34644       sql_rollback ();
34645       cleanup_iterator (&iterator);
34646       return -1;
34647     }
34648 
34649   cleanup_iterator (&iterator);
34650 
34651   sql_commit ();
34652 
34653   return 0;
34654 }
34655 
34656 /**
34657  * @brief Delete a Credential.
34658  *
34659  * @param[in]  credential_id      UUID of Credential.
34660  * @param[in]  ultimate           Whether to remove entirely, or to trashcan.
34661  *
34662  * @return 0 success, 1 fail because the credential is in use,
34663  *         2 failed to find credential, 99 permission denied, -1 error.
34664  */
34665 int
delete_credential(const char * credential_id,int ultimate)34666 delete_credential (const char *credential_id, int ultimate)
34667 {
34668   credential_t credential = 0;
34669 
34670   sql_begin_immediate ();
34671 
34672   if (acl_user_may ("delete_credential") == 0)
34673     {
34674       sql_rollback ();
34675       return 99;
34676     }
34677 
34678   if (find_credential_with_permission (credential_id, &credential,
34679                                        "delete_credential"))
34680     {
34681       sql_rollback ();
34682       return -1;
34683     }
34684 
34685   if (credential == 0)
34686     {
34687       if (find_trash ("credential", credential_id, &credential))
34688         {
34689           sql_rollback ();
34690           return -1;
34691         }
34692       if (credential == 0)
34693         {
34694           sql_rollback ();
34695           return 2;
34696         }
34697       if (ultimate == 0)
34698         {
34699           /* It's already in the trashcan. */
34700           sql_commit ();
34701           return 0;
34702         }
34703 
34704       /* Check if it's in use by another resource in the trashcan. */
34705       if (trash_credential_in_use (credential))
34706         {
34707           sql_rollback ();
34708           return 1;
34709         }
34710 
34711       permissions_set_orphans ("credential", credential,
34712                                LOCATION_TRASH);
34713       tags_remove_resource ("credential", credential,
34714                             LOCATION_TRASH);
34715 
34716       sql ("DELETE FROM credentials_trash_data WHERE credential = %llu;",
34717            credential);
34718       sql ("DELETE FROM credentials_trash WHERE id = %llu;", credential);
34719       sql_commit ();
34720       return 0;
34721     }
34722 
34723   /* Check if it's in use by another resource. */
34724   if (credential_in_use (credential))
34725     {
34726       sql_rollback ();
34727       return 1;
34728     }
34729 
34730   if (ultimate == 0)
34731     {
34732       credential_t trash_credential;
34733 
34734       sql ("INSERT INTO credentials_trash"
34735            " (uuid, owner, name, comment, creation_time,"
34736            "  modification_time, type)"
34737            " SELECT uuid, owner, name, comment, creation_time,"
34738            "        modification_time, type"
34739            " FROM credentials WHERE id = %llu;",
34740            credential);
34741       trash_credential = sql_last_insert_id ();
34742 
34743       sql ("INSERT INTO credentials_trash_data"
34744            " (credential, type, value)"
34745            " SELECT %llu, type, value"
34746            " FROM credentials_data"
34747            " WHERE credential = %llu",
34748            trash_credential, credential);
34749 
34750       /* Update the credential references in any trashcan targets or scanners.
34751        * This situation is possible if the user deletes the credential when
34752        * the target or scanner is in the trashcan. */
34753       sql ("UPDATE targets_trash_login_data"
34754            " SET credential_location = " G_STRINGIFY (LOCATION_TRASH) ","
34755            "     credential = %llu"
34756            " WHERE credential = %llu"
34757            "   AND credential_location = " G_STRINGIFY (LOCATION_TABLE) ";",
34758            trash_credential,
34759            credential);
34760       sql ("UPDATE scanners_trash"
34761            " SET credential_location = " G_STRINGIFY (LOCATION_TRASH) ","
34762            "     credential = %llu"
34763            " WHERE credential = %llu"
34764            "   AND credential_location = " G_STRINGIFY (LOCATION_TABLE) ";",
34765            trash_credential,
34766            credential);
34767 
34768       permissions_set_locations ("credential", credential,
34769                                  trash_credential,
34770                                  LOCATION_TRASH);
34771       tags_set_locations ("credential", credential,
34772                           trash_credential,
34773                           LOCATION_TRASH);
34774     }
34775   else
34776     {
34777       permissions_set_orphans ("credential", credential, LOCATION_TABLE);
34778       tags_remove_resource ("credential", credential, LOCATION_TABLE);
34779     }
34780 
34781   sql ("DELETE FROM credentials_data WHERE credential = %llu;", credential);
34782   sql ("DELETE FROM credentials WHERE id = %llu;", credential);
34783 
34784   sql_commit ();
34785   return 0;
34786 }
34787 
34788 /**
34789  * @brief Filter columns for LSC Credential iterator.
34790  */
34791 #define CREDENTIAL_ITERATOR_FILTER_COLUMNS                                    \
34792  { GET_ITERATOR_FILTER_COLUMNS, "login", "type", "allow_insecure", NULL }
34793 
34794 /**
34795  * @brief LSC Credential iterator columns.
34796  */
34797 #define CREDENTIAL_ITERATOR_COLUMNS                                           \
34798  {                                                                            \
34799    GET_ITERATOR_COLUMNS (credentials),                                        \
34800    /* public generic data */                                                  \
34801    { "type", NULL, KEYWORD_TYPE_STRING },                                     \
34802    { "allow_insecure", NULL, KEYWORD_TYPE_INTEGER },                          \
34803    /* public type specific data */                                            \
34804    { "(SELECT value FROM credentials_data"                                    \
34805      " WHERE credential = credentials.id AND type = 'username')",             \
34806      "login",                                                                 \
34807      KEYWORD_TYPE_STRING                                                      \
34808    },                                                                         \
34809    { "(SELECT value FROM credentials_data"                                    \
34810      " WHERE credential = credentials.id AND type = 'certificate')",          \
34811      NULL,                                                                    \
34812      KEYWORD_TYPE_STRING },                                                   \
34813    { "(SELECT value FROM credentials_data"                                    \
34814      " WHERE credential = credentials.id AND type = 'auth_algorithm')",       \
34815      NULL,                                                                    \
34816      KEYWORD_TYPE_STRING },                                                   \
34817    { "(SELECT value FROM credentials_data"                                    \
34818      " WHERE credential = credentials.id AND type = 'privacy_algorithm')",    \
34819      NULL,                                                                    \
34820      KEYWORD_TYPE_STRING },                                                   \
34821    { "(SELECT value FROM credentials_data"                                    \
34822      " WHERE credential = credentials.id AND type = 'public_key')",           \
34823      NULL,                                                                    \
34824      KEYWORD_TYPE_STRING },                                                   \
34825    /* private data */                                                         \
34826    { "(SELECT value FROM credentials_data"                                    \
34827      " WHERE credential = credentials.id AND type = 'secret')",               \
34828      "secret",                                                                \
34829      KEYWORD_TYPE_STRING },                                                   \
34830    { "(SELECT value FROM credentials_data"                                    \
34831      " WHERE credential = credentials.id AND type = 'password')",             \
34832      "password",                                                              \
34833      KEYWORD_TYPE_STRING },                                                   \
34834    { "(SELECT value FROM credentials_data"                                    \
34835      " WHERE credential = credentials.id AND type = 'private_key')",          \
34836      "private_key",                                                           \
34837      KEYWORD_TYPE_STRING },                                                   \
34838    { "(SELECT value FROM credentials_data"                                    \
34839      " WHERE credential = credentials.id AND type = 'community')",            \
34840      "community",                                                             \
34841      KEYWORD_TYPE_STRING },                                                   \
34842    { "(SELECT value FROM credentials_data"                                    \
34843      " WHERE credential = credentials.id AND type = 'privacy_password')",     \
34844      "privacy_password",                                                      \
34845      KEYWORD_TYPE_STRING },                                                   \
34846    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
34847  }
34848 
34849 /**
34850  * @brief LSC Credential iterator columns for trash case.
34851  */
34852 #define CREDENTIAL_ITERATOR_TRASH_COLUMNS                                     \
34853  {                                                                            \
34854    GET_ITERATOR_COLUMNS (credentials_trash),                                  \
34855    /* public generic data */                                                  \
34856    { "type", NULL, KEYWORD_TYPE_STRING },                                     \
34857    { "allow_insecure", NULL, KEYWORD_TYPE_INTEGER },                          \
34858    /* public type specific data */                                            \
34859    { "(SELECT value FROM credentials_trash_data"                              \
34860      " WHERE credential = credentials_trash.id AND type = 'username')",       \
34861      "login",                                                                 \
34862      KEYWORD_TYPE_STRING },                                                   \
34863    { "(SELECT value FROM credentials_trash_data"                              \
34864      " WHERE credential = credentials_trash.id AND type = 'certificate')",    \
34865      NULL,                                                                    \
34866      KEYWORD_TYPE_STRING },                                                   \
34867    { "(SELECT value FROM credentials_trash_data"                              \
34868      " WHERE credential = credentials_trash.id"                               \
34869      "   AND type = 'auth_algorithm')",                                       \
34870      NULL,                                                                    \
34871      KEYWORD_TYPE_STRING },                                                   \
34872    { "(SELECT value FROM credentials_trash_data"                              \
34873      " WHERE credential = credentials_trash.id"                               \
34874      "   AND type = 'privacy_algorithm')",                                    \
34875      NULL,                                                                    \
34876      KEYWORD_TYPE_STRING },                                                   \
34877    { "(SELECT value FROM credentials_data"                                    \
34878      " WHERE credential = credentials_trash.id AND type = 'public_key')",     \
34879      NULL,                                                                    \
34880      KEYWORD_TYPE_STRING },                                                   \
34881    /* private data */                                                         \
34882    { "(SELECT value FROM credentials_trash_data"                              \
34883      " WHERE credential = credentials_trash.id AND type = 'secret')",         \
34884      "secret",                                                                \
34885      KEYWORD_TYPE_STRING },                                                   \
34886    { "(SELECT value FROM credentials_trash_data"                              \
34887      " WHERE credential = credentials_trash.id AND type = 'password')",       \
34888      "password",                                                              \
34889      KEYWORD_TYPE_STRING },                                                   \
34890    { "(SELECT value FROM credentials_trash_data"                              \
34891      " WHERE credential = credentials_trash.id AND type = 'private_key')",    \
34892      "private_key",                                                           \
34893      KEYWORD_TYPE_STRING },                                                   \
34894    { "(SELECT value FROM credentials_trash_data"                              \
34895      " WHERE credential = credentials_trash.id AND type = 'community')",      \
34896      "community",                                                             \
34897      KEYWORD_TYPE_STRING },                                                   \
34898    { "(SELECT value FROM credentials_trash_data"                              \
34899      " WHERE credential = credentials_trash.id"                               \
34900      " AND type = 'privacy_password')",                                       \
34901      "privacy_password",                                                      \
34902      KEYWORD_TYPE_STRING },                                                   \
34903    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
34904  }
34905 
34906 /**
34907  * @brief Count number of LSC Credentials.
34908  *
34909  * @param[in]  get  GET params.
34910  *
34911  * @return Total number of LSC Credentials in filtered set.
34912  */
34913 int
credential_count(const get_data_t * get)34914 credential_count (const get_data_t *get)
34915 {
34916   static const char *filter_columns[] = CREDENTIAL_ITERATOR_FILTER_COLUMNS;
34917   static column_t columns[] = CREDENTIAL_ITERATOR_COLUMNS;
34918   static column_t trash_columns[] = CREDENTIAL_ITERATOR_TRASH_COLUMNS;
34919   return count ("credential", get, columns, trash_columns, filter_columns,
34920                 0, 0, 0, TRUE);
34921 }
34922 
34923 /**
34924  * @brief Check whether a Credential is in use.
34925  *
34926  * @param[in]  credential  Credential.
34927  *
34928  * @return 1 yes, 0 no.
34929  */
34930 int
credential_in_use(credential_t credential)34931 credential_in_use (credential_t credential)
34932 {
34933   int ret;
34934   char *uuid = credential_uuid (credential);
34935 
34936   ret = !!(sql_int ("SELECT count (*) FROM targets_login_data"
34937                     " WHERE credential = %llu;",
34938                     credential)
34939            || sql_int ("SELECT count (*) FROM scanners"
34940                        " WHERE credential = %llu;",
34941                        credential)
34942            || sql_int ("SELECT count (*) FROM alert_method_data"
34943                        " WHERE (name = 'recipient_credential'"
34944                        "        OR name = 'scp_credential'"
34945                        "        OR name = 'smb_credential'"
34946                        "        OR name = 'tp_sms_credential'"
34947                        "        OR name = 'verinice_server_credential'"
34948                        "        OR name = 'pkcs12_credential')"
34949                        " AND data = '%s'",
34950                        uuid));
34951 
34952   free (uuid);
34953   return ret;
34954 }
34955 
34956 /**
34957  * @brief Check whether a trashcan Credential is in use.
34958  *
34959  * @param[in]  credential  Credential.
34960  *
34961  * @return 1 yes, 0 no.
34962  */
34963 int
trash_credential_in_use(credential_t credential)34964 trash_credential_in_use (credential_t credential)
34965 {
34966   int ret;
34967   char *uuid = trash_credential_uuid (credential);
34968 
34969   ret = !!(sql_int ("SELECT count (*) FROM targets_trash_login_data"
34970                      " WHERE credential = %llu"
34971                      " AND credential_location"
34972                      "      = " G_STRINGIFY (LOCATION_TRASH) ";",
34973                      credential)
34974            || sql_int ("SELECT count (*) FROM scanners_trash"
34975                        " WHERE credential = %llu"
34976                        " AND credential_location"
34977                        "      = " G_STRINGIFY (LOCATION_TRASH) ";",
34978                        credential)
34979            || sql_int ("SELECT count (*) FROM alert_method_data_trash"
34980                        " WHERE (name = 'recipient_credential'"
34981                        "        OR name = 'scp_credential'"
34982                        "        OR name = 'smb_credential'"
34983                        "        OR name = 'tp_sms_credential'"
34984                        "        OR name = 'verinice_server_credential'"
34985                        "        OR name = 'pkcs12_credential')"
34986                        " AND data = '%s'",
34987                        uuid));
34988 
34989   free (uuid);
34990   return ret;
34991 }
34992 
34993 /**
34994  * @brief Check whether a Credential is writable.
34995  *
34996  * @param[in]  credential  Credential.
34997  *
34998  * @return 1 yes, 0 no.
34999  */
35000 int
credential_writable(credential_t credential)35001 credential_writable (credential_t credential)
35002 {
35003   return 1;
35004 }
35005 
35006 /**
35007  * @brief Check whether a trashcan Credential is writable.
35008  *
35009  * @param[in]  credential  Credential.
35010  *
35011  * @return 1 yes, 0 no.
35012  */
35013 int
trash_credential_writable(credential_t credential)35014 trash_credential_writable (credential_t credential)
35015 {
35016   return 1;
35017 }
35018 
35019 /**
35020  * @brief Get a value from a credential.
35021  *
35022  * @param[in]  credential     The Credential.
35023  * @param[in]  value_name     Name of the value.
35024  *
35025  * @return Value.
35026  */
35027 gchar *
credential_value(credential_t credential,const char * value_name)35028 credential_value (credential_t credential, const char* value_name)
35029 {
35030   if (credential == 0)
35031     return NULL;
35032 
35033   return sql_string ("SELECT value FROM credentials_data"
35034                      " WHERE credential = %llu"
35035                      "   AND type = '%s';",
35036                      credential, value_name);
35037 }
35038 
35039 /**
35040  * @brief Get a possibly encrypted credential value in decrypted form.
35041  *
35042  * @param[in]  credential     The Credential.
35043  * @param[in]  value_name     Name of the value.
35044  *
35045  * @return Value.
35046  */
35047 gchar *
credential_encrypted_value(credential_t credential,const char * value_name)35048 credential_encrypted_value (credential_t credential, const char* value_name)
35049 {
35050   gchar *value;
35051   value = sql_string ("SELECT value FROM credentials_data"
35052                       " WHERE credential = %llu"
35053                       "   AND type = '%s';",
35054                       credential, value_name);
35055 
35056   if (value == NULL)
35057     {
35058       gchar *secret;
35059       const char* decrypted;
35060       lsc_crypt_ctx_t crypt_ctx;
35061       crypt_ctx = lsc_crypt_new ();
35062 
35063       secret = sql_string ("SELECT value FROM credentials_data"
35064                            " WHERE credential = %llu"
35065                            "   AND type = 'secret';",
35066                            credential);
35067 
35068       decrypted = lsc_crypt_decrypt (crypt_ctx, secret, value_name);
35069       if (decrypted)
35070         value = g_strdup (decrypted);
35071       lsc_crypt_release (crypt_ctx);
35072       g_free (secret);
35073     }
35074 
35075   return value;
35076 }
35077 
35078 /**
35079  * @brief Set the name of a Credential.
35080  *
35081  * @param[in]  credential      The Credential.
35082  * @param[in]  name            Name.
35083  */
35084 static void
set_credential_name(credential_t credential,const char * name)35085 set_credential_name (credential_t credential, const char *name)
35086 {
35087   gchar *quoted_name = sql_quote (name);
35088   sql ("UPDATE credentials SET name = '%s', modification_time = m_now ()"
35089        " WHERE id = %llu;",
35090        quoted_name,
35091        credential);
35092   g_free (quoted_name);
35093 }
35094 
35095 /**
35096  * @brief Set the comment of a Credential.
35097  *
35098  * @param[in]  credential      The Credential.
35099  * @param[in]  comment         Comment.
35100  */
35101 static void
set_credential_comment(credential_t credential,const char * comment)35102 set_credential_comment (credential_t credential,
35103                         const char *comment)
35104 {
35105   gchar *quoted_comment = sql_quote (comment);
35106   sql ("UPDATE credentials SET comment = '%s', modification_time = m_now ()"
35107        " WHERE id = %llu;",
35108        quoted_comment,
35109        credential);
35110   g_free (quoted_comment);
35111 }
35112 
35113 /**
35114  * @brief Set the login of a Credential.
35115  *
35116  * @param[in]  credential      The Credential.
35117  * @param[in]  login           Login.
35118  */
35119 static void
set_credential_login(credential_t credential,const char * login)35120 set_credential_login (credential_t credential, const char *login)
35121 {
35122   set_credential_data (credential, "username", login);
35123   sql ("UPDATE credentials SET"
35124        " modification_time = m_now ()"
35125        " WHERE id = %llu;",
35126        credential);
35127 }
35128 
35129 /**
35130  * @brief Set the certificate of a Credential.
35131  *
35132  * @param[in]  credential      The Credential.
35133  * @param[in]  certificate     Certificate.
35134  */
35135 static void
set_credential_certificate(credential_t credential,const char * certificate)35136 set_credential_certificate (credential_t credential, const char *certificate)
35137 {
35138   set_credential_data (credential, "certificate", certificate);
35139   sql ("UPDATE credentials SET"
35140        " modification_time = m_now ()"
35141        " WHERE id = %llu;",
35142        credential);
35143 }
35144 
35145 /**
35146  * @brief Set the auth_algorithm of a Credential.
35147  *
35148  * @param[in]  credential      The Credential.
35149  * @param[in]  algorithm       Authentication algorithm.
35150  */
35151 static void
set_credential_auth_algorithm(credential_t credential,const char * algorithm)35152 set_credential_auth_algorithm (credential_t credential, const char *algorithm)
35153 {
35154   set_credential_data (credential, "auth_algorithm", algorithm);
35155   sql ("UPDATE credentials SET"
35156        " modification_time = m_now ()"
35157        " WHERE id = %llu;",
35158        credential);
35159 }
35160 
35161 /**
35162  * @brief Set the privacy_algorithm of a Credential.
35163  *
35164  * @param[in]  credential      The Credential.
35165  * @param[in]  algorithm       Privacy algorithm.
35166  */
35167 void
set_credential_privacy_algorithm(credential_t credential,const char * algorithm)35168 set_credential_privacy_algorithm (credential_t credential,
35169                                   const char *algorithm)
35170 {
35171   set_credential_data (credential, "privacy_algorithm", algorithm);
35172   sql ("UPDATE credentials SET"
35173        " modification_time = m_now ()"
35174        " WHERE id = %llu;",
35175        credential);
35176 }
35177 
35178 /**
35179  * @brief Set the password of a Credential.
35180  *
35181  * @param[in]  credential      The Credential.
35182  * @param[in]  password        Password.
35183  */
35184 static void
set_credential_password(credential_t credential,const char * password)35185 set_credential_password (credential_t credential, const char *password)
35186 {
35187   lsc_crypt_ctx_t crypt_ctx;
35188 
35189   if (!disable_encrypted_credentials)
35190     {
35191       gchar *encrypted_blob;
35192       crypt_ctx = lsc_crypt_new ();
35193       encrypted_blob = lsc_crypt_encrypt (crypt_ctx,
35194                                           "password", password, NULL);
35195       if (!encrypted_blob)
35196         {
35197           g_critical ("%s: encryption failed", G_STRFUNC);
35198           lsc_crypt_release (crypt_ctx);
35199           return;
35200         }
35201       set_credential_data (credential, "secret", encrypted_blob);
35202       set_credential_data (credential, "password", NULL);
35203       set_credential_data (credential, "private_key", NULL);
35204       g_free (encrypted_blob);
35205     }
35206   else
35207     {
35208       crypt_ctx = NULL;
35209       set_credential_data (credential, "secret", NULL);
35210       set_credential_data (credential, "password", password);
35211       set_credential_data (credential, "private_key", NULL);
35212     }
35213 
35214   sql ("UPDATE credentials SET modification_time = m_now ()"
35215        " WHERE id = %llu;",
35216        credential);
35217   lsc_crypt_release (crypt_ctx);
35218 }
35219 
35220 /**
35221  * @brief Set the private key and passphrase of a Credential.
35222  *
35223  * @param[in]  credential      The Credential.
35224  * @param[in]  private_key     Private key.
35225  * @param[in]  passphrase      Passphrase.
35226  */
35227 static void
set_credential_private_key(credential_t credential,const char * private_key,const char * passphrase)35228 set_credential_private_key (credential_t credential,
35229                             const char *private_key, const char *passphrase)
35230 {
35231   lsc_crypt_ctx_t crypt_ctx;
35232 
35233   if (!disable_encrypted_credentials)
35234     {
35235       gchar *encrypted_blob;
35236       crypt_ctx = lsc_crypt_new ();
35237       encrypted_blob = lsc_crypt_encrypt (crypt_ctx,
35238                                           "private_key", private_key,
35239                                           "password", passphrase,
35240                                           NULL);
35241       if (!encrypted_blob)
35242         {
35243           g_critical ("%s: encryption failed", G_STRFUNC);
35244           lsc_crypt_release (crypt_ctx);
35245           return;
35246         }
35247       set_credential_data (credential, "secret", encrypted_blob);
35248       set_credential_data (credential, "password", NULL);
35249       set_credential_data (credential, "private_key", NULL);
35250       g_free (encrypted_blob);
35251     }
35252   else
35253     {
35254       crypt_ctx = NULL;
35255       set_credential_data (credential, "secret", NULL);
35256       set_credential_data (credential, "password", passphrase);
35257       set_credential_data (credential, "private_key", private_key);
35258     }
35259 
35260   sql ("UPDATE credentials SET modification_time = m_now ()"
35261        " WHERE id = %llu;",
35262        credential);
35263   lsc_crypt_release (crypt_ctx);
35264 }
35265 
35266 /**
35267  * @brief Set the public key of a Credential.
35268  *
35269  * @param[in]  credential      The Credential.
35270  * @param[in]  public_key      Public key.
35271  */
35272 void
set_credential_public_key(credential_t credential,const char * public_key)35273 set_credential_public_key (credential_t credential, const char *public_key)
35274 {
35275   set_credential_data (credential, "public_key", public_key);
35276   sql ("UPDATE credentials SET"
35277        " modification_time = m_now ()"
35278        " WHERE id = %llu;",
35279        credential);
35280 }
35281 
35282 /**
35283  * @brief Set the community, password and privacy password of a Credential.
35284  *
35285  * @param[in]  credential         The Credential.
35286  * @param[in]  community          SNMP community.
35287  * @param[in]  password           Authentication password.
35288  * @param[in]  privacy_password   Privacy password.
35289  */
35290 static void
set_credential_snmp_secret(credential_t credential,const char * community,const char * password,const char * privacy_password)35291 set_credential_snmp_secret (credential_t credential, const char* community,
35292                             const char *password, const char *privacy_password)
35293 {
35294   lsc_crypt_ctx_t crypt_ctx;
35295 
35296   if (!disable_encrypted_credentials)
35297     {
35298       gchar *encrypted_blob;
35299       crypt_ctx = lsc_crypt_new ();
35300       encrypted_blob = lsc_crypt_encrypt (crypt_ctx,
35301                                           "community", community,
35302                                           "password", password,
35303                                           "privacy_password", privacy_password,
35304                                           NULL);
35305       if (!encrypted_blob)
35306         {
35307           g_critical ("%s: encryption failed", G_STRFUNC);
35308           lsc_crypt_release (crypt_ctx);
35309           return;
35310         }
35311       set_credential_data (credential, "secret", encrypted_blob);
35312       set_credential_data (credential, "community", NULL);
35313       set_credential_data (credential, "password", NULL);
35314       set_credential_data (credential, "privacy_password", NULL);
35315       g_free (encrypted_blob);
35316     }
35317   else
35318     {
35319       crypt_ctx = NULL;
35320       set_credential_data (credential, "secret", NULL);
35321       set_credential_data (credential, "community", community);
35322       set_credential_data (credential, "password", password);
35323       set_credential_data (credential, "privacy_password", privacy_password);
35324     }
35325 
35326   sql ("UPDATE credentials SET modification_time = m_now ()"
35327        " WHERE id = %llu;",
35328        credential);
35329   lsc_crypt_release (crypt_ctx);
35330 }
35331 
35332 /**
35333  * @brief Initialise a Credential iterator, given a single Credential.
35334  *
35335  * @param[in]  iterator        Iterator.
35336  * @param[in]  credential      Single Credential to iterate.
35337  */
35338 void
init_credential_iterator_one(iterator_t * iterator,credential_t credential)35339 init_credential_iterator_one (iterator_t* iterator,
35340                               credential_t credential)
35341 {
35342   get_data_t get;
35343 
35344   assert (credential);
35345 
35346   memset (&get, '\0', sizeof (get));
35347   get.id = credential_uuid (credential);
35348   get.filter = "owner=any permission=get_credentials";
35349 
35350   /* We could pass the return up to the caller, but we don't pass in
35351    * a filter id and the callers are all in situations where the
35352    * credential cannot disappear, so it's safe to ignore the return. */
35353   init_credential_iterator (iterator, &get);
35354 }
35355 
35356 /**
35357  * @brief Initialise a Credential iterator.
35358  *
35359  * @param[in]  iterator  Iterator.
35360  * @param[in]  get       GET data.
35361  *
35362  * @return 0 success, 1 failed to find filter, 2 failed to find
35363  *         filter (filt_id), -1 error.
35364  */
35365 int
init_credential_iterator(iterator_t * iterator,const get_data_t * get)35366 init_credential_iterator (iterator_t* iterator, const get_data_t *get)
35367 {
35368   static const char *filter_columns[] = CREDENTIAL_ITERATOR_FILTER_COLUMNS;
35369   static column_t columns[] = CREDENTIAL_ITERATOR_COLUMNS;
35370   static column_t trash_columns[] = CREDENTIAL_ITERATOR_TRASH_COLUMNS;
35371 
35372   return init_get_iterator (iterator,
35373                             "credential",
35374                             get,
35375                             columns,
35376                             trash_columns,
35377                             filter_columns,
35378                             0,
35379                             NULL,
35380                             NULL,
35381                             TRUE);
35382 }
35383 
35384 /**
35385  * @brief Get possibly encrypted data from credentials.
35386  *
35387  * @param[in]  iterator  Iterator.
35388  * @param[in]  type      Type of data.
35389  *
35390  * @return Data.
35391  */
35392 static const char*
credential_iterator_encrypted_data(iterator_t * iterator,const char * type)35393 credential_iterator_encrypted_data (iterator_t* iterator, const char* type)
35394 {
35395   const char *secret, *unencrypted;
35396 
35397   if (iterator->done)
35398     return NULL;
35399   secret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
35400   if (type == NULL)
35401     {
35402       g_warning ("%s: NULL data type given", __func__);
35403       return NULL;
35404     }
35405   else if (strcmp (type, "password") == 0)
35406     unencrypted = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
35407   else if (strcmp (type, "private_key") == 0)
35408     unencrypted  = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
35409   else if (strcmp (type, "community") == 0)
35410     unencrypted  = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 10);
35411   else if (strcmp (type, "privacy_password") == 0)
35412     unencrypted  = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 11);
35413   else
35414     {
35415       g_warning ("%s: unknown data type \"%s\"", __func__, type);
35416       return NULL;
35417     }
35418   /* If we do not have a private key, there is no encrypted data.
35419      Return the password as is or NULL.  */
35420   if (secret)
35421     {
35422       /* This is an encrypted credential.  */
35423       if (!iterator->crypt_ctx)
35424         iterator->crypt_ctx = lsc_crypt_new ();
35425 
35426       return lsc_crypt_decrypt (iterator->crypt_ctx, secret, type);
35427     }
35428   else
35429     {
35430       return unencrypted;
35431     }
35432 }
35433 
35434 /**
35435  * @brief Get the login from a Credential iterator.
35436  *
35437  * @param[in]  iterator  Iterator.
35438  *
35439  * @return Login, or NULL if iteration is complete.  Freed by
35440  *         cleanup_iterator.
35441  */
35442 DEF_ACCESS (credential_iterator_type, GET_ITERATOR_COLUMN_COUNT);
35443 
35444 /**
35445  * @brief Get the login from a Credential iterator.
35446  *
35447  * @param[in]  iterator  Iterator.
35448  *
35449  * @return Login, or NULL if iteration is complete.  Freed by
35450  *         cleanup_iterator.
35451  */
credential_iterator_allow_insecure(iterator_t * iterator)35452 int credential_iterator_allow_insecure (iterator_t* iterator)
35453 {
35454   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
35455 }
35456 
35457 /**
35458  * @brief Get the credential type abbreviation from an LSC credential iterator.
35459  *
35460  * @param[in]  iterator  Iterator.
35461  *
35462  * @return Credential type, or NULL if iteration is complete.  Freed by
35463  *         cleanup_iterator.
35464  */
35465 DEF_ACCESS (credential_iterator_login, GET_ITERATOR_COLUMN_COUNT + 2);
35466 
35467 /**
35468  * @brief Get the certificate from a Credential iterator.
35469  *
35470  * @param[in]  iterator  Iterator.
35471  *
35472  * @return Certificate, or NULL if iteration is complete. Freed by
35473  *          cleanup_iterator.
35474  */
35475 DEF_ACCESS (credential_iterator_certificate, GET_ITERATOR_COLUMN_COUNT + 3);
35476 
35477 /**
35478  * @brief Get the authentication algorithm from an LSC credential iterator.
35479  *
35480  * @param[in]  iterator  Iterator.
35481  *
35482  * @return Auth algorithm, or NULL if iteration is complete.  Freed by
35483  *         cleanup_iterator.
35484  */
35485 DEF_ACCESS (credential_iterator_auth_algorithm, GET_ITERATOR_COLUMN_COUNT + 4);
35486 
35487 /**
35488  * @brief Get the authentication algorithm from an LSC credential iterator.
35489  *
35490  * @param[in]  iterator  Iterator.
35491  *
35492  * @return Auth algorithm, or NULL if iteration is complete.  Freed by
35493  *         cleanup_iterator.
35494  */
35495 DEF_ACCESS (credential_iterator_privacy_algorithm,
35496             GET_ITERATOR_COLUMN_COUNT + 5);
35497 
35498 /**
35499  * @brief Get the public key from an LSC credential iterator.
35500  *
35501  * @param[in]  iterator  Iterator.
35502  *
35503  * @return Public key, or NULL if iteration is complete.  Freed by
35504  *         cleanup_iterator.
35505  */
35506 DEF_ACCESS (credential_iterator_public_key,
35507             GET_ITERATOR_COLUMN_COUNT + 6);
35508 
35509 /**
35510  * @brief Get the password from a Credential iterator.
35511  *
35512  * @param[in]  iterator  Iterator.
35513  *
35514  * @return Password, or NULL if iteration is complete.  Freed by
35515  *         cleanup_iterator.
35516  */
35517 const char*
credential_iterator_password(iterator_t * iterator)35518 credential_iterator_password (iterator_t* iterator)
35519 {
35520   return credential_iterator_encrypted_data (iterator, "password");
35521 }
35522 
35523 
35524 /**
35525  * @brief Get the private_key from a Credential iterator.
35526  *
35527  * @param[in]  iterator  Iterator.
35528  *
35529  * @return Private_key, or NULL if iteration is complete.  Freed by
35530  *         cleanup_iterator.
35531  */
35532 const char*
credential_iterator_private_key(iterator_t * iterator)35533 credential_iterator_private_key (iterator_t* iterator)
35534 {
35535   return credential_iterator_encrypted_data (iterator, "private_key");
35536 }
35537 
35538 
35539 /**
35540  * @brief Get the SNMP community from a Credential iterator.
35541  *
35542  * @param[in]  iterator  Iterator.
35543  *
35544  * @return SNMP community, or NULL if iteration is complete.  Freed by
35545  *         cleanup_iterator.
35546  */
35547 const char*
credential_iterator_community(iterator_t * iterator)35548 credential_iterator_community (iterator_t* iterator)
35549 {
35550   return credential_iterator_encrypted_data (iterator, "community");
35551 }
35552 
35553 
35554 /**
35555  * @brief Get the privacy password from a Credential iterator.
35556  *
35557  * @param[in]  iterator  Iterator.
35558  *
35559  * @return SNMP community, or NULL if iteration is complete.  Freed by
35560  *         cleanup_iterator.
35561  */
35562 const char*
credential_iterator_privacy_password(iterator_t * iterator)35563 credential_iterator_privacy_password (iterator_t* iterator)
35564 {
35565   return credential_iterator_encrypted_data (iterator, "privacy_password");
35566 }
35567 
35568 
35569 /**
35570  * @brief Get the rpm from a Credential iterator.
35571  *
35572  * @param[in]  iterator  Iterator.
35573  *
35574  * @return Rpm, or NULL if iteration is complete. Free with g_free().
35575  */
35576 char*
credential_iterator_rpm(iterator_t * iterator)35577 credential_iterator_rpm (iterator_t *iterator)
35578 {
35579   const char *private_key, *login, *pass;
35580   void *rpm;
35581   char *public_key;
35582   gsize rpm_size;
35583   gchar *rpm64;
35584 
35585   if (iterator->done) return NULL;
35586 
35587   private_key = credential_iterator_private_key (iterator);
35588   pass = credential_iterator_password (iterator);
35589   public_key = gvm_ssh_public_from_private (private_key, pass);
35590   if (!public_key)
35591     return NULL;
35592   login = credential_iterator_login (iterator);
35593   if (credential_iterator_format_available
35594           (iterator, CREDENTIAL_FORMAT_RPM) == FALSE)
35595     {
35596       g_free (public_key);
35597       return NULL;
35598     }
35599   else if (lsc_user_rpm_recreate (login, public_key, &rpm, &rpm_size))
35600     {
35601       g_warning ("%s: Failed to create RPM", __func__);
35602       g_free (public_key);
35603       return NULL;
35604     }
35605   g_free (public_key);
35606   rpm64 = (rpm && rpm_size)
35607           ? g_base64_encode (rpm, rpm_size)
35608           : g_strdup ("");
35609   free (rpm);
35610   return rpm64;
35611 }
35612 
35613 /**
35614  * @brief Get the deb from a Credential iterator.
35615  *
35616  * @param[in]  iterator  Iterator.
35617  *
35618  * @return Deb, or NULL if iteration is complete. Free with g_free().
35619  */
35620 char*
credential_iterator_deb(iterator_t * iterator)35621 credential_iterator_deb (iterator_t *iterator)
35622 {
35623   const char *login, *private_key, *pass;
35624   char *public_key, *maintainer;
35625   void *deb;
35626   gsize deb_size;
35627   gchar *deb64;
35628 
35629   if (iterator->done) return NULL;
35630 
35631   maintainer = NULL;
35632   setting_value (SETTING_UUID_LSC_DEB_MAINTAINER, &maintainer);
35633   private_key = credential_iterator_private_key (iterator);
35634   pass = credential_iterator_password (iterator);
35635   public_key = gvm_ssh_public_from_private (private_key, pass);
35636   if (!public_key)
35637     return NULL;
35638   login = credential_iterator_login (iterator);
35639   if (credential_iterator_format_available
35640           (iterator, CREDENTIAL_FORMAT_DEB) == FALSE)
35641     {
35642       g_free (public_key);
35643       free (maintainer);
35644       return NULL;
35645     }
35646   else if (lsc_user_deb_recreate (login, public_key,
35647                                   maintainer ? maintainer : "",
35648                                   &deb, &deb_size))
35649     {
35650       g_warning ("%s: Failed to create DEB", __func__);
35651       g_free (public_key);
35652       free (maintainer);
35653       return NULL;
35654     }
35655   g_free (public_key);
35656   free (maintainer);
35657 
35658   deb64 = (deb && deb_size)
35659           ? g_base64_encode (deb, deb_size)
35660           : g_strdup ("");
35661   free (deb);
35662   return deb64;
35663 }
35664 
35665 /**
35666  * @brief Get the exe from a Credential iterator.
35667  *
35668  * @param[in]  iterator  Iterator.
35669  *
35670  * @return Exe, or NULL if iteration is complete. Free with g_free().
35671  */
35672 char*
credential_iterator_exe(iterator_t * iterator)35673 credential_iterator_exe (iterator_t *iterator)
35674 {
35675   const char *login, *password;
35676   void *exe;
35677   gsize exe_size;
35678   gchar *exe64;
35679 
35680   if (iterator->done) return NULL;
35681 
35682   login = credential_iterator_login (iterator);
35683   password = credential_iterator_password (iterator);
35684   if (credential_iterator_format_available
35685           (iterator, CREDENTIAL_FORMAT_EXE) == FALSE)
35686     return NULL;
35687   else if (lsc_user_exe_recreate (login, password, &exe, &exe_size))
35688     {
35689       g_warning ("%s: Failed to create EXE", __func__);
35690       return NULL;
35691     }
35692   exe64 = (exe && exe_size)
35693           ? g_base64_encode (exe, exe_size)
35694           : g_strdup ("");
35695   free (exe);
35696   return exe64;
35697 }
35698 
35699 /**
35700  * @brief  Test if a credential format is available for an iterator.
35701  *
35702  * @param[in]  iterator  Iterator.
35703  * @param[in]  format    The format to test availability of.
35704  *
35705  * @return  Whether format is available for the current credential of iterator.
35706  */
35707 gboolean
credential_iterator_format_available(iterator_t * iterator,credential_format_t format)35708 credential_iterator_format_available (iterator_t* iterator,
35709                                       credential_format_t format)
35710 {
35711   const char *type, *login, *private_key;
35712 
35713   type = credential_iterator_type (iterator);
35714   login = credential_iterator_login (iterator);
35715   private_key = credential_iterator_private_key (iterator);
35716 
35717   switch (format)
35718     {
35719       case CREDENTIAL_FORMAT_NONE:
35720         return TRUE;
35721       case CREDENTIAL_FORMAT_KEY:
35722       case CREDENTIAL_FORMAT_RPM:
35723       case CREDENTIAL_FORMAT_DEB:
35724         if (strcasecmp (type, "usk") == 0 && private_key)
35725           return validate_credential_username_for_format (login, format);
35726         else
35727           return FALSE;
35728       case CREDENTIAL_FORMAT_EXE:
35729         if (strcasecmp (type, "up") == 0)
35730           return validate_credential_username_for_format (login, format);
35731         else
35732           return FALSE;
35733       case CREDENTIAL_FORMAT_PEM:
35734         if (strcasecmp (type, "cc") == 0)
35735           return validate_credential_username_for_format (login, format);
35736         else
35737           return FALSE;
35738       case CREDENTIAL_FORMAT_ERROR:
35739         return FALSE;
35740     }
35741   return FALSE;
35742 }
35743 
35744 /**
35745  * @brief  Get XML of available formats for a credential iterator.
35746  *
35747  * @param[in]  iterator  Iterator.
35748  *
35749  * @return  Newly allocated XML string.
35750  */
35751 gchar *
credential_iterator_formats_xml(iterator_t * iterator)35752 credential_iterator_formats_xml (iterator_t* iterator)
35753 {
35754   GString *xml;
35755 
35756   xml = g_string_new ("<formats>");
35757 
35758   if (credential_iterator_format_available (iterator,
35759                                             CREDENTIAL_FORMAT_KEY))
35760     g_string_append (xml, "<format>key</format>");
35761 
35762   if (credential_iterator_format_available (iterator,
35763                                             CREDENTIAL_FORMAT_RPM))
35764     g_string_append (xml, "<format>rpm</format>");
35765 
35766   if (credential_iterator_format_available (iterator,
35767                                             CREDENTIAL_FORMAT_DEB))
35768     g_string_append (xml, "<format>deb</format>");
35769 
35770   if (credential_iterator_format_available (iterator,
35771                                             CREDENTIAL_FORMAT_EXE))
35772     g_string_append (xml, "<format>exe</format>");
35773 
35774   if (credential_iterator_format_available (iterator,
35775                                             CREDENTIAL_FORMAT_PEM))
35776     g_string_append (xml, "<format>pem</format>");
35777 
35778   g_string_append (xml, "</formats>");
35779 
35780   return g_string_free (xml, FALSE);
35781 }
35782 
35783 /**
35784  * @brief Get the UUID of a Credential.
35785  *
35786  * @param[in]  credential  Credential.
35787  *
35788  * @return UUID.
35789  */
35790 char*
credential_uuid(credential_t credential)35791 credential_uuid (credential_t credential)
35792 {
35793   return sql_string ("SELECT uuid FROM credentials WHERE id = %llu;",
35794                      credential);
35795 }
35796 
35797 /**
35798  * @brief Get the UUID of a Credential in the trashcan.
35799  *
35800  * @param[in]  credential  Credential.
35801  *
35802  * @return UUID.
35803  */
35804 char*
trash_credential_uuid(credential_t credential)35805 trash_credential_uuid (credential_t credential)
35806 {
35807   return sql_string ("SELECT uuid FROM credentials_trash"
35808                      " WHERE id = %llu;",
35809                      credential);
35810 }
35811 
35812 /**
35813  * @brief Get the name of an LSC credential.
35814  *
35815  * @param[in]  credential  Credential.
35816  *
35817  * @return Name.
35818  */
35819 char*
credential_name(credential_t credential)35820 credential_name (credential_t credential)
35821 {
35822   return sql_string ("SELECT name FROM credentials WHERE id = %llu;",
35823                      credential);
35824 }
35825 
35826 /**
35827  * @brief Get the name of an LSC credential in the trashcan.
35828  *
35829  * @param[in]  credential  Credential.
35830  *
35831  * @return Name.
35832  */
35833 char*
trash_credential_name(credential_t credential)35834 trash_credential_name (credential_t credential)
35835 {
35836   return sql_string ("SELECT name FROM credentials_trash"
35837                      " WHERE id = %llu;",
35838                      credential);
35839 }
35840 
35841 /**
35842  * @brief Get the type of a Credential.
35843  *
35844  * @param[in]  credential  Credential.
35845  *
35846  * @return Credential type.
35847  */
35848 char*
credential_type(credential_t credential)35849 credential_type (credential_t credential)
35850 {
35851   return sql_string ("SELECT type FROM credentials WHERE id = %llu;",
35852                      credential);
35853 }
35854 
35855 /**
35856  * @brief Return whether a trashcan credential is readable.
35857  *
35858  * @param[in]  credential  Credential.
35859  *
35860  * @return 1 if readable, else 0.
35861  */
35862 int
trash_credential_readable(credential_t credential)35863 trash_credential_readable (credential_t credential)
35864 {
35865   char *uuid;
35866   credential_t found = 0;
35867 
35868   if (credential == 0)
35869     return 0;
35870   uuid = credential_uuid (credential);
35871   if (find_trash ("credential", uuid, &found))
35872     {
35873       g_free (uuid);
35874       return 0;
35875     }
35876   g_free (uuid);
35877   return found > 0;
35878 }
35879 
35880 /**
35881  * @brief Initialise a Credential target iterator.
35882  *
35883  * Iterates over all targets that use the credential.
35884  *
35885  * @param[in]  iterator        Iterator.
35886  * @param[in]  credential      Name of credential.
35887  * @param[in]  ascending       Whether to sort ascending or descending.
35888  */
35889 void
init_credential_target_iterator(iterator_t * iterator,credential_t credential,int ascending)35890 init_credential_target_iterator (iterator_t* iterator,
35891                                  credential_t credential,
35892                                  int ascending)
35893 {
35894   gchar *available, *with_clause;
35895   get_data_t get;
35896   array_t *permissions;
35897 
35898   assert (credential);
35899 
35900   get.trash = 0;
35901   permissions = make_array ();
35902   array_add (permissions, g_strdup ("get_targets"));
35903   available = acl_where_owned ("target", &get, 1, "any", 0, permissions, 0,
35904                                &with_clause);
35905   array_free (permissions);
35906 
35907   init_iterator (iterator,
35908                  "%s"
35909                  " SELECT uuid, name, %s FROM targets"
35910                  " WHERE id IN"
35911                  "   (SELECT target FROM targets_login_data"
35912                  "    WHERE credential = %llu)"
35913                  " ORDER BY name %s;",
35914                  with_clause ? with_clause : "",
35915                  available,
35916                  credential,
35917                  ascending ? "ASC" : "DESC");
35918 
35919   g_free (with_clause);
35920   g_free (available);
35921 }
35922 
35923 /**
35924  * @brief Get the uuid from an Credential Target iterator.
35925  *
35926  * @param[in]  iterator  Iterator.
35927  *
35928  * @return Uuid, or NULL if iteration is complete.  Freed by
35929  *         cleanup_iterator.
35930  */
35931 DEF_ACCESS (credential_target_iterator_uuid, 0);
35932 
35933 /**
35934  * @brief Get the name from an Credential Target iterator.
35935  *
35936  * @param[in]  iterator  Iterator.
35937  *
35938  * @return Name, or NULL if iteration is complete.  Freed by
35939  *         cleanup_iterator.
35940  */
35941 DEF_ACCESS (credential_target_iterator_name, 1);
35942 
35943 /**
35944  * @brief Get the read permission status from a GET iterator.
35945  *
35946  * @param[in]  iterator  Iterator.
35947  *
35948  * @return 1 if may read, else 0.
35949  */
35950 int
credential_target_iterator_readable(iterator_t * iterator)35951 credential_target_iterator_readable (iterator_t* iterator)
35952 {
35953   if (iterator->done) return 0;
35954   return iterator_int (iterator, 2);
35955 }
35956 
35957 /**
35958  * @brief Initialise a Credential scanner iterator.
35959  *
35960  * Iterates over all scanners that use the credential.
35961  *
35962  * @param[in]  iterator        Iterator.
35963  * @param[in]  credential      Name of credential.
35964  * @param[in]  ascending       Whether to sort ascending or descending.
35965  */
35966 void
init_credential_scanner_iterator(iterator_t * iterator,credential_t credential,int ascending)35967 init_credential_scanner_iterator (iterator_t* iterator,
35968                                   credential_t credential,
35969                                   int ascending)
35970 {
35971   gchar *available, *with_clause;
35972   get_data_t get;
35973   array_t *permissions;
35974 
35975   assert (credential);
35976 
35977   get.trash = 0;
35978   permissions = make_array ();
35979   array_add (permissions, g_strdup ("get_scanners"));
35980   available = acl_where_owned ("scanner", &get, 1, "any", 0, permissions, 0,
35981                                &with_clause);
35982   array_free (permissions);
35983 
35984   init_iterator (iterator,
35985                  "%s"
35986                  " SELECT uuid, name, %s FROM scanners"
35987                  " WHERE credential = %llu"
35988                  " ORDER BY name %s;",
35989                  with_clause ? with_clause : "",
35990                  available,
35991                  credential,
35992                  ascending ? "ASC" : "DESC");
35993 
35994   g_free (with_clause);
35995   g_free (available);
35996 }
35997 
35998 /**
35999  * @brief Get the uuid from an Credential Scanner iterator.
36000  *
36001  * @param[in]  iterator  Iterator.
36002  *
36003  * @return Uuid, or NULL if iteration is complete.  Freed by
36004  *         cleanup_iterator.
36005  */
36006 DEF_ACCESS (credential_scanner_iterator_uuid, 0);
36007 
36008 /**
36009  * @brief Get the name from an Credential Scanner iterator.
36010  *
36011  * @param[in]  iterator  Iterator.
36012  *
36013  * @return Name, or NULL if iteration is complete.  Freed by
36014  *         cleanup_iterator.
36015  */
36016 DEF_ACCESS (credential_scanner_iterator_name, 1);
36017 
36018 /**
36019  * @brief Get the read permission status from a Credential Scanner iterator.
36020  *
36021  * @param[in]  iterator  Iterator.
36022  *
36023  * @return 1 if may read, else 0.
36024  */
36025 int
credential_scanner_iterator_readable(iterator_t * iterator)36026 credential_scanner_iterator_readable (iterator_t* iterator)
36027 {
36028   if (iterator->done) return 0;
36029   return iterator_int (iterator, 2);
36030 }
36031 
36032 
36033 /* Notes. */
36034 
36035 /**
36036  * @brief Find a note for a specific permission, given a UUID.
36037  *
36038  * @param[in]   uuid        UUID of note.
36039  * @param[out]  note        Note return, 0 if successfully failed to find note.
36040  * @param[in]   permission  Permission.
36041  *
36042  * @return FALSE on success (including if failed to find note), TRUE on error.
36043  */
36044 gboolean
find_note_with_permission(const char * uuid,note_t * note,const char * permission)36045 find_note_with_permission (const char* uuid, note_t* note,
36046                            const char *permission)
36047 {
36048   return find_resource_with_permission ("note", uuid, note, permission, 0);
36049 }
36050 
36051 /**
36052  * @brief Check if an NVT exists.
36053  *
36054  * @param[in]  nvt  NVT OID.
36055  *
36056  * @return 1 if exists, else 0.
36057  */
36058 static gboolean
nvt_exists(const char * nvt)36059 nvt_exists (const char* nvt)
36060 {
36061   gchar *quoted_nvt;
36062 
36063   quoted_nvt = sql_quote (nvt);
36064   if (g_str_has_prefix (nvt, "CVE-"))
36065     {
36066       if (sql_int ("SELECT count (*) FROM cves WHERE uuid = '%s'", quoted_nvt)
36067           != 0)
36068         {
36069           g_free (quoted_nvt);
36070           return 1;
36071         }
36072     }
36073   else if (strcmp (nvt, "0")
36074            && (sql_int ("SELECT count (*) FROM nvts WHERE oid = '%s'", quoted_nvt)
36075                != 0))
36076     {
36077       g_free (quoted_nvt);
36078       return 1;
36079     }
36080   g_free (quoted_nvt);
36081   return 0;
36082 }
36083 
36084 /**
36085  * @brief Create a note.
36086  *
36087  * @param[in]  active      NULL or -1 on, 0 off, n on for n days.
36088  * @param[in]  nvt         OID of noted NVT.
36089  * @param[in]  text        Note text.
36090  * @param[in]  hosts       Hosts to apply note to, NULL for any host.
36091  * @param[in]  port        Port to apply note to, NULL for any port.
36092  * @param[in]  severity    Severity to apply note to, "" or NULL for any.
36093  * @param[in]  threat      Threat to apply note to, "" or NULL for any threat.
36094  *                         Only used if severity is "" or NULL.
36095  * @param[in]  task        Task to apply note to, 0 for any task.
36096  * @param[in]  result      Result to apply note to, 0 for any result.
36097  * @param[out] note        Created note.
36098  *
36099  * @return 0 success, 1 failed to find NVT, 2 invalid port, 99 permission
36100  *         denied, -1 error.
36101  */
36102 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)36103 create_note (const char* active, const char* nvt, const char* text,
36104              const char* hosts, const char* port, const char* severity,
36105              const char* threat, task_t task, result_t result, note_t *note)
36106 {
36107   gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
36108   double severity_dbl;
36109 
36110   if (acl_user_may ("create_note") == 0)
36111     return 99;
36112 
36113   if (nvt == NULL)
36114     return -1;
36115 
36116   if (!nvt_exists (nvt))
36117     return 1;
36118 
36119   if (port && validate_results_port (port))
36120     return 2;
36121 
36122   if (text == NULL)
36123     return -1;
36124 
36125   if (threat && strcmp (threat, "High") && strcmp (threat, "Medium")
36126       && strcmp (threat, "Low") && strcmp (threat, "Log")
36127       && strcmp (threat, ""))
36128     return -1;
36129 
36130   quoted_text = sql_insert (text);
36131   quoted_hosts = sql_insert (hosts);
36132   quoted_port = sql_insert (port);
36133 
36134   severity_dbl = 0.0;
36135   if (severity != NULL && strcmp (severity, ""))
36136     {
36137       if (sscanf (severity, "%lf", &severity_dbl) != 1
36138           || ((severity_dbl < 0.0 || severity_dbl > 10.0)
36139               && severity_dbl != SEVERITY_LOG))
36140         return 3;
36141       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
36142     }
36143   else if (threat != NULL && strcmp (threat, ""))
36144     {
36145       if (strcmp (threat, "Alarm") == 0)
36146         severity_dbl = 0.1;
36147       else if (strcmp (threat, "High") == 0)
36148         severity_dbl = 0.1;
36149       else if (strcmp (threat, "Medium") == 0)
36150         severity_dbl = 0.1;
36151       else if (strcmp (threat, "Low") == 0)
36152         severity_dbl = 0.1;
36153       else if (strcmp (threat, "Log") == 0)
36154         severity_dbl = SEVERITY_LOG;
36155       else
36156         return -1;
36157 
36158       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
36159     }
36160   else
36161     quoted_severity = g_strdup ("NULL");
36162 
36163   sql ("INSERT INTO notes"
36164        " (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
36165        "  port, severity, task, result, end_time)"
36166        " VALUES"
36167        " (make_uuid (), (SELECT id FROM users WHERE users.uuid = '%s'),"
36168        "  '%s', %i, %i, %s, %s, %s, %s, %llu, %llu, %i);",
36169        current_credentials.uuid,
36170        nvt,
36171        time (NULL),
36172        time (NULL),
36173        quoted_text,
36174        quoted_hosts,
36175        quoted_port,
36176        quoted_severity,
36177        task,
36178        result,
36179        (active == NULL || (strcmp (active, "-1") == 0))
36180          ? 0
36181          : (strcmp (active, "0")
36182              ? (time (NULL) + (atoi (active) * 60 * 60 * 24))
36183              : 1));
36184 
36185   g_free (quoted_text);
36186   g_free (quoted_hosts);
36187   g_free (quoted_port);
36188   g_free (quoted_severity);
36189 
36190   if (note)
36191     *note = sql_last_insert_id ();
36192 
36193   return 0;
36194 }
36195 
36196 /**
36197  * @brief Create a note from an existing note.
36198  *
36199  * @param[in]  note_id   UUID of existing note.
36200  * @param[out] new_note  New note.
36201  *
36202  * @return 0 success, 1 note exists already, 2 failed to find existing
36203  *         note, -1 error.
36204  */
36205 int
copy_note(const char * note_id,note_t * new_note)36206 copy_note (const char *note_id, note_t* new_note)
36207 {
36208   return copy_resource ("note", NULL, NULL, note_id,
36209                         "nvt, text, hosts, port, severity, task, result,"
36210                         "end_time",
36211                         1, new_note, NULL);
36212 }
36213 
36214 /**
36215  * @brief Delete a note.
36216  *
36217  * @param[in]  note_id    UUID of note.
36218  * @param[in]  ultimate   Whether to remove entirely, or to trashcan.
36219  *
36220  * @return 0 success, 2 failed to find note, 99 permission denied, -1 error.
36221  */
36222 int
delete_note(const char * note_id,int ultimate)36223 delete_note (const char *note_id, int ultimate)
36224 {
36225   note_t note = 0;
36226 
36227   sql_begin_immediate ();
36228 
36229   if (acl_user_may ("delete_note") == 0)
36230     {
36231       sql_rollback ();
36232       return 99;
36233     }
36234 
36235   if (find_note_with_permission (note_id, &note, "delete_note"))
36236     {
36237       sql_rollback ();
36238       return -1;
36239     }
36240 
36241   if (note == 0)
36242     {
36243       if (find_trash ("note", note_id, &note))
36244         {
36245           sql_rollback ();
36246           return -1;
36247         }
36248       if (note == 0)
36249         {
36250           sql_rollback ();
36251           return 2;
36252         }
36253       if (ultimate == 0)
36254         {
36255           /* It's already in the trashcan. */
36256           sql_commit ();
36257           return 0;
36258         }
36259 
36260       permissions_set_orphans ("note", note, LOCATION_TRASH);
36261       tags_remove_resource ("note", note, LOCATION_TRASH);
36262 
36263       sql ("DELETE FROM notes_trash WHERE id = %llu;", note);
36264       sql_commit ();
36265       return 0;
36266     }
36267 
36268   if (ultimate == 0)
36269     {
36270       sql ("INSERT INTO notes_trash"
36271            " (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
36272            "  port, severity, task, result, end_time)"
36273            " SELECT uuid, owner, nvt, creation_time, modification_time, text,"
36274            "        hosts, port, severity, task, result, end_time"
36275            " FROM notes WHERE id = %llu;",
36276            note);
36277 
36278       permissions_set_locations ("note", note,
36279                                  sql_last_insert_id (),
36280                                  LOCATION_TRASH);
36281       tags_set_locations ("note", note,
36282                           sql_last_insert_id (),
36283                           LOCATION_TRASH);
36284     }
36285   else
36286     {
36287       permissions_set_orphans ("note", note, LOCATION_TABLE);
36288       tags_remove_resource ("note", note, LOCATION_TABLE);
36289     }
36290 
36291   sql ("DELETE FROM notes WHERE id = %llu;", note);
36292 
36293   sql_commit ();
36294   return 0;
36295 }
36296 
36297 /**
36298  * @brief Return the UUID of a note.
36299  *
36300  * @param[in]   note  Note.
36301  * @param[out]  id    Pointer to a newly allocated string.
36302  *
36303  * @return 0.
36304  */
36305 int
note_uuid(note_t note,char ** id)36306 note_uuid (note_t note, char ** id)
36307 {
36308   *id = sql_string ("SELECT uuid FROM notes WHERE id = %llu;",
36309                     note);
36310   return 0;
36311 }
36312 
36313 /**
36314  * @brief Modify a note.
36315  *
36316  * @param[in]  note_id     Note.
36317  * @param[in]  active      NULL or -2 leave as is, -1 on, 0 off, n on for n
36318  *                         days.
36319  * @param[in]  nvt         OID of noted NVT.
36320  * @param[in]  text        Note text.
36321  * @param[in]  hosts       Hosts to apply note to, NULL for any host.
36322  * @param[in]  port        Port to apply note to, NULL for any port.
36323  * @param[in]  severity    Severity to apply note to, "" or NULL for any.
36324  * @param[in]  threat      Threat to apply note to, "" or NULL for any threat.
36325  *                         Only used if severity is "" or NULL.
36326  * @param[in]  task_id     Task to apply note to, NULL for any task.
36327  * @param[in]  result_id   Result to apply note to, 0 for any result.
36328  *
36329  * @return 0 success, -1 error, 1 syntax error in active, 2 invalid port,
36330  *         3 invalid severity, 4 failed to find NVT, 5 failed to find note,
36331  *         6 failed to find task, 7 failed to find result.
36332  */
36333 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)36334 modify_note (const gchar *note_id, const char *active, const char *nvt,
36335              const char *text, const char *hosts, const char *port,
36336              const char *severity, const char *threat, const gchar *task_id,
36337              const gchar *result_id)
36338 {
36339   gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
36340   double severity_dbl;
36341   gchar *quoted_nvt;
36342   note_t note;
36343   task_t task;
36344   result_t result;
36345 
36346   note = 0;
36347   if (find_note_with_permission (note_id, &note, "modify_note"))
36348     return -1;
36349   else if (note == 0)
36350     return 5;
36351 
36352   task = 0;
36353   if (task_id)
36354     {
36355       if (find_task_with_permission (task_id, &task, NULL))
36356         return -1;
36357       if (task == 0)
36358         {
36359           if (find_trash_task_with_permission (task_id, &task, NULL))
36360             return -1;
36361           if (task == 0)
36362             return 6;
36363         }
36364     }
36365 
36366   result = 0;
36367   if (result_id)
36368     {
36369       if (find_result_with_permission (result_id, &result, NULL))
36370         return -1;
36371       if (result == 0)
36372         return 7;
36373     }
36374 
36375   if (text == NULL)
36376     return -1;
36377 
36378   if (nvt && !nvt_exists (nvt))
36379     return 4;
36380 
36381   if (threat && strcmp (threat, "High") && strcmp (threat, "Medium")
36382       && strcmp (threat, "Low") && strcmp (threat, "Log")
36383       && strcmp (threat, "Alarm") && strcmp (threat, ""))
36384     return -1;
36385 
36386   if (port && validate_results_port (port))
36387     return 2;
36388 
36389   quoted_text = sql_insert (text);
36390   quoted_hosts = sql_insert (hosts);
36391   quoted_port = sql_insert (port);
36392   quoted_nvt = sql_insert (nvt);
36393 
36394   severity_dbl = 0.0;
36395   if (severity != NULL && strcmp (severity, ""))
36396     {
36397       if (sscanf (severity, "%lf", &severity_dbl) != 1
36398           || ((severity_dbl < 0.0 || severity_dbl > 10.0)
36399               && severity_dbl != SEVERITY_LOG))
36400         return 3;
36401       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
36402     }
36403   else if (threat != NULL && strcmp (threat, ""))
36404     {
36405       if (strcmp (threat, "Alarm") == 0)
36406         severity_dbl = 0.1;
36407       else if (strcmp (threat, "High") == 0)
36408         severity_dbl = 0.1;
36409       else if (strcmp (threat, "Medium") == 0)
36410         severity_dbl = 0.1;
36411       else if (strcmp (threat, "Low") == 0)
36412         severity_dbl = 0.1;
36413       else if (strcmp (threat, "Log") == 0)
36414         severity_dbl = SEVERITY_LOG;
36415       else
36416         return -1;
36417 
36418       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
36419     }
36420   else
36421     quoted_severity = g_strdup ("NULL");
36422 
36423   if ((active == NULL) || (strcmp (active, "-2") == 0))
36424     sql ("UPDATE notes SET"
36425          " modification_time = %i,"
36426          " text = %s,"
36427          " hosts = %s,"
36428          " port = %s,"
36429          " severity = %s,"
36430          " %s%s%s"
36431          " task = %llu,"
36432          " result = %llu"
36433          " WHERE id = %llu;",
36434          time (NULL),
36435          quoted_text,
36436          quoted_hosts,
36437          quoted_port,
36438          quoted_severity,
36439          nvt ? "nvt = " : "",
36440          nvt ? quoted_nvt : "",
36441          nvt ? "," : "",
36442          task,
36443          result,
36444          note);
36445   else
36446     {
36447       const char *point;
36448       point = active;
36449       if (strcmp (point, "-1"))
36450         {
36451           while (*point && isdigit (*point)) point++;
36452           if (*point)
36453             return 1;
36454         }
36455       sql ("UPDATE notes SET"
36456            " end_time = %i,"
36457            " modification_time = %i,"
36458            " text = %s,"
36459            " hosts = %s,"
36460            " port = %s,"
36461            " severity = %s,"
36462            "%s%s%s"
36463            " task = %llu,"
36464            " result = %llu"
36465            " WHERE id = %llu;",
36466            (strcmp (active, "-1")
36467              ? (strcmp (active, "0")
36468                  ? (time (NULL) + atoi (active) * 60 * 60 * 24)
36469                  : 1)
36470              : 0),
36471            time (NULL),
36472            quoted_text,
36473            quoted_hosts,
36474            quoted_port,
36475            quoted_severity,
36476            nvt ? "nvt = " : "",
36477            nvt ? quoted_nvt : "",
36478            nvt ? "," : "",
36479            task,
36480            result,
36481            note);
36482     }
36483 
36484   g_free (quoted_text);
36485   g_free (quoted_hosts);
36486   g_free (quoted_port);
36487   g_free (quoted_severity);
36488   g_free (quoted_nvt);
36489 
36490   return 0;
36491 }
36492 
36493 /**
36494  * @brief Filter columns for note iterator.
36495  */
36496 #define NOTE_ITERATOR_FILTER_COLUMNS                                          \
36497  { ANON_GET_ITERATOR_FILTER_COLUMNS, "name", "nvt", "text", "nvt_id",         \
36498    "task_name", "task_id", "hosts", "port", "active", "result", "severity",   \
36499    "end_time", "active_days", NULL }
36500 
36501 /**
36502  * @brief Note iterator columns.
36503  */
36504 #define NOTE_ITERATOR_COLUMNS                                              \
36505  {                                                                         \
36506    { "notes.id", "id", KEYWORD_TYPE_INTEGER },                             \
36507    { "notes.uuid", "uuid", KEYWORD_TYPE_STRING },                          \
36508    {                                                                       \
36509      "(CASE"                                                               \
36510      " WHEN notes.nvt LIKE 'CVE-%%'"                                       \
36511      " THEN notes.nvt"                                                     \
36512      " ELSE (SELECT name FROM nvts WHERE oid = notes.nvt)"                 \
36513      " END)",                                                              \
36514      "name",                                                               \
36515      KEYWORD_TYPE_STRING                                                   \
36516    },                                                                      \
36517    { "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING },                     \
36518    { "iso_time (notes.creation_time)", NULL, KEYWORD_TYPE_STRING },        \
36519    { "iso_time (notes.modification_time)", NULL, KEYWORD_TYPE_STRING },    \
36520    { "notes.creation_time", "created", KEYWORD_TYPE_INTEGER },             \
36521    { "notes.modification_time", "modified", KEYWORD_TYPE_INTEGER },        \
36522    { "(SELECT name FROM users WHERE users.id = notes.owner)",              \
36523      "_owner",                                                             \
36524      KEYWORD_TYPE_STRING },                                                \
36525    { "owner", NULL, KEYWORD_TYPE_INTEGER },                                \
36526    /* Columns specific to notes. */                                        \
36527    { "notes.nvt", "oid", KEYWORD_TYPE_STRING },                            \
36528    { "notes.text", "text", KEYWORD_TYPE_STRING },                          \
36529    { "notes.hosts", "hosts", KEYWORD_TYPE_STRING },                        \
36530    { "notes.port", "port", KEYWORD_TYPE_STRING },                          \
36531    { "notes.task", NULL, KEYWORD_TYPE_INTEGER },                           \
36532    { "notes.result", "result", KEYWORD_TYPE_INTEGER },                     \
36533    { "notes.end_time", "end_time", KEYWORD_TYPE_INTEGER },                 \
36534    { "CAST (((notes.end_time = 0) OR (notes.end_time >= m_now ()))"        \
36535      "      AS INTEGER)",                                                  \
36536      "active",                                                             \
36537      KEYWORD_TYPE_INTEGER },                                               \
36538    {                                                                       \
36539      "(CASE"                                                               \
36540      " WHEN notes.nvt LIKE 'CVE-%%'"                                       \
36541      " THEN notes.nvt"                                                     \
36542      " ELSE (SELECT name FROM nvts WHERE oid = notes.nvt)"                 \
36543      " END)",                                                              \
36544      "nvt",                                                                \
36545      KEYWORD_TYPE_STRING                                                   \
36546    },                                                                      \
36547    { "notes.nvt", "nvt_id", KEYWORD_TYPE_STRING },                         \
36548    { "(SELECT uuid FROM tasks WHERE id = notes.task)",                     \
36549      "task_id",                                                            \
36550      KEYWORD_TYPE_STRING },                                                \
36551    { "(SELECT name FROM tasks WHERE id = notes.task)",                     \
36552      "task_name",                                                          \
36553      KEYWORD_TYPE_STRING },                                                \
36554    { "notes.severity", "severity", KEYWORD_TYPE_DOUBLE },                  \
36555    { "(SELECT name FROM users WHERE users.id = notes.owner)",              \
36556      "_owner",                                                             \
36557      KEYWORD_TYPE_STRING },                                                \
36558    { "days_from_now (notes.end_time)",                                     \
36559      "active_days",                                                        \
36560      KEYWORD_TYPE_INTEGER },                                               \
36561    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                    \
36562  }
36563 
36564 /**
36565  * @brief Note iterator columns for trash case.
36566  */
36567 #define NOTE_ITERATOR_TRASH_COLUMNS                                              \
36568  {                                                                               \
36569    { "notes_trash.id", "id", KEYWORD_TYPE_INTEGER },                             \
36570    { "notes_trash.uuid", "uuid", KEYWORD_TYPE_STRING },                          \
36571    { "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING },                           \
36572    { "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING },                           \
36573    { "iso_time (notes_trash.creation_time)", NULL, KEYWORD_TYPE_STRING },        \
36574    { "iso_time (notes_trash.modification_time)", NULL, KEYWORD_TYPE_STRING },    \
36575    { "notes_trash.creation_time", "created", KEYWORD_TYPE_INTEGER },             \
36576    { "notes_trash.modification_time", "modified", KEYWORD_TYPE_INTEGER },        \
36577    { "(SELECT name FROM users WHERE users.id = notes_trash.owner)",              \
36578      "_owner",                                                                   \
36579      KEYWORD_TYPE_STRING },                                                      \
36580    { "owner", NULL, KEYWORD_TYPE_INTEGER },                                      \
36581    /* Columns specific to notes_trash. */                                        \
36582    { "notes_trash.nvt", "oid", KEYWORD_TYPE_STRING },                            \
36583    { "notes_trash.text", "text", KEYWORD_TYPE_STRING  },                         \
36584    { "notes_trash.hosts", "hosts", KEYWORD_TYPE_STRING },                        \
36585    { "notes_trash.port", "port", KEYWORD_TYPE_STRING },                          \
36586    { "severity_to_level (notes_trash.severity, 1)",                              \
36587      "threat",                                                                   \
36588      KEYWORD_TYPE_STRING },                                                      \
36589    { "notes_trash.task", NULL, KEYWORD_TYPE_INTEGER },                           \
36590    { "notes_trash.result", "result", KEYWORD_TYPE_INTEGER },                     \
36591    { "notes_trash.end_time", NULL, KEYWORD_TYPE_INTEGER },                       \
36592    { "CAST (((notes_trash.end_time = 0) OR (notes_trash.end_time >= m_now ()))"  \
36593      "      AS INTEGER)",                                                        \
36594      "active",                                                                   \
36595      KEYWORD_TYPE_INTEGER },                                                     \
36596    {                                                                             \
36597      "(CASE"                                                                     \
36598      " WHEN notes_trash.nvt LIKE 'CVE-%%'"                                       \
36599      " THEN notes_trash.nvt"                                                     \
36600      " ELSE (SELECT name FROM nvts WHERE oid = notes_trash.nvt)"                 \
36601      " END)",                                                                    \
36602      "nvt",                                                                      \
36603      KEYWORD_TYPE_STRING                                                         \
36604    },                                                                            \
36605    { "notes_trash.nvt", "nvt_id", KEYWORD_TYPE_STRING },                         \
36606    { "(SELECT uuid FROM tasks WHERE id = notes_trash.task)",                     \
36607      "task_id",                                                                  \
36608      KEYWORD_TYPE_STRING },                                                      \
36609    { "(SELECT name FROM tasks WHERE id = notes_trash.task)",                     \
36610      "task_name",                                                                \
36611      KEYWORD_TYPE_STRING },                                                      \
36612    { "notes_trash.severity", "severity", KEYWORD_TYPE_DOUBLE },                  \
36613    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                          \
36614  }
36615 
36616 /**
36617  * @brief Count number of notes.
36618  *
36619  * @param[in]  get         GET params.
36620  * @param[in]  result      Result to limit notes to, 0 for all.
36621  * @param[in]  task        If result is > 0, task whose notes on result to
36622  *                         include, otherwise task to limit notes to.  0 for
36623  *                         all tasks.
36624  * @param[in]  nvt         NVT to limit notes to, 0 for all.
36625  *
36626  * @return Total number of notes in filtered set.
36627  */
36628 int
note_count(const get_data_t * get,nvt_t nvt,result_t result,task_t task)36629 note_count (const get_data_t *get, nvt_t nvt, result_t result, task_t task)
36630 {
36631   static const char *filter_columns[] = NOTE_ITERATOR_FILTER_COLUMNS;
36632   static column_t columns[] = NOTE_ITERATOR_COLUMNS;
36633   static column_t trash_columns[] = NOTE_ITERATOR_TRASH_COLUMNS;
36634   gchar *result_clause, *filter, *task_id;
36635   int ret;
36636 
36637   /* Treat the "task_id" filter keyword as if the task was given in "task". */
36638 
36639   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
36640     {
36641       filter = filter_term (get->filt_id);
36642       if (filter == NULL)
36643         return 2;
36644     }
36645   else
36646     filter = NULL;
36647 
36648   task_id = filter_term_value (filter ? filter : get->filter, "task_id");
36649 
36650   g_free (filter);
36651 
36652   if (task_id)
36653     {
36654       find_task_with_permission (task_id, &task, "get_tasks");
36655       g_free (task_id);
36656     }
36657 
36658   if (result)
36659     {
36660       gchar *severity_sql;
36661 
36662       if (setting_dynamic_severity_int ())
36663         severity_sql = g_strdup_printf ("(SELECT CASE"
36664                                         " WHEN results.severity"
36665                                         "      > " G_STRINGIFY (SEVERITY_LOG)
36666                                         " THEN CAST (nvts.cvss_base AS real)"
36667                                         " ELSE results.severity END"
36668                                         " FROM results, nvts"
36669                                         " WHERE (nvts.oid = results.nvt)"
36670                                         "   AND (results.id = %llu))",
36671                                         result);
36672       else
36673         severity_sql = g_strdup_printf ("(SELECT results.severity"
36674                                         " FROM results"
36675                                         " WHERE results.id = %llu)",
36676                                         result);
36677 
36678       result_clause = g_strdup_printf (" AND"
36679                                        " (result = %llu"
36680                                        "  OR (result = 0 AND nvt ="
36681                                        "      (SELECT results.nvt FROM results"
36682                                        "       WHERE results.id = %llu)))"
36683                                        " AND (hosts is NULL"
36684                                        "      OR hosts = ''"
36685                                        "      OR hosts_contains (hosts,"
36686                                        "      (SELECT results.host FROM results"
36687                                        "       WHERE results.id = %llu)))"
36688                                        " AND (port is NULL"
36689                                        "      OR port = ''"
36690                                        "      OR port ="
36691                                        "      (SELECT results.port FROM results"
36692                                        "       WHERE results.id = %llu))"
36693                                        " AND (severity_matches_ov (%s,"
36694                                        "                           severity))"
36695                                        " AND (task = 0 OR task = %llu)",
36696                                        result,
36697                                        result,
36698                                        result,
36699                                        result,
36700                                        severity_sql,
36701                                        task);
36702       g_free (severity_sql);
36703     }
36704   else if (task)
36705     {
36706       result_clause = g_strdup_printf
36707                        (" AND (notes.task = %llu OR notes.task = 0)"
36708                         " AND nvt IN"
36709                         " (SELECT DISTINCT nvt FROM results"
36710                         "  WHERE results.task = %llu)"
36711                         " AND (notes.result = 0"
36712                         "      OR (SELECT task FROM results"
36713                         "          WHERE results.id = notes.result)"
36714                         "         = %llu)",
36715                         task,
36716                         task,
36717                         task);
36718     }
36719   else if (nvt)
36720     {
36721       result_clause = g_strdup_printf
36722                        (" AND (notes.nvt = (SELECT oid FROM nvts"
36723                         "                   WHERE nvts.id = %llu))",
36724                         nvt);
36725     }
36726   else
36727     result_clause = NULL;
36728 
36729   ret = count ("note",
36730                get,
36731                columns,
36732                trash_columns,
36733                filter_columns,
36734                task || nvt,
36735                NULL,
36736                result_clause,
36737                TRUE);
36738 
36739   g_free (result_clause);
36740 
36741   return ret;
36742 }
36743 
36744 /**
36745  * @brief Initialise a note iterator.
36746  *
36747  * @param[in]  iterator    Iterator.
36748  * @param[in]  get         GET data.
36749  * @param[in]  result      Result to limit notes to, 0 for all.
36750  * @param[in]  task        If result is > 0, task whose notes on result to
36751  *                         include, otherwise task to limit notes to.  0 for
36752  *                         all tasks.
36753  * @param[in]  nvt         NVT to limit notes to, 0 for all.
36754  *
36755  * @return 0 success, 1 failed to find target, 2 failed to find filter,
36756  *         -1 error.
36757  */
36758 int
init_note_iterator(iterator_t * iterator,const get_data_t * get,nvt_t nvt,result_t result,task_t task)36759 init_note_iterator (iterator_t* iterator, const get_data_t *get, nvt_t nvt,
36760                     result_t result, task_t task)
36761 {
36762   static const char *filter_columns[] = NOTE_ITERATOR_FILTER_COLUMNS;
36763   static column_t columns[] = NOTE_ITERATOR_COLUMNS;
36764   static column_t trash_columns[] = NOTE_ITERATOR_TRASH_COLUMNS;
36765   gchar *result_clause, *filter, *task_id;
36766   int ret;
36767 
36768   assert (current_credentials.uuid);
36769   assert ((nvt && get->id) == 0);
36770   assert ((task && get->id) == 0);
36771 
36772   assert (result ? nvt == 0 : 1);
36773   assert (task ? nvt == 0 : 1);
36774 
36775   /* Treat the "task_id" filter keyword as if the task was given in "task". */
36776 
36777   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
36778     {
36779       filter = filter_term (get->filt_id);
36780       if (filter == NULL)
36781         return 2;
36782     }
36783   else
36784     filter = NULL;
36785 
36786   task_id = filter_term_value (filter ? filter : get->filter, "task_id");
36787 
36788   g_free (filter);
36789 
36790   if (task_id)
36791     {
36792       find_task_with_permission (task_id, &task, "get_tasks");
36793       g_free (task_id);
36794     }
36795 
36796   if (result)
36797     {
36798       gchar *severity_sql;
36799 
36800       if (setting_dynamic_severity_int ())
36801         severity_sql = g_strdup_printf ("(SELECT CASE"
36802                                         " WHEN results.severity"
36803                                         "      > " G_STRINGIFY (SEVERITY_LOG)
36804                                         " THEN CAST (nvts.cvss_base AS real)"
36805                                         " ELSE results.severity END"
36806                                         " FROM results, nvts"
36807                                         " WHERE (nvts.oid = results.nvt)"
36808                                         "   AND (results.id = %llu))",
36809                                         result);
36810       else
36811         severity_sql = g_strdup_printf ("(SELECT results.severity"
36812                                         " FROM results"
36813                                         " WHERE results.id = %llu)",
36814                                         result);
36815 
36816       result_clause = g_strdup_printf (" AND"
36817                                        " (result = %llu"
36818                                        "  OR (result = 0 AND nvt ="
36819                                        "      (SELECT results.nvt FROM results"
36820                                        "       WHERE results.id = %llu)))"
36821                                        " AND (hosts is NULL"
36822                                        "      OR hosts = ''"
36823                                        "      OR hosts_contains (hosts,"
36824                                        "      (SELECT results.host FROM results"
36825                                        "       WHERE results.id = %llu)))"
36826                                        " AND (port is NULL"
36827                                        "      OR port = ''"
36828                                        "      OR port ="
36829                                        "      (SELECT results.port FROM results"
36830                                        "       WHERE results.id = %llu))"
36831                                        " AND (severity_matches_ov (%s,"
36832                                        "                           severity))"
36833                                        " AND (task = 0 OR task = %llu)",
36834                                        result,
36835                                        result,
36836                                        result,
36837                                        result,
36838                                        severity_sql,
36839                                        task);
36840 
36841       g_free (severity_sql);
36842     }
36843   else if (task)
36844     {
36845       result_clause = g_strdup_printf
36846                        (" AND (notes.task = %llu OR notes.task = 0)"
36847                         " AND nvt IN (SELECT DISTINCT nvt FROM results"
36848                         "             WHERE results.task = %llu)"
36849                         " AND (notes.result = 0"
36850                         "      OR (SELECT task FROM results"
36851                         "          WHERE results.id = notes.result)"
36852                         "         = %llu)",
36853                         task,
36854                         task,
36855                         task);
36856     }
36857   else if (nvt)
36858     {
36859       result_clause = g_strdup_printf
36860                        (" AND (notes.nvt = (SELECT oid FROM nvts"
36861                         "                   WHERE nvts.id = %llu))",
36862                         nvt);
36863     }
36864   else
36865     result_clause = NULL;
36866 
36867   ret = init_get_iterator (iterator,
36868                            "note",
36869                            get,
36870                            columns,
36871                            trash_columns,
36872                            filter_columns,
36873                            task || nvt,
36874                            NULL,
36875                            result_clause,
36876                            TRUE);
36877 
36878   g_free (result_clause);
36879 
36880   return ret;
36881 }
36882 
36883 /**
36884  * @brief Get the NVT OID from a note iterator.
36885  *
36886  * @param[in]  iterator  Iterator.
36887  *
36888  * @return NVT OID, or NULL if iteration is complete.  Freed by
36889  *         cleanup_iterator.
36890  */
36891 DEF_ACCESS (note_iterator_nvt_oid, GET_ITERATOR_COLUMN_COUNT);
36892 
36893 /**
36894  * @brief Get the text from a note iterator.
36895  *
36896  * @param[in]  iterator  Iterator.
36897  *
36898  * @return Text, or NULL if iteration is complete.  Freed by
36899  *         cleanup_iterator.
36900  */
36901 DEF_ACCESS (note_iterator_text, GET_ITERATOR_COLUMN_COUNT + 1);
36902 
36903 /**
36904  * @brief Get the hosts from a note iterator.
36905  *
36906  * @param[in]  iterator  Iterator.
36907  *
36908  * @return Hosts, or NULL if iteration is complete.  Freed by
36909  *         cleanup_iterator.
36910  */
36911 DEF_ACCESS (note_iterator_hosts, GET_ITERATOR_COLUMN_COUNT + 2);
36912 
36913 /**
36914  * @brief Get the port from a note iterator.
36915  *
36916  * @param[in]  iterator  Iterator.
36917  *
36918  * @return Port, or NULL if iteration is complete.  Freed by
36919  *         cleanup_iterator.
36920  */
36921 DEF_ACCESS (note_iterator_port, GET_ITERATOR_COLUMN_COUNT + 3);
36922 
36923 /**
36924  * @brief Get the task from a note iterator.
36925  *
36926  * @param[in]  iterator  Iterator.
36927  *
36928  * @return The task associated with the note, or 0 on error.
36929  */
36930 task_t
note_iterator_task(iterator_t * iterator)36931 note_iterator_task (iterator_t* iterator)
36932 {
36933   if (iterator->done) return 0;
36934   return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
36935 }
36936 
36937 /**
36938  * @brief Get the result from a note iterator.
36939  *
36940  * @param[in]  iterator  Iterator.
36941  *
36942  * @return The result associated with the note, or 0 on error.
36943  */
36944 result_t
note_iterator_result(iterator_t * iterator)36945 note_iterator_result (iterator_t* iterator)
36946 {
36947   if (iterator->done) return 0;
36948   return (result_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
36949 }
36950 
36951 /**
36952  * @brief Get the end time from an note iterator.
36953  *
36954  * @param[in]  iterator  Iterator.
36955  *
36956  * @return Time until which note applies.  0 for always.  1 means the
36957  *         note has been explicitly turned off.
36958  */
36959 time_t
note_iterator_end_time(iterator_t * iterator)36960 note_iterator_end_time (iterator_t* iterator)
36961 {
36962   int ret;
36963   if (iterator->done) return -1;
36964   ret = (time_t) iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
36965   return ret;
36966 }
36967 
36968 /**
36969  * @brief Get the active status from an note iterator.
36970  *
36971  * @param[in]  iterator  Iterator.
36972  *
36973  * @return 1 if active, else 0.
36974  */
36975 int
note_iterator_active(iterator_t * iterator)36976 note_iterator_active (iterator_t* iterator)
36977 {
36978   int ret;
36979   if (iterator->done) return -1;
36980   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
36981   return ret;
36982 }
36983 
36984 /**
36985  * @brief Get the NVT name from a note iterator.
36986  *
36987  * @param[in]  iterator  Iterator.
36988  *
36989  * @return NVT name, or NULL if iteration is complete.  Freed by
36990  *         cleanup_iterator.
36991  */
36992 DEF_ACCESS (note_iterator_nvt_name, GET_ITERATOR_COLUMN_COUNT + 8);
36993 
36994 /**
36995  * @brief Get the NVT type from a note iterator.
36996  *
36997  * @param[in]  iterator  Iterator.
36998  *
36999  * @return NVT type, or NULL.  Static string.
37000  */
37001 const char *
note_iterator_nvt_type(iterator_t * iterator)37002 note_iterator_nvt_type (iterator_t *iterator)
37003 {
37004   const char *oid;
37005 
37006   oid = note_iterator_nvt_oid (iterator);
37007   if (oid == NULL)
37008     return NULL;
37009 
37010   if (g_str_has_prefix (oid, "CVE-"))
37011     return "cve";
37012 
37013   if (g_str_has_prefix (oid, "oval:"))
37014     return "ovaldef";
37015 
37016   return "nvt";
37017 }
37018 
37019 /**
37020  * @brief Get the severity from a note iterator.
37021  *
37022  * @param[in]  iterator  Iterator.
37023  *
37024  * @return The severity to apply the note to, or NULL if iteration is complete.
37025  *         Freed by cleanup_iterator.
37026  */
37027 DEF_ACCESS (note_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 12);
37028 
37029 
37030 /* Overrides. */
37031 
37032 /**
37033  * @brief Find a override for a specific permission, given a UUID.
37034  *
37035  * @param[in]   uuid        UUID of override.
37036  * @param[out]  override    Override return, 0 if successfully failed to find
37037  *                          override.
37038  * @param[in]   permission  Permission.
37039  *
37040  * @return FALSE on success (including if failed to find override), TRUE on
37041  *         error.
37042  */
37043 gboolean
find_override_with_permission(const char * uuid,override_t * override,const char * permission)37044 find_override_with_permission (const char* uuid, override_t* override,
37045                                const char *permission)
37046 {
37047   return find_resource_with_permission ("override", uuid, override, permission,
37048                                         0);
37049 }
37050 
37051 /**
37052  * @brief Create an override.
37053  *
37054  * @param[in]  active      NULL or -1 on, 0 off, n on for n days.
37055  * @param[in]  nvt         OID of overridden NVT.
37056  * @param[in]  text        Override text.
37057  * @param[in]  hosts       Hosts to apply override to, NULL for any host.
37058  * @param[in]  port        Port to apply override to, NULL for any port.
37059  * @param[in]  threat      Threat to apply override to, "" or NULL for any threat.
37060  * @param[in]  new_threat  Threat to override result to.
37061  * @param[in]  severity    Severity to apply override to, "" or NULL for any.
37062  * @param[in]  new_severity Severity score to override "Alarm" type results to.
37063  * @param[in]  task        Task to apply override to, 0 for any task.
37064  * @param[in]  result      Result to apply override to, 0 for any result.
37065  * @param[out] override    Created override.
37066  *
37067  * @return 0 success, 1 failed to find NVT, 2 invalid port, 3 invalid severity,
37068  *         99 permission denied, -1 error.
37069  */
37070 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)37071 create_override (const char* active, const char* nvt, const char* text,
37072                  const char* hosts, const char* port, const char* threat,
37073                  const char* new_threat, const char* severity,
37074                  const char* new_severity, task_t task, result_t result,
37075                  override_t* override)
37076 {
37077   gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
37078   double severity_dbl, new_severity_dbl;
37079   GHashTable *reports;
37080   GHashTableIter reports_iter;
37081   report_t *reports_ptr;
37082   gchar *override_id, *users_where;
37083   int auto_cache_rebuild;
37084   override_t new_override;
37085 
37086   if (acl_user_may ("create_override") == 0)
37087     return 99;
37088 
37089   if (nvt == NULL)
37090     return -1;
37091 
37092   if (text == NULL)
37093     return -1;
37094 
37095   if (!nvt_exists (nvt))
37096     return 1;
37097 
37098   if (port && validate_results_port (port))
37099     return 2;
37100 
37101   if (threat && strcmp (threat, "High") && strcmp (threat, "Medium")
37102       && strcmp (threat, "Low") && strcmp (threat, "Log")
37103       && strcmp (threat, "Alarm") && strcmp (threat, ""))
37104     return -1;
37105 
37106   if (new_threat && strcmp (new_threat, "High") && strcmp (new_threat, "Medium")
37107       && strcmp (new_threat, "Low") && strcmp (new_threat, "Log")
37108       && strcmp (new_threat, "False Positive")
37109       && strcmp (new_threat, "Alarm") && strcmp (new_threat, ""))
37110     return -1;
37111 
37112   severity_dbl = 0.0;
37113   if (severity != NULL && strcmp (severity, ""))
37114     {
37115       if (sscanf (severity, "%lf", &severity_dbl) != 1
37116           || ((severity_dbl < 0.0 || severity_dbl > 10.0)
37117               && severity_dbl != SEVERITY_LOG))
37118         return 3;
37119       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
37120     }
37121   else if (threat != NULL && strcmp (threat, ""))
37122     {
37123       if (strcmp (threat, "Alarm") == 0)
37124         severity_dbl = 0.1;
37125       else if (strcmp (threat, "High") == 0)
37126         severity_dbl = 0.1;
37127       else if (strcmp (threat, "Medium") == 0)
37128         severity_dbl = 0.1;
37129       else if (strcmp (threat, "Low") == 0)
37130         severity_dbl = 0.1;
37131       else if (strcmp (threat, "Log") == 0)
37132         severity_dbl = SEVERITY_LOG;
37133       else
37134         return -1;
37135 
37136       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
37137     }
37138   else
37139     quoted_severity = g_strdup ("NULL");
37140 
37141   new_severity_dbl = 0.0;
37142   if (new_severity != NULL && strcmp (new_severity, ""))
37143     {
37144       if (sscanf (new_severity, "%lf", &new_severity_dbl) != 1
37145           || ((new_severity_dbl < 0.0 || new_severity_dbl > 10.0)
37146               && new_severity_dbl != SEVERITY_LOG
37147               && new_severity_dbl != SEVERITY_FP))
37148         {
37149           g_free (quoted_severity);
37150           return 2;
37151         }
37152     }
37153   else if (new_threat != NULL && strcmp (new_threat, ""))
37154     {
37155       if (strcmp (new_threat, "Alarm") == 0)
37156         new_severity_dbl = 10.0;
37157       else if (strcmp (new_threat, "High") == 0)
37158         new_severity_dbl = 10.0;
37159       else if (strcmp (new_threat, "Medium") == 0)
37160         new_severity_dbl = 5.0;
37161       else if (strcmp (new_threat, "Low") == 0)
37162         new_severity_dbl = 2.0;
37163       else if (strcmp (new_threat, "Log") == 0)
37164         new_severity_dbl = SEVERITY_LOG;
37165       else
37166         return -1;
37167     }
37168   else
37169     {
37170       g_free (quoted_severity);
37171       return -1;
37172     }
37173 
37174   quoted_text = sql_insert (text);
37175   quoted_hosts = sql_insert (hosts);
37176   quoted_port = sql_insert (port);
37177 
37178   result_nvt_notice (nvt);
37179   sql ("INSERT INTO overrides"
37180        " (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
37181        "  port, severity, new_severity, task, result, end_time,"
37182        "  result_nvt)"
37183        " VALUES"
37184        " (make_uuid (), (SELECT id FROM users WHERE users.uuid = '%s'),"
37185        "  '%s', %i, %i, %s, %s, %s, %s, %1.1f, %llu, %llu, %i,"
37186        "  (SELECT id FROM result_nvts WHERE nvt = '%s'));",
37187        current_credentials.uuid,
37188        nvt,
37189        time (NULL),
37190        time (NULL),
37191        quoted_text,
37192        quoted_hosts,
37193        quoted_port,
37194        quoted_severity,
37195        new_severity_dbl,
37196        task,
37197        result,
37198        (active == NULL || (strcmp (active, "-1") == 0))
37199          ? 0
37200          : (strcmp (active, "0")
37201              ? (time (NULL) + (atoi (active) * 60 * 60 * 24))
37202              : 1),
37203        nvt);
37204 
37205   g_free (quoted_text);
37206   g_free (quoted_hosts);
37207   g_free (quoted_port);
37208   g_free (quoted_severity);
37209 
37210   if (override)
37211     *override = sql_last_insert_id ();
37212   new_override = sql_last_insert_id ();
37213 
37214   override_uuid (new_override, &override_id);
37215   users_where = acl_users_with_access_where ("override", override_id, NULL,
37216                                              "id");
37217 
37218   reports = reports_for_override (new_override);
37219   reports_ptr = NULL;
37220   g_hash_table_iter_init (&reports_iter, reports);
37221   auto_cache_rebuild = setting_auto_cache_rebuild_int ();
37222   while (g_hash_table_iter_next (&reports_iter,
37223                                  ((gpointer*)&reports_ptr), NULL))
37224     {
37225       if (auto_cache_rebuild)
37226         report_cache_counts (*reports_ptr, 0, 1, users_where);
37227       else
37228         report_clear_count_cache (*reports_ptr, 0, 1, users_where);
37229     }
37230   g_hash_table_destroy (reports);
37231   g_free (override_id);
37232   g_free (users_where);
37233 
37234   return 0;
37235 }
37236 
37237 /**
37238  * @brief Return the UUID of an override.
37239  *
37240  * @param[in]   override  Override.
37241  * @param[out]  id        Pointer to a newly allocated string.
37242  *
37243  * @return 0.
37244  */
37245 int
override_uuid(override_t override,char ** id)37246 override_uuid (override_t override, char ** id)
37247 {
37248   *id = sql_string ("SELECT uuid FROM overrides WHERE id = %llu;",
37249                     override);
37250   return 0;
37251 }
37252 
37253 /**
37254  * @brief Create a override from an existing override.
37255  *
37256  * @param[in]  override_id   UUID of existing override.
37257  * @param[out] new_override  New override.
37258  *
37259  * @return 0 success, 1 override exists already, 2 failed to find existing
37260  *         override, -1 error.
37261  */
37262 int
copy_override(const char * override_id,override_t * new_override)37263 copy_override (const char *override_id, override_t* new_override)
37264 {
37265   return copy_resource ("override", NULL, NULL, override_id,
37266                         "nvt, text, hosts, port, severity, new_severity, task,"
37267                         " result, end_time, result_nvt",
37268                         1, new_override, NULL);
37269 }
37270 
37271 /**
37272  * @brief Delete a override.
37273  *
37274  * @param[in]  override_id  UUID of override.
37275  * @param[in]  ultimate     Whether to remove entirely, or to trashcan.
37276  *
37277  * @return 0 success, 2 failed to find override, 99 permission denied, -1 error.
37278  */
37279 int
delete_override(const char * override_id,int ultimate)37280 delete_override (const char *override_id, int ultimate)
37281 {
37282   override_t override;
37283   GHashTable *reports;
37284   GHashTableIter reports_iter;
37285   report_t *reports_ptr;
37286   gchar *users_where;
37287   int auto_cache_rebuild;
37288 
37289   sql_begin_immediate ();
37290 
37291   if (acl_user_may ("delete_override") == 0)
37292     {
37293       sql_rollback ();
37294       return 99;
37295     }
37296 
37297   override = 0;
37298 
37299   if (find_override_with_permission (override_id, &override, "delete_override"))
37300     {
37301       sql_rollback ();
37302       return -1;
37303     }
37304 
37305   if (override == 0)
37306     {
37307       if (find_trash ("override", override_id, &override))
37308         {
37309           sql_rollback ();
37310           return -1;
37311         }
37312       if (override == 0)
37313         {
37314           sql_rollback ();
37315           return 2;
37316         }
37317       if (ultimate == 0)
37318         {
37319           /* It's already in the trashcan. */
37320           sql_commit ();
37321           return 0;
37322         }
37323 
37324       permissions_set_orphans ("override", override, LOCATION_TRASH);
37325       tags_remove_resource ("override", override, LOCATION_TRASH);
37326 
37327       sql ("DELETE FROM overrides_trash WHERE id = %llu;", override);
37328       sql_commit ();
37329       return 0;
37330     }
37331 
37332   reports = reports_for_override (override);
37333 
37334   users_where = acl_users_with_access_where ("override", override_id, NULL,
37335                                              "id");
37336 
37337   if (ultimate == 0)
37338     {
37339       sql ("INSERT INTO overrides_trash"
37340            " (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
37341            "  port, severity, new_severity, task, result, end_time, result_nvt)"
37342            " SELECT uuid, owner, nvt, creation_time, modification_time, text,"
37343            "        hosts, port, severity, new_severity,task,"
37344            "        result, end_time, result_nvt"
37345            " FROM overrides WHERE id = %llu;",
37346            override);
37347 
37348       permissions_set_locations ("override", override,
37349                                  sql_last_insert_id (),
37350                                  LOCATION_TRASH);
37351       tags_set_locations ("override", override,
37352                           sql_last_insert_id (),
37353                           LOCATION_TRASH);
37354     }
37355   else
37356     {
37357       permissions_set_orphans ("override", override, LOCATION_TABLE);
37358       tags_remove_resource ("override", override, LOCATION_TABLE);
37359     }
37360 
37361   sql ("DELETE FROM overrides WHERE id = %llu;", override);
37362 
37363   g_hash_table_iter_init (&reports_iter, reports);
37364   reports_ptr = NULL;
37365   auto_cache_rebuild = setting_auto_cache_rebuild_int ();
37366   while (g_hash_table_iter_next (&reports_iter,
37367                                  ((gpointer*)&reports_ptr), NULL))
37368     {
37369       if (auto_cache_rebuild)
37370         report_cache_counts (*reports_ptr, 0, 1, users_where);
37371       else
37372         report_clear_count_cache (*reports_ptr, 0, 1, users_where);
37373     }
37374   g_hash_table_destroy (reports);
37375   g_free (users_where);
37376 
37377   sql_commit ();
37378   return 0;
37379 }
37380 
37381 /**
37382  * @brief Modify an override.
37383  *
37384  * @param[in]  override_id  Override.
37385  * @param[in]  active       NULL or -2 leave as is, -1 on, 0 off, n on for n
37386  *                          days.
37387  * @param[in]  nvt         OID of noted NVT.
37388  * @param[in]  text        Override text.
37389  * @param[in]  hosts       Hosts to apply override to, NULL for any host.
37390  * @param[in]  port        Port to apply override to, NULL for any port.
37391  * @param[in]  threat      Threat to apply override to, "" or NULL for any threat.
37392  * @param[in]  new_threat  Threat to override result to.
37393  * @param[in]  severity    Severity to apply override to, "" or NULL for any threat.
37394  * @param[in]  new_severity Severity score to override "Alarm" type results to.
37395  * @param[in]  task_id     Task to apply override to, 0 for any task.
37396  * @param[in]  result_id   Result to apply override to, 0 for any result.
37397  *
37398  * @return 0 success, -1 error, 1 syntax error in active, 2 invalid port,
37399  *         3 invalid severity score, 4 failed to find NVT, 5 failed to find
37400  *         override, 6 failed to find task, 7 failed to find result,
37401  *         8 invalid threat, 9 invalid new_threat, 10 invalid new_severity,
37402  *         11 missing new_severity.
37403  */
37404 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)37405 modify_override (const gchar *override_id, const char *active, const char *nvt,
37406                  const char *text, const char *hosts, const char *port,
37407                  const char *threat, const char *new_threat,
37408                  const char *severity, const char *new_severity,
37409                  const gchar *task_id, const gchar *result_id)
37410 {
37411   gchar *quoted_text, *quoted_hosts, *quoted_port, *quoted_severity;
37412   double severity_dbl, new_severity_dbl;
37413   gchar *quoted_nvt;
37414   GHashTable *reports;
37415   GString *cache_invalidated_sql;
37416   int cache_invalidated;
37417   override_t override;
37418   task_t task;
37419   result_t result;
37420 
37421   reports = NULL;
37422   cache_invalidated = 0;
37423 
37424   override = 0;
37425   if (find_override_with_permission (override_id, &override, "modify_override"))
37426     return -1;
37427   if (override == 0)
37428     return 5;
37429 
37430   task = 0;
37431   if (task_id)
37432     {
37433       if (find_task_with_permission (task_id, &task, NULL))
37434         return -1;
37435       if (task == 0)
37436         {
37437           if (find_trash_task_with_permission (task_id, &task, NULL))
37438             return -1;
37439           if (task == 0)
37440             return 6;
37441         }
37442     }
37443 
37444   result = 0;
37445   if (result_id)
37446     {
37447       if (find_result_with_permission (result_id, &result, NULL))
37448         return -1;
37449       if (result == 0)
37450         return 7;
37451     }
37452 
37453   if (text == NULL)
37454     return -1;
37455 
37456   if (port && validate_results_port (port))
37457     return 2;
37458 
37459   if (nvt && !nvt_exists (nvt))
37460     return 4;
37461 
37462   severity_dbl = 0.0;
37463   if (severity != NULL && strcmp (severity, ""))
37464     {
37465       if (sscanf (severity, "%lf", &severity_dbl) != 1
37466           || ((severity_dbl < 0.0 || severity_dbl > 10.0)
37467               && severity_dbl != SEVERITY_LOG))
37468         return 3;
37469       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
37470     }
37471   else if (threat != NULL && strcmp (threat, ""))
37472     {
37473       if (strcmp (threat, "Alarm") == 0)
37474         severity_dbl = 0.1;
37475       else if (strcmp (threat, "High") == 0)
37476         severity_dbl = 0.1;
37477       else if (strcmp (threat, "Medium") == 0)
37478         severity_dbl = 0.1;
37479       else if (strcmp (threat, "Low") == 0)
37480         severity_dbl = 0.1;
37481       else if (strcmp (threat, "Log") == 0)
37482         severity_dbl = SEVERITY_LOG;
37483       else
37484         return 8;
37485 
37486       quoted_severity = g_strdup_printf ("'%1.1f'", severity_dbl);
37487     }
37488   else
37489     quoted_severity = g_strdup ("NULL");
37490 
37491   new_severity_dbl = 0.0;
37492   if (new_severity != NULL && strcmp (new_severity, ""))
37493     {
37494       if (sscanf (new_severity, "%lf", &new_severity_dbl) != 1
37495           || ((new_severity_dbl < 0.0 || new_severity_dbl > 10.0)
37496               && new_severity_dbl != SEVERITY_LOG
37497               && new_severity_dbl != SEVERITY_FP))
37498         {
37499           g_free (quoted_severity);
37500           return 10;
37501         }
37502     }
37503   else if (new_threat != NULL && strcmp (new_threat, ""))
37504     {
37505       if (strcmp (new_threat, "Alarm") == 0)
37506         new_severity_dbl = 10.0;
37507       else if (strcmp (new_threat, "High") == 0)
37508         new_severity_dbl = 10.0;
37509       else if (strcmp (new_threat, "Medium") == 0)
37510         new_severity_dbl = 5.0;
37511       else if (strcmp (new_threat, "Low") == 0)
37512         new_severity_dbl = 2.0;
37513       else if (strcmp (new_threat, "Log") == 0)
37514         new_severity_dbl = SEVERITY_LOG;
37515       else
37516         {
37517           g_free (quoted_severity);
37518           return 9;
37519         }
37520     }
37521   else
37522     {
37523       g_free (quoted_severity);
37524       return 11;
37525     }
37526 
37527   quoted_text = sql_insert (text);
37528   quoted_hosts = sql_insert (hosts);
37529   quoted_port = sql_insert (port);
37530   quoted_nvt = nvt ? sql_quote (nvt) : NULL;
37531 
37532   // Tests if a cache rebuild is necessary.
37533   //  The "active" status is checked separately
37534   cache_invalidated_sql = g_string_new ("");
37535 
37536   g_string_append_printf (cache_invalidated_sql,
37537                           "SELECT (cast (new_severity AS numeric) != %1.1f)",
37538                           new_severity_dbl);
37539 
37540   g_string_append_printf (cache_invalidated_sql,
37541                           " OR (task != %llu)",
37542                           task);
37543 
37544   g_string_append_printf (cache_invalidated_sql,
37545                           " OR (result != %llu)",
37546                           result);
37547 
37548   if (strcmp (quoted_severity, "NULL") == 0)
37549     g_string_append_printf (cache_invalidated_sql,
37550                             " OR (severity IS NOT NULL)");
37551   else
37552     g_string_append_printf (cache_invalidated_sql,
37553                             " OR (cast (severity AS numeric) != %1.1f)",
37554                             severity_dbl);
37555 
37556   if (strcmp (quoted_hosts, "NULL") == 0)
37557     g_string_append_printf (cache_invalidated_sql,
37558                             " OR (hosts IS NOT NULL)");
37559   else
37560     g_string_append_printf (cache_invalidated_sql,
37561                             " OR (hosts != %s)",
37562                             quoted_hosts);
37563 
37564   if (strcmp (quoted_port, "NULL") == 0)
37565     g_string_append_printf (cache_invalidated_sql,
37566                             " OR (hosts IS NOT NULL)");
37567   else
37568     g_string_append_printf (cache_invalidated_sql,
37569                             " OR (port != %s)",
37570                             quoted_port);
37571 
37572   g_string_append_printf (cache_invalidated_sql,
37573                           " FROM overrides WHERE id = %llu",
37574                           override);
37575 
37576   if (sql_int ("%s", cache_invalidated_sql->str))
37577     {
37578       cache_invalidated = 1;
37579     }
37580 
37581   g_string_free (cache_invalidated_sql, TRUE);
37582 
37583   // Check active status for changes, get old reports for rebuild if necessary
37584   //  and update override.
37585   result_nvt_notice (quoted_nvt);
37586   if ((active == NULL) || (strcmp (active, "-2") == 0))
37587     {
37588       if (cache_invalidated)
37589         reports = reports_for_override (override);
37590 
37591       sql ("UPDATE overrides SET"
37592            " modification_time = %i,"
37593            " text = %s,"
37594            " hosts = %s,"
37595            " port = %s,"
37596            " severity = %s,"
37597            " %s%s%s"
37598            " %s%s%s"
37599            " new_severity = %f,"
37600            " task = %llu,"
37601            " result = %llu"
37602            " WHERE id = %llu;",
37603            time (NULL),
37604            quoted_text,
37605            quoted_hosts,
37606            quoted_port,
37607            quoted_severity,
37608            nvt ? "nvt = '" : "",
37609            nvt ? quoted_nvt : "",
37610            nvt ? "'," : "",
37611            nvt ? "result_nvt = (SELECT id FROM result_nvts WHERE nvt='" : "",
37612            nvt ? quoted_nvt : "",
37613            nvt ? "')," : "",
37614            new_severity_dbl,
37615            task,
37616            result,
37617            override);
37618     }
37619   else
37620     {
37621       const char *point;
37622       point = active;
37623       int new_end_time;
37624 
37625       if (strcmp (point, "-1"))
37626         {
37627           while (*point && isdigit (*point)) point++;
37628           if (*point)
37629             {
37630               return 1;
37631             }
37632         }
37633 
37634       new_end_time = (strcmp (active, "-1")
37635                         ? (strcmp (active, "0")
37636                             ? (time (NULL) + atoi (active) * 60 * 60 * 24)
37637                             : 1)
37638                         : 0);
37639 
37640       if (cache_invalidated == 0
37641           && sql_int ("SELECT end_time != %d FROM overrides"
37642                       " WHERE id = %llu",
37643                       new_end_time, override))
37644         cache_invalidated = 1;
37645 
37646       if (cache_invalidated)
37647         reports = reports_for_override (override);
37648 
37649       sql ("UPDATE overrides SET"
37650            " end_time = %i,"
37651            " modification_time = %i,"
37652            " text = %s,"
37653            " hosts = %s,"
37654            " port = %s,"
37655            " severity = %s,"
37656            " %s%s%s"
37657            " %s%s%s"
37658            " new_severity = %f,"
37659            " task = %llu,"
37660            " result = %llu"
37661            " WHERE id = %llu;",
37662            new_end_time,
37663            time (NULL),
37664            quoted_text,
37665            quoted_hosts,
37666            quoted_port,
37667            quoted_severity,
37668            nvt ? "nvt = '" : "",
37669            nvt ? quoted_nvt : "",
37670            nvt ? "'," : "",
37671            nvt ? "result_nvt = (SELECT id FROM result_nvts WHERE nvt='" : "",
37672            nvt ? quoted_nvt : "",
37673            nvt ? "')," : "",
37674            new_severity_dbl,
37675            task,
37676            result,
37677            override);
37678     }
37679 
37680   g_free (quoted_text);
37681   g_free (quoted_hosts);
37682   g_free (quoted_port);
37683   g_free (quoted_severity);
37684   g_free (quoted_nvt);
37685 
37686   if (cache_invalidated)
37687     {
37688       GHashTableIter reports_iter;
37689       report_t *reports_ptr;
37690       gchar *users_where;
37691       int auto_cache_rebuild;
37692 
37693       users_where = acl_users_with_access_where ("override", override_id, NULL,
37694                                                  "id");
37695 
37696       reports_add_for_override (reports, override);
37697 
37698       g_hash_table_iter_init (&reports_iter, reports);
37699       reports_ptr = NULL;
37700       auto_cache_rebuild = setting_auto_cache_rebuild_int ();
37701       while (g_hash_table_iter_next (&reports_iter,
37702                                     ((gpointer*)&reports_ptr), NULL))
37703         {
37704           if (auto_cache_rebuild)
37705             report_cache_counts (*reports_ptr, 0, 1, users_where);
37706           else
37707             report_clear_count_cache (*reports_ptr, 0, 1, users_where);
37708         }
37709       g_free (users_where);
37710     }
37711 
37712   if (reports)
37713     g_hash_table_destroy (reports);
37714 
37715   return 0;
37716 }
37717 
37718 /**
37719  * @brief Filter columns for override iterator.
37720  */
37721 #define OVERRIDE_ITERATOR_FILTER_COLUMNS                                      \
37722  { ANON_GET_ITERATOR_FILTER_COLUMNS, "name", "nvt", "text", "nvt_id",         \
37723    "task_name", "task_id", "hosts", "port", "threat", "new_threat", "active", \
37724    "result", "severity", "new_severity", "active_days", NULL }
37725 
37726 /**
37727  * @brief Override iterator columns.
37728  */
37729 #define OVERRIDE_ITERATOR_COLUMNS                                           \
37730  {                                                                          \
37731    { "overrides.id", "id", KEYWORD_TYPE_INTEGER },                          \
37732    { "overrides.uuid", "uuid", KEYWORD_TYPE_STRING },                       \
37733    {                                                                        \
37734      "(CASE"                                                                \
37735      " WHEN overrides.nvt LIKE 'CVE-%%'"                                    \
37736      " THEN overrides.nvt"                                                  \
37737      " ELSE (SELECT name FROM nvts WHERE oid = overrides.nvt)"              \
37738      " END)",                                                               \
37739      "name",                                                                \
37740      KEYWORD_TYPE_STRING                                                    \
37741    },                                                                       \
37742    { "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING },                      \
37743    { "iso_time (overrides.creation_time)", NULL, KEYWORD_TYPE_STRING },     \
37744    { "iso_time (overrides.modification_time)", NULL, KEYWORD_TYPE_STRING }, \
37745    { "overrides.creation_time", "created", KEYWORD_TYPE_INTEGER },          \
37746    { "overrides.modification_time", "modified", KEYWORD_TYPE_INTEGER },     \
37747    {                                                                        \
37748      "(SELECT name FROM users WHERE users.id = overrides.owner)",           \
37749      "_owner",                                                              \
37750      KEYWORD_TYPE_STRING                                                    \
37751    },                                                                       \
37752    { "owner", NULL, KEYWORD_TYPE_INTEGER },                                 \
37753    /* Columns specific to overrides. */                                     \
37754    { "overrides.nvt", "oid", KEYWORD_TYPE_STRING },                         \
37755    { "overrides.text", "text", KEYWORD_TYPE_STRING },                       \
37756    { "overrides.hosts", "hosts", KEYWORD_TYPE_STRING },                     \
37757    { "overrides.port", "port", KEYWORD_TYPE_STRING },                       \
37758    { "severity_to_level (overrides.severity, 1)",                           \
37759      "threat",                                                              \
37760      KEYWORD_TYPE_STRING },                                                 \
37761    { "severity_to_level (overrides.new_severity, 0)",                       \
37762      "new_threat",                                                          \
37763      KEYWORD_TYPE_STRING },                                                 \
37764    { "overrides.task", NULL, KEYWORD_TYPE_STRING },                         \
37765    { "overrides.result", "result", KEYWORD_TYPE_INTEGER },                  \
37766    { "overrides.end_time", NULL, KEYWORD_TYPE_INTEGER },                    \
37767    {                                                                        \
37768      "CAST (((overrides.end_time = 0) OR (overrides.end_time >= m_now ()))" \
37769      "      AS INTEGER)",                                                   \
37770      "active",                                                              \
37771      KEYWORD_TYPE_INTEGER                                                   \
37772    },                                                                       \
37773    {                                                                        \
37774      "(CASE"                                                                \
37775      " WHEN overrides.nvt LIKE 'CVE-%%'"                                    \
37776      " THEN overrides.nvt"                                                  \
37777      " ELSE (SELECT name FROM nvts WHERE oid = overrides.nvt)"              \
37778      " END)",                                                               \
37779      "name",                                                                \
37780      KEYWORD_TYPE_STRING                                                    \
37781    },                                                                       \
37782    { "overrides.nvt", "nvt_id", KEYWORD_TYPE_STRING },                      \
37783    { "(SELECT uuid FROM tasks WHERE id = overrides.task)",                  \
37784      "task_id",                                                             \
37785      KEYWORD_TYPE_STRING },                                                 \
37786    { "(SELECT name FROM tasks WHERE id = overrides.task)",                  \
37787      "task_name",                                                           \
37788      KEYWORD_TYPE_STRING },                                                 \
37789    { "overrides.severity", "severity", KEYWORD_TYPE_DOUBLE },               \
37790    { "overrides.new_severity", "new_severity", KEYWORD_TYPE_DOUBLE },       \
37791    {                                                                        \
37792      "(SELECT name FROM users WHERE users.id = overrides.owner)",           \
37793      "_owner",                                                              \
37794      KEYWORD_TYPE_STRING                                                    \
37795    },                                                                       \
37796    { "days_from_now (overrides.end_time)",                                  \
37797      "active_days",                                                         \
37798      KEYWORD_TYPE_INTEGER },                                                \
37799    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                     \
37800  }
37801 
37802 /**
37803  * @brief Override iterator columns for trash case.
37804  */
37805 #define OVERRIDE_ITERATOR_TRASH_COLUMNS                                     \
37806  {                                                                          \
37807    { "overrides_trash.id", "id", KEYWORD_TYPE_INTEGER },                    \
37808    { "overrides_trash.uuid", "uuid", KEYWORD_TYPE_STRING },                 \
37809    { "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING },                      \
37810    { "CAST ('' AS TEXT)", NULL, KEYWORD_TYPE_STRING },                      \
37811    { "iso_time (overrides_trash.creation_time)",                            \
37812      NULL,                                                                  \
37813      KEYWORD_TYPE_STRING },                                                 \
37814    { "iso_time (overrides_trash.modification_time)",                        \
37815      NULL,                                                                  \
37816      KEYWORD_TYPE_STRING },                                                 \
37817    { "overrides_trash.creation_time",                                       \
37818      "created",                                                             \
37819      KEYWORD_TYPE_INTEGER },                                                \
37820    { "overrides_trash.modification_time",                                   \
37821      "modified",                                                            \
37822      KEYWORD_TYPE_INTEGER },                                                \
37823    {                                                                        \
37824      "(SELECT name FROM users WHERE users.id = overrides_trash.owner)",     \
37825      "_owner",                                                              \
37826      KEYWORD_TYPE_STRING                                                    \
37827    },                                                                       \
37828    { "owner", NULL, KEYWORD_TYPE_STRING },                                  \
37829    /* Columns specific to overrides_trash. */                               \
37830    { "overrides_trash.nvt", "oid", KEYWORD_TYPE_STRING },                   \
37831    { "overrides_trash.text", "text", KEYWORD_TYPE_STRING },                 \
37832    { "overrides_trash.hosts", "hosts", KEYWORD_TYPE_STRING },               \
37833    { "overrides_trash.port", "port", KEYWORD_TYPE_STRING },                 \
37834    { "severity_to_level (overrides_trash.severity, 1)",                     \
37835      "threat",                                                              \
37836      KEYWORD_TYPE_STRING },                                                 \
37837    { "severity_to_level (overrides_trash.new_severity, 0)",                 \
37838      "new_threat",                                                          \
37839      KEYWORD_TYPE_STRING },                                                 \
37840    { "overrides_trash.task", NULL, KEYWORD_TYPE_INTEGER },                  \
37841    { "overrides_trash.result", "result", KEYWORD_TYPE_INTEGER },            \
37842    { "overrides_trash.end_time", NULL, KEYWORD_TYPE_INTEGER },              \
37843    {                                                                        \
37844      "CAST (((overrides_trash.end_time = 0)"                                \
37845      "       OR (overrides_trash.end_time >= m_now ())) AS INTEGER)",       \
37846      "active",                                                              \
37847      KEYWORD_TYPE_INTEGER                                                   \
37848    },                                                                       \
37849    {                                                                        \
37850      "(CASE"                                                                \
37851      " WHEN overrides_trash.nvt LIKE 'CVE-%%'"                              \
37852      " THEN overrides_trash.nvt"                                            \
37853      " ELSE (SELECT name FROM nvts WHERE oid = overrides_trash.nvt)"        \
37854      " END)",                                                               \
37855      "nvt",                                                                 \
37856      KEYWORD_TYPE_STRING                                                    \
37857    },                                                                       \
37858    { "overrides_trash.nvt", "nvt_id", KEYWORD_TYPE_STRING },                \
37859    {                                                                        \
37860      "(SELECT uuid FROM tasks WHERE id = overrides_trash.task)",            \
37861      "task_id",                                                             \
37862      KEYWORD_TYPE_STRING                                                    \
37863    },                                                                       \
37864    {                                                                        \
37865      "(SELECT name FROM tasks WHERE id = overrides_trash.task)",            \
37866      "task_name",                                                           \
37867      KEYWORD_TYPE_STRING                                                    \
37868    },                                                                       \
37869    { "overrides_trash.severity", NULL, KEYWORD_TYPE_DOUBLE },               \
37870    { "overrides_trash.new_severity", NULL, KEYWORD_TYPE_DOUBLE },           \
37871    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                     \
37872  }
37873 
37874 /**
37875  * @brief Count number of overrides.
37876  *
37877  * @param[in]  get         GET params.
37878  * @param[in]  result      Result to limit overrides to, 0 for all.
37879  * @param[in]  task        If result is > 0, task whose overrides on result to
37880  *                         include, otherwise task to limit overrides to.  0 for
37881  *                         all tasks.
37882  * @param[in]  nvt         NVT to limit overrides to, 0 for all.
37883  *
37884  * @return Total number of overrides in filtered set.
37885  */
37886 int
override_count(const get_data_t * get,nvt_t nvt,result_t result,task_t task)37887 override_count (const get_data_t *get, nvt_t nvt, result_t result, task_t task)
37888 {
37889   static const char *filter_columns[] = OVERRIDE_ITERATOR_FILTER_COLUMNS;
37890   static column_t columns[] = OVERRIDE_ITERATOR_COLUMNS;
37891   static column_t trash_columns[] = OVERRIDE_ITERATOR_TRASH_COLUMNS;
37892   gchar *result_clause, *filter, *task_id;
37893   int ret;
37894 
37895   /* Treat the "task_id" filter keyword as if the task was given in "task". */
37896 
37897   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
37898     {
37899       filter = filter_term (get->filt_id);
37900       if (filter == NULL)
37901         return 2;
37902     }
37903   else
37904     filter = NULL;
37905 
37906   task_id = filter_term_value (filter ? filter : get->filter, "task_id");
37907 
37908   g_free (filter);
37909 
37910   if (task_id)
37911     {
37912       find_task_with_permission (task_id, &task, "get_tasks");
37913       g_free (task_id);
37914     }
37915 
37916   if (result)
37917     {
37918       gchar *severity_sql;
37919 
37920       if (setting_dynamic_severity_int ())
37921         severity_sql = g_strdup_printf ("(SELECT CASE"
37922                                         " WHEN results.severity"
37923                                         "      > " G_STRINGIFY (SEVERITY_LOG)
37924                                         " THEN CAST (nvts.cvss_base AS real)"
37925                                         " ELSE results.severity END"
37926                                         " FROM results, nvts"
37927                                         " WHERE (nvts.oid = results.nvt)"
37928                                         "   AND (results.id = %llu))",
37929                                         result);
37930       else
37931         severity_sql = g_strdup_printf ("(SELECT results.severity"
37932                                         " FROM results"
37933                                         " WHERE results.id = %llu)",
37934                                         result);
37935 
37936       result_clause = g_strdup_printf (" AND"
37937                                        " (result = %llu"
37938                                        "  OR (result = 0 AND nvt ="
37939                                        "      (SELECT results.nvt FROM results"
37940                                        "       WHERE results.id = %llu)))"
37941                                        " AND (hosts is NULL"
37942                                        "      OR hosts = ''"
37943                                        "      OR hosts_contains (hosts,"
37944                                        "      (SELECT results.host FROM results"
37945                                        "       WHERE results.id = %llu)))"
37946                                        " AND (port is NULL"
37947                                        "      OR port = ''"
37948                                        "      OR port ="
37949                                        "      (SELECT results.port FROM results"
37950                                        "       WHERE results.id = %llu))"
37951                                        " AND (severity_matches_ov (%s,"
37952                                        "                           severity))"
37953                                        " AND (task = 0 OR task = %llu)",
37954                                        result,
37955                                        result,
37956                                        result,
37957                                        result,
37958                                        severity_sql,
37959                                        task);
37960 
37961       g_free (severity_sql);
37962     }
37963   else if (task)
37964     {
37965       result_clause = g_strdup_printf
37966                        (" AND (overrides.task = %llu OR overrides.task = 0)"
37967                         " AND nvt IN"
37968                         " (SELECT DISTINCT nvt FROM results"
37969                         "  WHERE results.task = %llu)"
37970                         " AND (overrides.result = 0"
37971                         "      OR (SELECT task FROM results"
37972                         "          WHERE results.id = overrides.result)"
37973                         "         = %llu)",
37974                         task,
37975                         task,
37976                         task);
37977     }
37978   else if (nvt)
37979     {
37980       result_clause = g_strdup_printf
37981                        (" AND (overrides.nvt"
37982                         "      = (SELECT oid FROM nvts WHERE nvts.id = %llu))",
37983                         nvt);
37984     }
37985   else
37986     result_clause = NULL;
37987 
37988   ret = count ("override",
37989                get,
37990                columns,
37991                trash_columns,
37992                filter_columns,
37993                task || nvt,
37994                NULL,
37995                result_clause,
37996                TRUE);
37997 
37998   g_free (result_clause);
37999 
38000   return ret;
38001 }
38002 
38003 /**
38004  * @brief Initialise an override iterator.
38005  *
38006  * @param[in]  iterator    Iterator.
38007  * @param[in]  get         GET data.
38008  * @param[in]  result      Result to limit overrides to, 0 for all.
38009  * @param[in]  task        If result is > 0, task whose overrides on result to
38010  *                         include, otherwise task to limit overrides to.  0 for
38011  *                         all tasks.
38012  * @param[in]  nvt         NVT to limit overrides to, 0 for all.
38013  *
38014  * @return 0 success, 1 failed to find target, 2 failed to find filter,
38015  *         -1 error.
38016  */
38017 int
init_override_iterator(iterator_t * iterator,const get_data_t * get,nvt_t nvt,result_t result,task_t task)38018 init_override_iterator (iterator_t* iterator, const get_data_t *get, nvt_t nvt,
38019                         result_t result, task_t task)
38020 {
38021   static const char *filter_columns[] = OVERRIDE_ITERATOR_FILTER_COLUMNS;
38022   static column_t columns[] = OVERRIDE_ITERATOR_COLUMNS;
38023   static column_t trash_columns[] = OVERRIDE_ITERATOR_TRASH_COLUMNS;
38024   gchar *result_clause, *filter, *task_id;
38025   int ret;
38026 
38027   assert (current_credentials.uuid);
38028   assert ((nvt && get->id) == 0);
38029   assert ((task && get->id) == 0);
38030 
38031   assert (result ? nvt == 0 : 1);
38032   assert (task ? nvt == 0 : 1);
38033 
38034   /* Treat the "task_id" filter keyword as if the task was given in "task". */
38035 
38036   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
38037     {
38038       filter = filter_term (get->filt_id);
38039       if (filter == NULL)
38040         return 2;
38041     }
38042   else
38043     filter = NULL;
38044 
38045   task_id = filter_term_value (filter ? filter : get->filter, "task_id");
38046 
38047   g_free (filter);
38048 
38049   if (task_id)
38050     {
38051       find_task_with_permission (task_id, &task, "get_tasks");
38052       g_free (task_id);
38053     }
38054 
38055   if (result)
38056     {
38057       gchar *severity_sql;
38058 
38059       if (setting_dynamic_severity_int ())
38060         severity_sql = g_strdup_printf ("(SELECT CASE"
38061                                         " WHEN results.severity"
38062                                         "      > " G_STRINGIFY (SEVERITY_LOG)
38063                                         " THEN CAST (nvts.cvss_base AS real)"
38064                                         " ELSE results.severity END"
38065                                         " FROM results, nvts"
38066                                         " WHERE (nvts.oid = results.nvt)"
38067                                         "   AND (results.id = %llu))",
38068                                         result);
38069       else
38070         severity_sql = g_strdup_printf ("(SELECT results.severity"
38071                                         " FROM results"
38072                                         " WHERE results.id = %llu)",
38073                                         result);
38074 
38075       result_clause = g_strdup_printf (" AND"
38076                                        " (result = %llu"
38077                                        "  OR (result = 0 AND nvt ="
38078                                        "      (SELECT results.nvt FROM results"
38079                                        "       WHERE results.id = %llu)))"
38080                                        " AND (hosts is NULL"
38081                                        "      OR hosts = ''"
38082                                        "      OR hosts_contains (hosts,"
38083                                        "      (SELECT results.host FROM results"
38084                                        "       WHERE results.id = %llu)))"
38085                                        " AND (port is NULL"
38086                                        "      OR port = ''"
38087                                        "      OR port ="
38088                                        "      (SELECT results.port FROM results"
38089                                        "       WHERE results.id = %llu))"
38090                                        " AND (severity_matches_ov (%s,"
38091                                        "                           severity))"
38092                                        " AND (task = 0 OR task = %llu)",
38093                                        result,
38094                                        result,
38095                                        result,
38096                                        result,
38097                                        severity_sql,
38098                                        task);
38099 
38100       g_free (severity_sql);
38101     }
38102   else if (task)
38103     {
38104       result_clause = g_strdup_printf
38105                        (" AND (overrides.task = %llu OR overrides.task = 0)"
38106                         " AND nvt IN"
38107                         " (SELECT DISTINCT nvt FROM results"
38108                         "  WHERE results.task = %llu)"
38109                         " AND (overrides.result = 0"
38110                         "      OR (SELECT task FROM results"
38111                         "          WHERE results.id = overrides.result)"
38112                         "         = %llu)",
38113                         task,
38114                         task,
38115                         task);
38116     }
38117   else if (nvt)
38118     {
38119       result_clause = g_strdup_printf
38120                        (" AND (overrides.nvt = (SELECT oid FROM nvts"
38121                        "                        WHERE nvts.id = %llu))",
38122                         nvt);
38123     }
38124   else
38125     result_clause = NULL;
38126 
38127   ret = init_get_iterator (iterator,
38128                            "override",
38129                            get,
38130                            columns,
38131                            trash_columns,
38132                            filter_columns,
38133                            task || nvt,
38134                            NULL,
38135                            result_clause,
38136                            TRUE);
38137 
38138   g_free (result_clause);
38139 
38140   return ret;
38141 }
38142 
38143 /**
38144  * @brief Get the NVT OID from a override iterator.
38145  *
38146  * @param[in]  iterator  Iterator.
38147  *
38148  * @return NVT OID, or NULL if iteration is complete.  Freed by
38149  *         cleanup_iterator.
38150  */
38151 DEF_ACCESS (override_iterator_nvt_oid, GET_ITERATOR_COLUMN_COUNT);
38152 
38153 /**
38154  * @brief Get the text from a override iterator.
38155  *
38156  * @param[in]  iterator  Iterator.
38157  *
38158  * @return Text, or NULL if iteration is complete.  Freed by
38159  *         cleanup_iterator.
38160  */
38161 DEF_ACCESS (override_iterator_text, GET_ITERATOR_COLUMN_COUNT + 1);
38162 
38163 /**
38164  * @brief Get the hosts from a override iterator.
38165  *
38166  * @param[in]  iterator  Iterator.
38167  *
38168  * @return Hosts, or NULL if iteration is complete.  Freed by
38169  *         cleanup_iterator.
38170  */
38171 DEF_ACCESS (override_iterator_hosts, GET_ITERATOR_COLUMN_COUNT + 2);
38172 
38173 /**
38174  * @brief Get the port from a override iterator.
38175  *
38176  * @param[in]  iterator  Iterator.
38177  *
38178  * @return Port, or NULL if iteration is complete.  Freed by
38179  *         cleanup_iterator.
38180  */
38181 DEF_ACCESS (override_iterator_port, GET_ITERATOR_COLUMN_COUNT + 3);
38182 
38183 /**
38184  * @brief Get the threat from a override iterator.
38185  *
38186  * @param[in]  iterator  Iterator.
38187  *
38188  * @return Threat.
38189  */
38190 const char *
override_iterator_threat(iterator_t * iterator)38191 override_iterator_threat (iterator_t *iterator)
38192 {
38193   const char *ret;
38194   if (iterator->done) return NULL;
38195   ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
38196   return ret;
38197 }
38198 
38199 /**
38200  * @brief Get the threat from an override iterator.
38201  *
38202  * @param[in]  iterator  Iterator.
38203  *
38204  * @return Threat.
38205  */
38206 const char *
override_iterator_new_threat(iterator_t * iterator)38207 override_iterator_new_threat (iterator_t *iterator)
38208 {
38209   const char *ret;
38210   if (iterator->done) return NULL;
38211   ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
38212   return ret;
38213 }
38214 
38215 /**
38216  * @brief Get the task from a override iterator.
38217  *
38218  * @param[in]  iterator  Iterator.
38219  *
38220  * @return The task associated with the override, or 0 on error.
38221  */
38222 task_t
override_iterator_task(iterator_t * iterator)38223 override_iterator_task (iterator_t* iterator)
38224 {
38225   if (iterator->done) return 0;
38226   return (task_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
38227 }
38228 
38229 /**
38230  * @brief Get the result from a override iterator.
38231  *
38232  * @param[in]  iterator  Iterator.
38233  *
38234  * @return The result associated with the override, or 0 on error.
38235  */
38236 result_t
override_iterator_result(iterator_t * iterator)38237 override_iterator_result (iterator_t* iterator)
38238 {
38239   if (iterator->done) return 0;
38240   return (result_t) iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 7);
38241 }
38242 
38243 /**
38244  * @brief Get the end time from an override iterator.
38245  *
38246  * @param[in]  iterator  Iterator.
38247  *
38248  * @return Time until which override applies.  0 for always.  1 means the
38249  *         override has been explicitly turned off.
38250  */
38251 time_t
override_iterator_end_time(iterator_t * iterator)38252 override_iterator_end_time (iterator_t* iterator)
38253 {
38254   int ret;
38255   if (iterator->done) return -1;
38256   ret = (time_t) iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
38257   return ret;
38258 }
38259 
38260 /**
38261  * @brief Get the active status from an override iterator.
38262  *
38263  * @param[in]  iterator  Iterator.
38264  *
38265  * @return 1 if active, else 0.
38266  */
38267 int
override_iterator_active(iterator_t * iterator)38268 override_iterator_active (iterator_t* iterator)
38269 {
38270   int ret;
38271   if (iterator->done) return -1;
38272   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
38273   return ret;
38274 }
38275 
38276 /**
38277  * @brief Get the NVT name from a override iterator.
38278  *
38279  * @param[in]  iterator  Iterator.
38280  *
38281  * @return NVT name, or NULL if iteration is complete.  Freed by
38282  *         cleanup_iterator.
38283  */
38284 DEF_ACCESS (override_iterator_nvt_name, GET_ITERATOR_COLUMN_COUNT + 10);
38285 
38286 /**
38287  * @brief Get the NVT type from a override iterator.
38288  *
38289  * @param[in]  iterator  Iterator.
38290  *
38291  * @return NVT type, or NULL.  Static string.
38292  */
38293 const char *
override_iterator_nvt_type(iterator_t * iterator)38294 override_iterator_nvt_type (iterator_t *iterator)
38295 {
38296   const char *oid;
38297 
38298   oid = override_iterator_nvt_oid (iterator);
38299   if (oid == NULL)
38300     return NULL;
38301 
38302   if (g_str_has_prefix (oid, "CVE-"))
38303     return "cve";
38304 
38305   if (g_str_has_prefix (oid, "oval:"))
38306     return "ovaldef";
38307 
38308   return "nvt";
38309 }
38310 
38311 /**
38312  * @brief Get the severity from an override iterator.
38313  *
38314  * @param[in]  iterator  Iterator.
38315  *
38316  * @return The severity score to which the override applies or NULL if
38317  *         iteration is complete, Freed by cleanup_iterator.
38318  */
38319 DEF_ACCESS (override_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 14);
38320 
38321 /**
38322  * @brief Get the new severity from an override iterator.
38323  *
38324  * @param[in]  iterator  Iterator.
38325  *
38326  * @return The severity score to override to or NULL if
38327  *         iteration is complete, Freed by cleanup_iterator.
38328  */
38329 DEF_ACCESS (override_iterator_new_severity, GET_ITERATOR_COLUMN_COUNT + 15);
38330 
38331 
38332 /* Scanners */
38333 
38334 /**
38335  * @brief Create the given scanner.
38336  *
38337  * @param[in]  log_config       Log configuration.
38338  * @param[in]  database         Location of manage database.
38339  * @param[in]  name             Name of scanner.
38340  * @param[in]  host             Host of scanner.
38341  * @param[in]  port             Port of scanner.
38342  * @param[in]  type             Type of scanner.
38343  * @param[in]  ca_pub_path      CA Certificate path.
38344  * @param[in]  credential_id    UUID of credential to use or NULL to create.
38345  * @param[in]  key_pub_path     Certificate path.
38346  * @param[in]  key_priv_path    Private key path.
38347  *
38348  * @return 0 success, -1 error, -2 database is wrong version, -3 database needs
38349  *         to be initialised from server.
38350  */
38351 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)38352 manage_create_scanner (GSList *log_config, const db_conn_info_t *database,
38353                        const char *name, const char *host, const char *port,
38354                        const char *type, const char *ca_pub_path,
38355                        const char *credential_id,
38356                        const char *key_pub_path, const char *key_priv_path)
38357 {
38358   int ret;
38359   char *ca_pub, *key_pub, *key_priv;
38360   GError *error = NULL;
38361   credential_t new_credential;
38362   gchar *used_credential_id;
38363   gchar *name_for_credential;
38364   scanner_t scanner;
38365 
38366   g_info ("   Creating scanner.");
38367 
38368   ret = manage_option_setup (log_config, database);
38369   if (ret)
38370     return ret;
38371 
38372   current_credentials.uuid = "";
38373 
38374   if (!g_file_get_contents (ca_pub_path, &ca_pub, NULL, &error))
38375     {
38376       fprintf (stderr, "%s.\n", error->message);
38377       g_error_free (error);
38378       manage_option_cleanup ();
38379       return -1;
38380     }
38381 
38382   if (credential_id)
38383     {
38384       key_pub = NULL;
38385       key_priv = NULL;
38386       used_credential_id = g_strdup (credential_id);
38387     }
38388   else
38389     {
38390       if (!g_file_get_contents (key_pub_path, &key_pub, NULL, &error))
38391         {
38392           fprintf (stderr, "%s.\n", error->message);
38393           g_error_free (error);
38394           g_free (ca_pub);
38395           manage_option_cleanup ();
38396           return -1;
38397         }
38398       if (!g_file_get_contents (key_priv_path, &key_priv, NULL, &error))
38399         {
38400           fprintf (stderr, "%s.\n", error->message);
38401           g_error_free (error);
38402           g_free (ca_pub);
38403           g_free (key_pub);
38404           manage_option_cleanup ();
38405           return -1;
38406         }
38407 
38408       name_for_credential = sql_quote (name);
38409 
38410       if (sql_int ("SELECT count(*) FROM credentials"
38411                   " WHERE name = 'Credential for Scanner %s'"
38412                   "   AND owner = NULL;",
38413                   name_for_credential))
38414         sql ("INSERT INTO credentials"
38415             " (uuid, name, owner, comment, type,"
38416             "  creation_time, modification_time)"
38417             " VALUES"
38418             " (make_uuid (),"
38419             "  uniquify ('scanner', 'Credential for Scanner %s',"
38420             "            NULL, ''),"
38421             "  NULL, 'Autogenerated', 'cc',"
38422             "  m_now (), m_now ());",
38423             name_for_credential);
38424       else
38425         sql ("INSERT INTO credentials"
38426             " (uuid, name, owner, comment, type,"
38427             "  creation_time, modification_time)"
38428             " VALUES"
38429             " (make_uuid (), 'Credential for Scanner %s',"
38430             "  NULL, 'Autogenerated', 'cc',"
38431             "  m_now (), m_now ());",
38432             name_for_credential);
38433 
38434       g_free (name_for_credential);
38435 
38436       new_credential = sql_last_insert_id();
38437 
38438       set_credential_data (new_credential, "certificate", key_pub);
38439 
38440       if (disable_encrypted_credentials)
38441         {
38442           set_credential_data (new_credential, "private_key", key_priv);
38443         }
38444       else
38445         {
38446           lsc_crypt_ctx_t crypt_ctx;
38447           char *secret;
38448 
38449           crypt_ctx = lsc_crypt_new ();
38450 
38451           secret = lsc_crypt_encrypt (crypt_ctx,
38452                                       "private_key", key_priv, NULL);
38453           if (!secret)
38454             {
38455               fprintf (stderr, "Failed to encrypt private key.\n");
38456               g_free (ca_pub);
38457               g_free (key_pub);
38458               g_free (key_priv);
38459               manage_option_cleanup ();
38460               return -1;
38461             }
38462           set_credential_data (new_credential, "secret", secret);
38463         }
38464 
38465       used_credential_id = credential_uuid (new_credential);
38466     }
38467   ret = create_scanner (name, NULL, host, port, type, &scanner, ca_pub,
38468                         used_credential_id);
38469   g_free (ca_pub);
38470   g_free (key_pub);
38471   g_free (key_priv);
38472   g_free (used_credential_id);
38473   switch (ret)
38474     {
38475       case 0:
38476         {
38477           gchar *uuid;
38478 
38479           uuid = sql_string ("SELECT uuid FROM scanners WHERE id = %llu;",
38480                              scanner);
38481           add_role_permission_resource (ROLE_UUID_ADMIN, "GET_SCANNERS",
38482                                         "scanner", uuid);
38483           add_role_permission_resource (ROLE_UUID_GUEST, "GET_SCANNERS",
38484                                         "scanner", uuid);
38485           add_role_permission_resource (ROLE_UUID_OBSERVER, "GET_SCANNERS",
38486                                         "scanner", uuid);
38487           add_role_permission_resource (ROLE_UUID_USER, "GET_SCANNERS",
38488                                         "scanner", uuid);
38489           g_free (uuid);
38490         }
38491 
38492         printf ("Scanner created.\n");
38493         break;
38494       case 1:
38495         fprintf (stderr, "Scanner exists already.\n");
38496         break;
38497       case 2:
38498         fprintf (stderr, "Invalid value provided.\n");
38499         break;
38500       case 3:
38501         fprintf (stderr, "Credential not found.\n");
38502         break;
38503       case 4:
38504         fprintf (stderr, "Credential should be 'up'.\n");
38505         break;
38506       case 5:
38507         fprintf (stderr, "Credential should be 'cc'.\n");
38508         break;
38509       case 6:
38510         fprintf (stderr, "Credential required.\n");
38511         break;
38512       default:
38513         fprintf (stderr, "Failed to create scanner.\n");
38514         break;
38515     }
38516 
38517   manage_option_cleanup ();
38518 
38519   return ret;
38520 }
38521 
38522 /**
38523  * @brief Delete the given scanner.
38524  *
38525  * @param[in]  log_config  Log configuration.
38526  * @param[in]  database    Location of manage database.
38527  * @param[in]  uuid        UUID of scanner.
38528  *
38529  * @return 0 success, 2 failed to find scanner, 3 scanner can't be deleted,
38530  *         -1 error.  -2 database is wrong version, -3 database needs to be
38531  *         initialised from server.
38532  */
38533 int
manage_delete_scanner(GSList * log_config,const db_conn_info_t * database,const gchar * uuid)38534 manage_delete_scanner (GSList *log_config, const db_conn_info_t *database,
38535                        const gchar *uuid)
38536 {
38537   int ret;
38538 
38539   assert (uuid);
38540 
38541   g_info ("   Deleting scanner.");
38542 
38543   if (!strcmp (uuid, SCANNER_UUID_CVE))
38544     {
38545       fprintf (stderr, "Default CVE Scanner can't be deleted.\n");
38546       return 3;
38547     }
38548 
38549   if (!strcmp (uuid, SCANNER_UUID_DEFAULT))
38550     {
38551       fprintf (stderr, "Default OpenVAS Scanner can't be deleted.\n");
38552       return 3;
38553     }
38554 
38555   ret = manage_option_setup (log_config, database);
38556   if (ret)
38557     return ret;
38558 
38559   current_credentials.uuid = "";
38560   switch ((ret = delete_scanner (uuid, 1)))
38561     {
38562       case 0:
38563         printf ("Scanner deleted.\n");
38564         break;
38565       case 1:
38566         fprintf (stderr, "Scanner in use.\n");
38567         break;
38568       case 2:
38569         fprintf (stderr, "Failed to find scanner.\n");
38570         break;
38571       case 3:
38572         fprintf (stderr, "Scanner is predefined.\n");
38573         break;
38574       default:
38575         fprintf (stderr, "Internal Error.\n");
38576         break;
38577     }
38578   current_credentials.uuid = NULL;
38579 
38580   manage_option_cleanup ();
38581   return ret;
38582 }
38583 
38584 /**
38585  * @brief Modify the given scanner.
38586  *
38587  * @param[in]  log_config       Log configuration.
38588  * @param[in]  database         Location of manage database.
38589  * @param[in]  scanner_id       ID of scanner.
38590  * @param[in]  name             Name of scanner.
38591  * @param[in]  host             Host of scanner.
38592  * @param[in]  port             Port of scanner.
38593  * @param[in]  type             Type of scanner.
38594  * @param[in]  ca_pub_path      CA Certificate path.  NULL to leave it as is.
38595  *                              "" to use the default.
38596  * @param[in]  credential_id    UUID of credential to use or NULL to create.
38597  * @param[in]  key_pub_path     Certificate path.
38598  * @param[in]  key_priv_path    Private key path.
38599  *
38600  * @return 0 success, , 1 failed to find scanner, 2 scanner with new name
38601  *         exists, 3 scanner_id required, 4 invalid value, 99 permission
38602  *         denied, -1 error, -2 database is wrong version, -3 database needs
38603  *         to be initialised from server.
38604  */
38605 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)38606 manage_modify_scanner (GSList *log_config, const db_conn_info_t *database,
38607                        const char *scanner_id, const char *name,
38608                        const char *host, const char *port,
38609                        const char *type, const char *ca_pub_path,
38610                        const char *credential_id,
38611                        const char *key_pub_path, const char *key_priv_path)
38612 {
38613   int ret;
38614   char *ca_pub, *key_pub, *key_priv;
38615   GError *error = NULL;
38616   scanner_t scanner;
38617   credential_t new_credential;
38618   gchar *used_credential_id;
38619   gchar *name_for_credential;
38620 
38621   g_info ("   Modifying scanner.");
38622 
38623   ret = manage_option_setup (log_config, database);
38624   if (ret)
38625     return ret;
38626 
38627   current_credentials.uuid = "";
38628 
38629   if (scanner_id)
38630     {
38631       /* Because the credentials are empty this will find the scanner regardless
38632        * of permissions and ownership, but that's the intention. */
38633       if (find_scanner_with_permission (scanner_id, &scanner, "get_scanners"))
38634         {
38635           fprintf (stderr, "Error finding scanner.\n");
38636           manage_option_cleanup ();
38637           return -1;
38638         }
38639       if (scanner == 0)
38640         {
38641           fprintf (stderr, "Failed to find scanner %s.\n", scanner_id);
38642           manage_option_cleanup ();
38643           return 1;
38644         }
38645     }
38646   else
38647     {
38648       fprintf (stderr, "Scanner UUID required.\n");
38649       manage_option_cleanup ();
38650       return 3;
38651     }
38652 
38653   if (name)
38654     name_for_credential = sql_quote (name);
38655   else
38656     {
38657       gchar *current_scanner_name = scanner_name (scanner);
38658       name_for_credential = sql_quote (current_scanner_name);
38659       g_free (current_scanner_name);
38660     }
38661 
38662   if (ca_pub_path)
38663     {
38664       if (*ca_pub_path == '\0')
38665         ca_pub = g_strdup ("");
38666       else if (g_file_get_contents (ca_pub_path, &ca_pub, NULL, &error) == 0)
38667         {
38668           fprintf (stderr, "%s.\n", error->message);
38669           g_error_free (error);
38670           manage_option_cleanup ();
38671           return -1;
38672         }
38673     }
38674   else
38675     ca_pub = NULL;
38676 
38677   if (credential_id)
38678     {
38679       key_pub = NULL;
38680       key_priv = NULL;
38681       used_credential_id = g_strdup (credential_id);
38682     }
38683   else
38684     {
38685       if (key_pub_path)
38686         {
38687           if (g_file_get_contents (key_pub_path, &key_pub, NULL, &error) == 0)
38688             {
38689               fprintf (stderr, "%s.\n", error->message);
38690               g_error_free (error);
38691               g_free (ca_pub);
38692               manage_option_cleanup ();
38693               return -1;
38694             }
38695         }
38696       else
38697         key_pub = scanner_key_pub (scanner);
38698 
38699       if (key_priv_path)
38700         {
38701           if (!g_file_get_contents (key_priv_path, &key_priv, NULL, &error))
38702             {
38703               fprintf (stderr, "%s.\n", error->message);
38704               g_error_free (error);
38705               g_free (ca_pub);
38706               g_free (key_pub);
38707               manage_option_cleanup ();
38708               return -1;
38709             }
38710         }
38711       else
38712         key_priv = scanner_key_priv (scanner);
38713 
38714       if (key_priv || key_pub)
38715         {
38716           if (sql_int ("SELECT count(*) FROM credentials"
38717                       " WHERE name = 'Credential for Scanner %s'"
38718                       "   AND owner IS NULL;",
38719                       name_for_credential))
38720             sql ("INSERT INTO credentials"
38721                 " (uuid, name, owner, comment, type,"
38722                 "  creation_time, modification_time)"
38723                 " VALUES"
38724                 " (make_uuid (),"
38725                 "  uniquify ('scanner', 'Credential for Scanner %s',"
38726                 "            NULL, ''),"
38727                 "  NULL, 'Autogenerated', 'cc',"
38728                 "  m_now (), m_now ());",
38729                 name_for_credential);
38730           else
38731             sql ("INSERT INTO credentials"
38732                 " (uuid, name, owner, comment, type,"
38733                 "  creation_time, modification_time)"
38734                 " VALUES"
38735                 " (make_uuid (), 'Credential for Scanner %s',"
38736                 "  NULL, 'Autogenerated', 'cc',"
38737                 "  m_now (), m_now ());",
38738                 name_for_credential);
38739 
38740           g_free (name_for_credential);
38741           new_credential = sql_last_insert_id();
38742           set_credential_data (new_credential, "certificate", key_pub);
38743 
38744           if (disable_encrypted_credentials)
38745             {
38746               set_credential_data (new_credential, "private_key", key_priv);
38747             }
38748           else
38749             {
38750               lsc_crypt_ctx_t crypt_ctx;
38751               char *secret;
38752 
38753               crypt_ctx = lsc_crypt_new ();
38754 
38755               secret = lsc_crypt_encrypt (crypt_ctx,
38756                                           "private_key", key_priv, NULL);
38757               if (!secret)
38758                 {
38759                   fprintf (stderr, "Failed to encrypt private key.\n");
38760                   g_free (ca_pub);
38761                   g_free (key_pub);
38762                   g_free (key_priv);
38763                   manage_option_cleanup ();
38764                   return -1;
38765                 }
38766               set_credential_data (new_credential, "secret", secret);
38767             }
38768           used_credential_id = credential_uuid (new_credential);
38769         }
38770       else
38771         used_credential_id = NULL;
38772     }
38773   ret = modify_scanner (scanner_id, name, NULL, host, port, type, ca_pub,
38774                         used_credential_id);
38775   g_free (ca_pub);
38776   g_free (key_pub);
38777   g_free (key_priv);
38778   g_free (used_credential_id);
38779   switch (ret)
38780     {
38781       case 0:
38782         printf ("Scanner modified.\n");
38783         break;
38784       case 2:
38785         fprintf (stderr, "Scanner with new name exists already.\n");
38786         break;
38787       case 3:
38788         fprintf (stderr, "Scanner ID required.\n");
38789         break;
38790       case 4:
38791         fprintf (stderr, "Invalid value.\n");
38792         break;
38793       case 5:
38794         fprintf (stderr, "Credential not found.\n");
38795         break;
38796       case 6:
38797         fprintf (stderr, "Credential should be 'cc'.\n");
38798         break;
38799       case 7:
38800         fprintf (stderr, "Credential should be 'up'.\n");
38801         break;
38802       case 8:
38803         fprintf (stderr, "Credential missing.\n");
38804         break;
38805       case 99:
38806         fprintf (stderr, "Permission denied.\n");
38807         break;
38808       default:
38809         fprintf (stderr, "Failed to modify scanner.\n");
38810         break;
38811     }
38812 
38813   manage_option_cleanup ();
38814 
38815   return ret;
38816 }
38817 
38818 /**
38819  * @brief Verify the given scanner.
38820  *
38821  * @param[in]  log_config  Log configuration.
38822  * @param[in]  database    Location of manage database.
38823  * @param[in]  uuid        UUID of scanner.
38824  *
38825  * @return 0 success, 1 failed to find scanner, 2 failed to verify scanner,
38826  *         -1 error.  -2 database is wrong version, -3 database needs to be
38827  *         initialised from server.
38828  */
38829 int
manage_verify_scanner(GSList * log_config,const db_conn_info_t * database,const gchar * uuid)38830 manage_verify_scanner (GSList *log_config, const db_conn_info_t *database,
38831                        const gchar *uuid)
38832 {
38833   int ret;
38834   char *version;
38835 
38836   assert (uuid);
38837 
38838   g_info ("   Verifying scanner.");
38839 
38840   ret = manage_option_setup (log_config, database);
38841   if (ret)
38842     return ret;
38843 
38844   current_credentials.uuid = "";
38845   switch ((ret = verify_scanner (uuid, &version)))
38846     {
38847       case 0:
38848         printf ("Scanner version: %s.\n", version);
38849         g_free (version);
38850         break;
38851       case 1:
38852         fprintf (stderr, "Failed to find scanner.\n");
38853         break;
38854       case 2:
38855         fprintf (stderr, "Failed to verify scanner.\n");
38856         break;
38857       case 3:
38858         fprintf (stderr, "Failed to authenticate. Scanner version: %s\n",
38859                  version);
38860         break;
38861       default:
38862         fprintf (stderr, "Internal Error.\n");
38863         break;
38864     }
38865   current_credentials.uuid = NULL;
38866 
38867   manage_option_cleanup ();
38868   return ret;
38869 }
38870 
38871 /**
38872  * @brief Find a scanner for a specific permission, given a UUID.
38873  *
38874  * @param[in]   uuid        UUID of scanner.
38875  * @param[out]  scanner     Scanner return, 0 if successfully failed to find
38876  *                          scanner.
38877  * @param[in]   permission  Permission.
38878  *
38879  * @return FALSE on success (including if failed to find scanner),
38880  *         TRUE on error.
38881  */
38882 gboolean
find_scanner_with_permission(const char * uuid,scanner_t * scanner,const char * permission)38883 find_scanner_with_permission (const char* uuid, scanner_t* scanner,
38884                               const char *permission)
38885 {
38886   return find_resource_with_permission ("scanner", uuid, scanner, permission,
38887                                         0);
38888 }
38889 
38890 /**
38891  * @brief Insert a scanner for create_scanner.
38892  *
38893  * @param[in]   name         Name of scanner.
38894  * @param[in]   comment      Comment on scanner.
38895  * @param[in]   host         Host of scanner.
38896  * @param[in]   ca_pub       CA Certificate for scanner.
38897  * @param[in]   iport        Port of scanner.
38898  * @param[in]   itype        Type of scanner.
38899  * @param[out]  new_scanner  The created scanner.
38900  */
38901 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)38902 insert_scanner (const char* name, const char *comment, const char *host,
38903                 const char *ca_pub, int iport, int itype,
38904                 scanner_t *new_scanner)
38905 {
38906   char *quoted_name, *quoted_comment, *quoted_host, *quoted_ca_pub;
38907 
38908   assert (current_credentials.uuid);
38909 
38910   quoted_name = sql_quote (name ?: "");
38911   quoted_comment = sql_quote (comment ?: "");
38912   quoted_host = sql_quote (host ?: "");
38913   quoted_ca_pub = sql_quote (ca_pub ?: "");
38914 
38915   sql ("INSERT INTO scanners (uuid, name, owner, comment, host, port, type,"
38916        "                      ca_pub,creation_time, modification_time)"
38917        " VALUES (make_uuid (), '%s',"
38918        "  (SELECT id FROM users WHERE users.uuid = '%s'),"
38919        "  '%s', '%s', %d, %d, %s%s%s, m_now (), m_now ());",
38920        quoted_name, current_credentials.uuid, quoted_comment, quoted_host,
38921        iport, itype,
38922        ca_pub ? "'" : "",
38923        ca_pub ? quoted_ca_pub : "NULL",
38924        ca_pub ? "'" : "");
38925 
38926   g_free (quoted_host);
38927   g_free (quoted_comment);
38928   g_free (quoted_name);
38929   g_free (quoted_ca_pub);
38930 
38931   if (new_scanner)
38932     *new_scanner = sql_last_insert_id ();
38933 }
38934 
38935 /**
38936  * @brief Create a scanner.
38937  *
38938  * @param[in]   name        Name of scanner.
38939  * @param[in]   comment     Comment on scanner.
38940  * @param[in]   host        Host of scanner.
38941  * @param[in]   port        Port of scanner.
38942  * @param[in]   type        Type of scanner.
38943  * @param[out]  new_scanner    The created scanner.
38944  * @param[in]   ca_pub         CA Certificate for scanner.
38945  * @param[in]   credential_id  ID of credential for scanner.
38946  *
38947  * @return 0 success, 1 scanner exists already, 2 Invalid value,
38948  *         3 credential not found, 4 credential should be 'up',
38949  *         5 credential should be 'cc', 6 credential required,
38950  *         99 permission denied.
38951  */
38952 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)38953 create_scanner (const char* name, const char *comment, const char *host,
38954                 const char *port, const char *type, scanner_t *new_scanner,
38955                 const char *ca_pub, const char *credential_id)
38956 {
38957   int iport, itype, unix_socket = 0;
38958   credential_t credential;
38959 
38960   assert (name);
38961 
38962   sql_begin_immediate ();
38963 
38964   if (acl_user_may ("create_scanner") == 0)
38965     {
38966       sql_rollback ();
38967       return 99;
38968     }
38969 
38970   if (!host || !port || !type)
38971     return 2;
38972   if (*host == '/')
38973     {
38974       unix_socket = 1;
38975       ca_pub = NULL;
38976     }
38977   iport = atoi (port);
38978   itype = atoi (type);
38979   if (iport <= 0 || iport > 65535)
38980     return 2;
38981   if (scanner_type_valid (itype) == 0)
38982     return 2;
38983   /* XXX: Workaround for unix socket case. */
38984   if (gvm_get_host_type (host) == -1 && !unix_socket)
38985     return 2;
38986   if (resource_with_name_exists (name, "scanner", 0))
38987     {
38988       sql_rollback ();
38989       return 1;
38990     }
38991 
38992   if (unix_socket)
38993     insert_scanner (name, comment, host, ca_pub, iport, itype, new_scanner);
38994   else
38995     {
38996       credential = 0;
38997       if (credential_id
38998           && strcmp (credential_id, "")
38999           && strcmp (credential_id, "0"))
39000         {
39001           if (find_credential_with_permission
39002               (credential_id, &credential, "get_credentials"))
39003             {
39004               sql_rollback ();
39005               return -1;
39006             }
39007           if (credential == 0)
39008             {
39009               sql_rollback ();
39010               return 3;
39011             }
39012           if (sql_int ("SELECT type != 'cc' FROM credentials"
39013                        " WHERE id = %llu;",
39014                        credential))
39015             {
39016               sql_rollback ();
39017               return 5;
39018             }
39019         }
39020 
39021       insert_scanner (name, comment, host, ca_pub, iport, itype, new_scanner);
39022 
39023       if (credential)
39024         {
39025           sql ("UPDATE scanners SET credential = %llu WHERE id = %llu;",
39026               credential, sql_last_insert_id ());
39027         }
39028     }
39029 
39030   sql_commit ();
39031   return 0;
39032 }
39033 
39034 /**
39035  * @brief Create a scanner from an existing scanner.
39036  *
39037  * @param[in]  name         Name of new scanner. NULL to copy from existing.
39038  * @param[in]  comment      Comment on new scanner. NULL to copy from
39039  *                          existing.
39040  * @param[in]  scanner_id   UUID of existing scanner.
39041  * @param[out] new_scanner  New scanner.
39042  *
39043  * @return 0 success, 1 scanner exists already, 2 failed to find existing
39044  *         scanner, -1 error, 98 not allowed to copy cve scanner,
39045  *         99 permission denied.
39046  */
39047 int
copy_scanner(const char * name,const char * comment,const char * scanner_id,scanner_t * new_scanner)39048 copy_scanner (const char* name, const char* comment, const char *scanner_id,
39049               scanner_t* new_scanner)
39050 {
39051   if (strcmp (scanner_id, SCANNER_UUID_CVE) == 0)
39052     return 98;
39053 
39054   return copy_resource ("scanner", name, comment, scanner_id,
39055                         "host, port, type, ca_pub, credential", 1,
39056                         new_scanner, NULL);
39057 }
39058 
39059 /**
39060  * @brief Modify an scanner.
39061  *
39062  * @param[in]   scanner_id  UUID of scanner.
39063  * @param[in]   name        Name of scanner.
39064  * @param[in]   comment     Comment on scanner.
39065  * @param[in]   host        Host of scanner.
39066  * @param[in]   port        Port of scanner.
39067  * @param[in]   type        Type of scanner.
39068  * @param[in]   ca_pub      CA Certificate of scanner, or "" for default, or
39069  *                          to keep existing value.
39070  * @param[in]   credential_id  UUID of credential or NULL.
39071  *
39072  * @return 0 success, 1 failed to find scanner, 2 scanner with new name exists,
39073  *         3 scanner_id required, 4 invalid value, 5 credential not found,
39074  *         6 credential should be 'cc', 7 credential should be 'up',
39075  *         8 credential missing, 99 permission denied, -1 internal error.
39076  */
39077 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)39078 modify_scanner (const char *scanner_id, const char *name, const char *comment,
39079                 const char *host, const char *port, const char *type,
39080                 const char *ca_pub, const char *credential_id)
39081 {
39082   gchar *quoted_name, *quoted_comment, *quoted_host, *new_port, *new_type;
39083   scanner_t scanner = 0;
39084   credential_t credential = 0;
39085   int iport, itype, unix_socket, credential_given;
39086 
39087   assert (current_credentials.uuid);
39088 
39089   if (scanner_id == NULL)
39090     return 3;
39091 
39092   if (port)
39093     {
39094       iport = atoi (port);
39095       if (iport <= 0 || iport > 65535)
39096         return 4;
39097     }
39098   else
39099     /* Keep compiler quiet. */
39100     iport = 0;
39101 
39102   if (type)
39103     {
39104       itype = atoi (type);
39105       if (scanner_type_valid (itype) == 0)
39106         return 4;
39107     }
39108   else
39109     itype = 0;
39110 
39111   sql_begin_immediate ();
39112 
39113   if (acl_user_may ("modify_scanner") == 0)
39114     {
39115       sql_rollback ();
39116       return 99;
39117     }
39118 
39119   if (find_scanner_with_permission (scanner_id, &scanner, "modify_scanner"))
39120     {
39121       sql_rollback ();
39122       return -1;
39123     }
39124   if (scanner == 0)
39125     {
39126       sql_rollback ();
39127       return 1;
39128     }
39129 
39130   if (host)
39131     {
39132       unix_socket = (*host == '/');
39133       if ((unix_socket == 0) && (gvm_get_host_type (host) == -1))
39134         return 4;
39135     }
39136   else
39137     {
39138       char *old_host = scanner_host (scanner);
39139       unix_socket = (*old_host == '/');
39140       g_free (old_host);
39141     }
39142 
39143   if (itype == 0)
39144     itype = sql_int ("SELECT type FROM scanners WHERE id = %llu;", scanner);
39145 
39146   if (credential_id
39147       && (strcmp (credential_id, "") == 0 || strcmp (credential_id, "0") == 0))
39148     {
39149       credential = 0;
39150       credential_given = 1;
39151     }
39152   else if (credential_id && !unix_socket)
39153     {
39154       if (find_credential_with_permission (credential_id, &credential,
39155                                            "get_credentials"))
39156         {
39157           sql_rollback ();
39158           return -1;
39159         }
39160 
39161       if (credential == 0)
39162         {
39163           sql_rollback ();
39164           return 5;
39165         }
39166 
39167       credential_given = 1;
39168     }
39169   else
39170     {
39171       credential = 0;
39172       credential_given = 1;
39173       sql_int64 (&credential,
39174                  "SELECT credential FROM scanners WHERE id = %llu;",
39175                  scanner);
39176     }
39177 
39178   if (credential)
39179     {
39180       if (sql_int ("SELECT type != 'cc' FROM credentials WHERE id = %llu;",
39181                    credential))
39182         {
39183           sql_rollback ();
39184           return 6;
39185         }
39186     }
39187 
39188   /* Check whether a scanner with the same name exists already. */
39189   if (name)
39190     {
39191       if (resource_with_name_exists (name, "scanner", scanner))
39192         {
39193           sql_rollback ();
39194           return 2;
39195         }
39196     }
39197 
39198   quoted_name = name ? sql_quote (name) : NULL;
39199   quoted_comment = sql_quote (comment ?: "");
39200   quoted_host = host ? sql_quote (host) : NULL;
39201   new_port = port ? g_strdup_printf ("%d", iport) : g_strdup ("port");
39202   new_type = type ? g_strdup_printf ("%d", itype) : g_strdup ("type");
39203   sql ("UPDATE scanners SET name = %s%s%s, comment = %s%s%s, host = %s%s%s,"
39204        " port = %s, type = %s, modification_time = m_now () WHERE id = %llu;",
39205        quoted_name ? "'" : "",
39206        quoted_name ? quoted_name : "name",
39207        quoted_name ? "'" : "",
39208        quoted_comment ? "'" : "",
39209        quoted_comment ? quoted_comment : "comment",
39210        quoted_comment ? "'" : "",
39211        quoted_host ? "'" : "",
39212        quoted_host ? quoted_host : "host",
39213        quoted_host ? "'" : "",
39214        new_port,
39215        new_type,
39216        scanner);
39217   g_free (new_type);
39218   g_free (new_port);
39219   g_free (quoted_host);
39220   g_free (quoted_comment);
39221   g_free (quoted_name);
39222 
39223   if (ca_pub && !unix_socket)
39224     {
39225       if (*ca_pub)
39226         {
39227           char *quoted_ca_pub = sql_quote (ca_pub);
39228           sql ("UPDATE scanners SET ca_pub = '%s' WHERE id = %llu;", quoted_ca_pub,
39229                scanner);
39230           g_free (quoted_ca_pub);
39231         }
39232       else
39233         /* Use default CA cert. */
39234         sql ("UPDATE scanners SET ca_pub = NULL WHERE id = %llu;", scanner);
39235     }
39236 
39237   if (credential_given)
39238     {
39239       if (credential)
39240         sql ("UPDATE scanners SET credential = %llu WHERE id = %llu;",
39241              credential, scanner);
39242       else
39243         sql ("UPDATE scanners SET credential = NULL WHERE id = %llu;",
39244              scanner);
39245     }
39246   sql_commit ();
39247   return 0;
39248 }
39249 
39250 /**
39251  * @brief Delete a scanner.
39252  *
39253  * @param[in]  scanner_id   UUID of scanner.
39254  * @param[in]  ultimate     Whether to remove entirely, or to trashcan.
39255  *
39256  * @return 0 success, 1 scanner in use, 2 failed to find scanner,
39257  *         3 predefined scanner, 99 permission denied, -1 error.
39258  */
39259 int
delete_scanner(const char * scanner_id,int ultimate)39260 delete_scanner (const char *scanner_id, int ultimate)
39261 {
39262   scanner_t scanner = 0;
39263 
39264   sql_begin_immediate ();
39265 
39266   if (acl_user_may ("delete_scanner") == 0)
39267     {
39268       sql_rollback ();
39269       return 99;
39270     }
39271 
39272   if (strcmp (scanner_id, SCANNER_UUID_CVE) == 0
39273       || strcmp (scanner_id, SCANNER_UUID_DEFAULT) == 0)
39274     return 3;
39275 
39276   if (find_scanner_with_permission (scanner_id, &scanner, "delete_scanner"))
39277     {
39278       sql_rollback ();
39279       return -1;
39280     }
39281 
39282   if (scanner == 0)
39283     {
39284       if (find_trash ("scanner", scanner_id, &scanner))
39285         {
39286           sql_rollback ();
39287           return -1;
39288         }
39289       if (scanner == 0)
39290         {
39291           sql_rollback ();
39292           return 2;
39293         }
39294       if (ultimate == 0)
39295         {
39296           /* It's already in the trashcan. */
39297           sql_commit ();
39298           return 0;
39299         }
39300 
39301       /* Check if it's in use by a config or task in the trashcan. */
39302       if (sql_int ("SELECT count(*) FROM tasks"
39303                    " WHERE scanner = %llu"
39304                    " AND scanner_location = " G_STRINGIFY (LOCATION_TRASH) ";",
39305                    scanner)
39306           || sql_int ("SELECT count(*) FROM configs_trash"
39307                       " WHERE scanner = %llu"
39308                       " AND scanner_location"
39309                       "      = " G_STRINGIFY (LOCATION_TRASH) ";",
39310                       scanner))
39311         {
39312           sql_rollback ();
39313           return 1;
39314         }
39315 
39316       permissions_set_orphans ("scanner", scanner, LOCATION_TRASH);
39317       tags_remove_resource ("scanner", scanner, LOCATION_TRASH);
39318 
39319       sql ("DELETE FROM scanners_trash WHERE id = %llu;", scanner);
39320       sql_commit ();
39321       return 0;
39322     }
39323 
39324   if (ultimate == 0)
39325     {
39326       scanner_t trash_scanner;
39327 
39328       if (sql_int ("SELECT count(*) FROM tasks"
39329                    " WHERE scanner = %llu"
39330                    " AND scanner_location = " G_STRINGIFY (LOCATION_TABLE)
39331                    " AND hidden = 0;",
39332                    scanner)
39333           || sql_int ("SELECT count(*) FROM configs"
39334                       " WHERE scanner = %llu;",
39335                       scanner))
39336         {
39337           sql_rollback ();
39338           return 1;
39339         }
39340 
39341       sql ("INSERT INTO scanners_trash"
39342            " (uuid, owner, name, comment, host, port, type, ca_pub,"
39343            "  credential, credential_location,"
39344            "  creation_time, modification_time)"
39345            " SELECT uuid, owner, name, comment, host, port, type, ca_pub,"
39346            "        credential, " G_STRINGIFY (LOCATION_TABLE) ","
39347            "        creation_time, modification_time"
39348            " FROM scanners WHERE id = %llu;", scanner);
39349 
39350       trash_scanner = sql_last_insert_id ();
39351 
39352       /* Update the location of the scanner in any trashcan configs & tasks. */
39353       sql ("UPDATE configs_trash"
39354            " SET scanner = %llu,"
39355            "     scanner_location = " G_STRINGIFY (LOCATION_TRASH)
39356            " WHERE scanner = %llu;",
39357            trash_scanner,
39358            scanner);
39359 
39360       sql ("UPDATE tasks"
39361            " SET scanner = %llu,"
39362            "     scanner_location = " G_STRINGIFY (LOCATION_TRASH)
39363            " WHERE scanner = %llu"
39364            " AND scanner_location = " G_STRINGIFY (LOCATION_TABLE) ";",
39365            trash_scanner,
39366            scanner);
39367 
39368       permissions_set_locations ("scanner", scanner, sql_last_insert_id (),
39369                                  LOCATION_TRASH);
39370       tags_set_locations ("scanner", scanner,
39371                           sql_last_insert_id (), LOCATION_TRASH);
39372     }
39373   else
39374     {
39375       permissions_set_orphans ("scanner", scanner, LOCATION_TABLE);
39376       tags_remove_resource ("scanner", scanner, LOCATION_TABLE);
39377     }
39378 
39379   sql ("DELETE FROM scanners WHERE id = %llu;", scanner);
39380   sql_commit ();
39381   return 0;
39382 }
39383 
39384 /**
39385  * @brief Filter columns for scanner iterator.
39386  */
39387 #define SCANNER_ITERATOR_FILTER_COLUMNS                              \
39388  { GET_ITERATOR_FILTER_COLUMNS, "host", "port", "type", NULL }
39389 
39390 /**
39391  * @brief Scanner iterator columns.
39392  */
39393 #define SCANNER_ITERATOR_COLUMNS                                     \
39394  {                                                                   \
39395    GET_ITERATOR_COLUMNS (scanners),                                  \
39396    { "host", NULL, KEYWORD_TYPE_STRING },                            \
39397    { "port", NULL, KEYWORD_TYPE_INTEGER },                           \
39398    { "type", NULL, KEYWORD_TYPE_INTEGER },                           \
39399    { "ca_pub", NULL, KEYWORD_TYPE_STRING },                          \
39400    {                                                                 \
39401      "(SELECT name FROM credentials WHERE id = credential)",         \
39402      "credential",                                                   \
39403      KEYWORD_TYPE_STRING                                             \
39404    },                                                                \
39405    { "credential", NULL, KEYWORD_TYPE_INTEGER },                     \
39406    { "0", NULL, KEYWORD_TYPE_INTEGER },                              \
39407    { "credential_value (credential, 0, CAST ('certificate' AS TEXT))", \
39408      NULL,                                                             \
39409      KEYWORD_TYPE_STRING },                                            \
39410    { "credential_value (credential, 0, CAST ('private_key' AS TEXT))", \
39411      NULL,                                                             \
39412      KEYWORD_TYPE_STRING },                                            \
39413    { "credential_value (credential, 0, CAST ('secret' AS TEXT))",      \
39414      NULL,                                                             \
39415      KEYWORD_TYPE_STRING },                                            \
39416    {                                                                   \
39417      "(SELECT type FROM credentials WHERE id = credential)",           \
39418      "credential_type",                                                \
39419      KEYWORD_TYPE_STRING                                               \
39420    },                                                                  \
39421    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                \
39422  }
39423 
39424 /**
39425  * @brief Scanner iterator columns for trash case.
39426  */
39427 #define SCANNER_ITERATOR_TRASH_COLUMNS                               \
39428  {                                                                   \
39429    GET_ITERATOR_COLUMNS (scanners_trash),                            \
39430    { "host" , NULL, KEYWORD_TYPE_STRING },                           \
39431    { "port" , NULL, KEYWORD_TYPE_INTEGER },                          \
39432    { "type", NULL, KEYWORD_TYPE_INTEGER },                           \
39433    { "ca_pub", NULL, KEYWORD_TYPE_STRING },                          \
39434    {                                                                    \
39435      "(SELECT CASE"                                                     \
39436      " WHEN credential_location = " G_STRINGIFY (LOCATION_TABLE)        \
39437      " THEN (SELECT name FROM credentials WHERE id = credential)"       \
39438      " ELSE (SELECT name FROM credentials_trash WHERE id = credential)" \
39439      " END)",                                                           \
39440      "credential",                                                      \
39441      KEYWORD_TYPE_STRING                                                \
39442    },                                                                   \
39443    { "credential", NULL, KEYWORD_TYPE_INTEGER },                        \
39444    { "credential_location", NULL, KEYWORD_TYPE_INTEGER },               \
39445    { "credential_value (credential, 1, CAST ('certificate' AS TEXT))",  \
39446      NULL,                                                              \
39447      KEYWORD_TYPE_STRING },                                             \
39448    { "credential_value (credential, 1, CAST ('private_key' AS TEXT))",  \
39449      NULL,                                                              \
39450      KEYWORD_TYPE_STRING },                                             \
39451    { "credential_value (credential, 1, CAST ('secret' AS TEXT))",       \
39452      NULL,                                                              \
39453      KEYWORD_TYPE_STRING },                                             \
39454    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                 \
39455  }
39456 
39457 /**
39458  * @brief Initialise an scanner iterator.
39459  *
39460  * @param[in]  iterator    Iterator.
39461  * @param[in]  get         GET data.
39462  *
39463  * @return 0 success, 1 failed to find scanner, 2 failed to find filter, -1 error.
39464  */
39465 int
init_scanner_iterator(iterator_t * iterator,const get_data_t * get)39466 init_scanner_iterator (iterator_t* iterator, const get_data_t *get)
39467 {
39468   static const char *filter_columns[] = SCANNER_ITERATOR_FILTER_COLUMNS;
39469   static column_t columns[] = SCANNER_ITERATOR_COLUMNS;
39470   static column_t trash_columns[] = SCANNER_ITERATOR_TRASH_COLUMNS;
39471 
39472   return init_get_iterator (iterator, "scanner", get, columns, trash_columns,
39473                             filter_columns, 0, NULL, NULL, TRUE);
39474 }
39475 
39476 /**
39477  * @brief Get the host from an scanner iterator.
39478  *
39479  * @param[in]  iterator  Iterator.
39480  *
39481  * @return Host, or NULL if iteration is complete.  Freed
39482  *         by cleanup_iterator.
39483  */
39484 DEF_ACCESS (scanner_iterator_host, GET_ITERATOR_COLUMN_COUNT);
39485 
39486 /**
39487  * @brief Get the port from an scanner iterator.
39488  *
39489  * @param[in]  iterator  Iterator.
39490  *
39491  * @return Port, or -1 if iteration is complete.
39492  */
39493 int
scanner_iterator_port(iterator_t * iterator)39494 scanner_iterator_port (iterator_t* iterator)
39495 {
39496   int ret;
39497   if (iterator->done) return -1;
39498   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
39499   return ret;
39500 }
39501 
39502 /**
39503  * @brief Get the type from an scanner iterator.
39504  *
39505  * @param[in]  iterator  Iterator.
39506  *
39507  * @return Type, or SCANNER_TYPE_NONE if iteration is complete.
39508  */
39509 int
scanner_iterator_type(iterator_t * iterator)39510 scanner_iterator_type (iterator_t* iterator)
39511 {
39512   int ret;
39513   if (iterator->done) return SCANNER_TYPE_NONE;
39514   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
39515   return ret;
39516 }
39517 
39518 /**
39519  * @brief Get the CA Certificate from a scanner iterator.
39520  *
39521  * @param[in]  iterator  Iterator.
39522  *
39523  * @return CA Certificate, or NULL if iteration is complete. Freed by
39524  *         cleanup_iterator.
39525  */
39526 DEF_ACCESS (scanner_iterator_ca_pub, GET_ITERATOR_COLUMN_COUNT + 3);
39527 
39528 /**
39529  * @brief Get the Credential name from a scanner iterator.
39530  *
39531  * @param[in]  iterator  Iterator.
39532  *
39533  * @return Credential name, or NULL if iteration is complete. Freed by
39534  *         cleanup_iterator.
39535  */
39536 DEF_ACCESS (scanner_iterator_credential_name, GET_ITERATOR_COLUMN_COUNT + 4);
39537 
39538 /**
39539  * @brief Get the credential of the scanner from a scanner iterator.
39540  *
39541  * @param[in]  iterator  Iterator.
39542  *
39543  * @return Credential of the scanner or 0 if iteration is complete.
39544  */
39545 credential_t
scanner_iterator_credential(iterator_t * iterator)39546 scanner_iterator_credential (iterator_t *iterator)
39547 {
39548   if (iterator->done)
39549     return 0;
39550   else
39551     return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
39552 }
39553 
39554 /**
39555  * @brief Get the credential location of the scanner from a scanner iterator.
39556  *
39557  * @param[in]  iterator  Iterator.
39558  *
39559  * @return Location of the credential or NULL if iteration is complete.
39560  */
39561 int
scanner_iterator_credential_trash(iterator_t * iterator)39562 scanner_iterator_credential_trash (iterator_t *iterator)
39563 {
39564   if (iterator->done)
39565     return 0;
39566   else
39567     return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
39568 }
39569 
39570 /**
39571  * @brief Get the Scanner Certificate from a scanner iterator.
39572  *
39573  * @param[in]  iterator  Iterator.
39574  *
39575  * @return Scanner Certificate, or NULL if iteration is complete. Freed by
39576  *         cleanup_iterator.
39577  */
39578 DEF_ACCESS (scanner_iterator_key_pub, GET_ITERATOR_COLUMN_COUNT + 7);
39579 
39580 /**
39581  * @brief Get the Scanner private key from a scanner iterator.
39582  *
39583  * @param[in]  iterator  Iterator.
39584  *
39585  * @return Scanner private key, or NULL if iteration is complete. Freed by
39586  *         cleanup_iterator.
39587  */
39588 static const char*
scanner_iterator_key_priv(iterator_t * iterator)39589 scanner_iterator_key_priv (iterator_t* iterator)
39590 {
39591   const char *private_key;
39592 
39593   private_key = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
39594 
39595   if (private_key == NULL)
39596     {
39597       const char *secret;
39598       if (!iterator->crypt_ctx)
39599         iterator->crypt_ctx = lsc_crypt_new ();
39600 
39601       secret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 9);
39602       private_key = lsc_crypt_get_private_key (iterator->crypt_ctx, secret);
39603     }
39604 
39605   return private_key;
39606 }
39607 
39608 /**
39609  * @brief Get the Credential type from a scanner iterator.
39610  *
39611  * @param[in]  iterator  Iterator.
39612  *
39613  * @return Credential type, or NULL if iteration is complete. Freed by
39614  *         cleanup_iterator.
39615  */
39616 DEF_ACCESS (scanner_iterator_credential_type, GET_ITERATOR_COLUMN_COUNT + 10);
39617 
39618 /**
39619  * @brief Initialise a scanner config iterator.
39620  *
39621  * @param[in]  iterator  Iterator.
39622  * @param[in]  scanner   Scanner.
39623  */
39624 void
init_scanner_config_iterator(iterator_t * iterator,scanner_t scanner)39625 init_scanner_config_iterator (iterator_t* iterator, scanner_t scanner)
39626 {
39627   gchar *available, *with_clause;
39628   get_data_t get;
39629   array_t *permissions;
39630 
39631   assert (scanner);
39632 
39633   get.trash = 0;
39634   permissions = make_array ();
39635   array_add (permissions, g_strdup ("get_configs"));
39636   available = acl_where_owned ("config", &get, 1, "any", 0, permissions, 0,
39637                                &with_clause);
39638   array_free (permissions);
39639 
39640   init_iterator (iterator,
39641                  "%s"
39642                  " SELECT id, uuid, name, %s FROM configs"
39643                  " WHERE scanner = %llu"
39644                  " ORDER BY name ASC;",
39645                  with_clause ? with_clause : "",
39646                  available,
39647                  scanner);
39648 
39649   g_free (with_clause);
39650   g_free (available);
39651 }
39652 
39653 /**
39654  * @brief Get the UUID from a scanner config iterator.
39655  *
39656  * @param[in]  iterator  Iterator.
39657  *
39658  * @return UUID, or NULL if iteration is complete. Freed by cleanup_iterator.
39659  */
39660 DEF_ACCESS (scanner_config_iterator_uuid, 1);
39661 
39662 /**
39663  * @brief Get the name from a scanner config iterator.
39664  *
39665  * @param[in]  iterator  Iterator.
39666  *
39667  * @return Name, or NULL if iteration is complete. Freed by cleanup_iterator.
39668  */
39669 DEF_ACCESS (scanner_config_iterator_name, 2);
39670 
39671 /**
39672  * @brief Get the read permission status from a GET iterator.
39673  *
39674  * @param[in]  iterator  Iterator.
39675  *
39676  * @return 1 if may read, else 0.
39677  */
39678 int
scanner_config_iterator_readable(iterator_t * iterator)39679 scanner_config_iterator_readable (iterator_t* iterator)
39680 {
39681   if (iterator->done) return 0;
39682   return iterator_int (iterator, 3);
39683 }
39684 
39685 /**
39686  * @brief Initialise a scanner task iterator.
39687  *
39688  * @param[in]  iterator  Iterator.
39689  * @param[in]  scanner   Scanner.
39690  */
39691 void
init_scanner_task_iterator(iterator_t * iterator,scanner_t scanner)39692 init_scanner_task_iterator (iterator_t* iterator, scanner_t scanner)
39693 {
39694   gchar *available, *with_clause;
39695   get_data_t get;
39696   array_t *permissions;
39697 
39698   assert (scanner);
39699 
39700   get.trash = 0;
39701   permissions = make_array ();
39702   array_add (permissions, g_strdup ("get_tasks"));
39703   available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
39704                                &with_clause);
39705   array_free (permissions);
39706 
39707   init_iterator (iterator,
39708                  "%s"
39709                  " SELECT id, uuid, name, %s FROM tasks"
39710                  " WHERE scanner = %llu AND hidden = 0"
39711                  " ORDER BY name ASC;",
39712                  with_clause ? with_clause : "",
39713                  available,
39714                  scanner);
39715 
39716   g_free (with_clause);
39717   g_free (available);
39718 }
39719 
39720 /**
39721  * @brief Get the UUID from a scanner task iterator.
39722  *
39723  * @param[in]  iterator  Iterator.
39724  *
39725  * @return UUID, or NULL if iteration is complete. Freed by cleanup_iterator.
39726  */
39727 DEF_ACCESS (scanner_task_iterator_uuid, 1);
39728 
39729 /**
39730  * @brief Get the name from a scanner task iterator.
39731  *
39732  * @param[in]  iterator  Iterator.
39733  *
39734  * @return Name, or NULL if iteration is complete. Freed by cleanup_iterator.
39735  */
39736 DEF_ACCESS (scanner_task_iterator_name, 2);
39737 
39738 /**
39739  * @brief Get the read permission status from a GET iterator.
39740  *
39741  * @param[in]  iterator  Iterator.
39742  *
39743  * @return 1 if may read, else 0.
39744  */
39745 int
scanner_task_iterator_readable(iterator_t * iterator)39746 scanner_task_iterator_readable (iterator_t* iterator)
39747 {
39748   if (iterator->done) return 0;
39749   return iterator_int (iterator, 3);
39750 }
39751 
39752 /**
39753  * @brief Check whether an scanner is in use.
39754  *
39755  * @param[in]  scanner  Scanner.
39756  *
39757  * @return 1 yes, 0 no.
39758  */
39759 int
scanner_in_use(scanner_t scanner)39760 scanner_in_use (scanner_t scanner)
39761 {
39762   return !!(sql_int ("SELECT count(*) FROM tasks WHERE scanner = %llu"
39763                      " AND hidden = 0;", scanner)
39764             || sql_int ("SELECT count(*) FROM configs WHERE scanner = %llu",
39765                         scanner));
39766 }
39767 
39768 /**
39769  * @brief Check whether a trashcan scanner is writable.
39770  *
39771  * @param[in]  scanner  Scanner.
39772  *
39773  * @return 1 yes, 0 no.
39774  */
39775 int
trash_scanner_in_use(scanner_t scanner)39776 trash_scanner_in_use (scanner_t scanner)
39777 {
39778   return !!(sql_int ("SELECT count(*) FROM tasks"
39779                      " WHERE scanner = %llu"
39780                      " AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
39781                      scanner)
39782             || sql_int ("SELECT count(*) FROM configs_trash"
39783                         " WHERE scanner = %llu"
39784                         " AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
39785                      scanner));
39786 }
39787 
39788 /**
39789  * @brief Check whether a scanner is writable.
39790  *
39791  * @param[in]  scanner  Scanner.
39792  *
39793  * @return 1 yes, 0 no.
39794  */
39795 int
scanner_writable(scanner_t scanner)39796 scanner_writable (scanner_t scanner)
39797 {
39798   return 1;
39799 }
39800 
39801 /**
39802  * @brief Check whether a trashcan scanner is writable.
39803  *
39804  * @param[in]  scanner  Scanner.
39805  *
39806  * @return 1 yes, 0 no.
39807  */
39808 int
trash_scanner_writable(scanner_t scanner)39809 trash_scanner_writable (scanner_t scanner)
39810 {
39811   return trash_scanner_in_use (scanner) == 0;
39812 }
39813 
39814 /**
39815  * @brief Return whether a trashcan scanner is readable.
39816  *
39817  * @param[in]  scanner  Scanner.
39818  *
39819  * @return 1 if readable, else 0.
39820  */
39821 int
trash_scanner_readable(scanner_t scanner)39822 trash_scanner_readable (scanner_t scanner)
39823 {
39824   char *uuid;
39825   scanner_t found = 0;
39826 
39827   if (scanner == 0)
39828     return 0;
39829   uuid = scanner_uuid (scanner);
39830   if (find_trash ("scanner", uuid, &found))
39831     {
39832       g_free (uuid);
39833       return 0;
39834     }
39835   g_free (uuid);
39836   return found > 0;
39837 }
39838 
39839 /**
39840  * @brief Return the name of a scanner.
39841  *
39842  * @param[in]  scanner  Scanner.
39843  *
39844  * @return Newly allocated name if available, else NULL.
39845  */
39846 char*
scanner_name(scanner_t scanner)39847 scanner_name (scanner_t scanner)
39848 {
39849   return sql_string ("SELECT name FROM scanners WHERE id = %llu;",
39850                      scanner);
39851 }
39852 
39853 /**
39854  * @brief Return the UUID of a scanner.
39855  *
39856  * @param[in]  scanner  Scanner.
39857  *
39858  * @return Newly allocated UUID.
39859  */
39860 char *
scanner_uuid(scanner_t scanner)39861 scanner_uuid (scanner_t scanner)
39862 {
39863   return sql_string ("SELECT uuid FROM scanners WHERE id = %llu;",
39864                      scanner);
39865 }
39866 
39867 /**
39868  * @brief Return the UUID of the default scanner.
39869  *
39870  * @return UUID.
39871  */
39872 const char *
scanner_uuid_default()39873 scanner_uuid_default ()
39874 {
39875   return SCANNER_UUID_DEFAULT;
39876 }
39877 
39878 /**
39879  * @brief Return the host of a scanner.
39880  *
39881  * @param[in]  scanner  Scanner.
39882  *
39883  * @return Newly allocated host.
39884  */
39885 char *
scanner_host(scanner_t scanner)39886 scanner_host (scanner_t scanner)
39887 {
39888   return sql_string ("SELECT host FROM scanners WHERE id = %llu;", scanner);
39889 }
39890 
39891 /**
39892  * @brief Return the port of a scanner.
39893  *
39894  * @param[in]  scanner  Scanner.
39895  *
39896  * @return Scanner port, -1 if not found;
39897  */
39898 int
scanner_port(scanner_t scanner)39899 scanner_port (scanner_t scanner)
39900 {
39901   int port;
39902   char *str;
39903   str = sql_string ("SELECT port FROM scanners WHERE id = %llu;", scanner);
39904   if (!str)
39905     return -1;
39906   port = atoi (str);
39907   g_free (str);
39908   return port;
39909 }
39910 
39911 /**
39912  * @brief Return the type of a scanner.
39913  *
39914  * @param[in]  scanner  Scanner.
39915  *
39916  * @return Scanner type, -1 if not found;
39917  */
39918 int
scanner_type(scanner_t scanner)39919 scanner_type (scanner_t scanner)
39920 {
39921   int type;
39922   char *str;
39923   str = sql_string ("SELECT type FROM scanners WHERE id = %llu;", scanner);
39924   if (!str)
39925     return -1;
39926   type = atoi (str);
39927   g_free (str);
39928   return type;
39929 }
39930 
39931 /**
39932  * @brief Return the CA Certificate of a scanner.
39933  *
39934  * @param[in]  scanner  Scanner.
39935  *
39936  * @return Newly allocated CA Certificate.
39937  */
39938 char *
scanner_ca_pub(scanner_t scanner)39939 scanner_ca_pub (scanner_t scanner)
39940 {
39941   return sql_string ("SELECT ca_pub FROM scanners WHERE id = %llu;", scanner);
39942 }
39943 
39944 /**
39945  * @brief Return the Certificate of a scanner.
39946  *
39947  * @param[in]  scanner  Scanner.
39948  *
39949  * @return Newly allocated Certificate.
39950  */
39951 char *
scanner_key_pub(scanner_t scanner)39952 scanner_key_pub (scanner_t scanner)
39953 {
39954   if (scanner == 0)
39955     return NULL;
39956 
39957   return sql_string ("SELECT value FROM credentials_data"
39958                      " WHERE credential = (SELECT credential FROM scanners"
39959                      "                     WHERE id = %llu)"
39960                      "   AND type = 'certificate';",
39961                      scanner);
39962 }
39963 
39964 /**
39965  * @brief Return the private key of a scanner.
39966  *
39967  * @param[in]  scanner  Scanner.
39968  *
39969  * @return Newly allocated private key.
39970  */
39971 char *
scanner_key_priv(scanner_t scanner)39972 scanner_key_priv (scanner_t scanner)
39973 {
39974   gchar *key;
39975 
39976   key = sql_string ("SELECT value FROM credentials_data"
39977                     " WHERE credential = (SELECT credential FROM scanners"
39978                     "                     WHERE id = %llu)"
39979                     "   AND type = 'private_key';",
39980                     scanner);
39981 
39982   if (key == NULL)
39983     {
39984       gchar *secret;
39985       lsc_crypt_ctx_t crypt_ctx;
39986       crypt_ctx = lsc_crypt_new ();
39987 
39988       secret = sql_string ("SELECT value FROM credentials_data"
39989                            " WHERE credential"
39990                            "         = (SELECT credential FROM scanners"
39991                            "            WHERE id = %llu)"
39992                            "   AND type = 'secret';",
39993                            scanner);
39994 
39995       key = g_strdup (lsc_crypt_get_private_key (crypt_ctx, secret));
39996       lsc_crypt_release (crypt_ctx);
39997       g_free (secret);
39998     }
39999 
40000   return key;
40001 }
40002 
40003 /**
40004  * @brief Return the login associated with a scanner.
40005  *
40006  * @param[in]  scanner  Scanner.
40007  *
40008  * @return Newly allocated login if available, else NULL.
40009  */
40010 char*
scanner_login(scanner_t scanner)40011 scanner_login (scanner_t scanner)
40012 {
40013   return sql_string ("SELECT credentials_data.value"
40014                      " FROM scanners, credentials_data"
40015                      " WHERE scanners.id = %llu"
40016                      "   AND credentials_data.credential = scanners.credential"
40017                      "   AND credentials_data.type = 'username';",
40018                      scanner);
40019 }
40020 
40021 /**
40022  * @brief Return the password associated with a scanner.
40023  *
40024  * @param[in]  scanner  Scanner.
40025  *
40026  * @return Newly allocated password if available, else NULL.
40027  */
40028 char*
scanner_password(scanner_t scanner)40029 scanner_password (scanner_t scanner)
40030 {
40031   gchar *password;
40032 
40033   password = sql_string ("SELECT credentials_data.value"
40034                          " FROM scanners, credentials_data"
40035                          " WHERE scanners.id = %llu"
40036                          "   AND credentials_data.credential"
40037                          "         = scanners.credential"
40038                          "   AND credentials_data.type = 'password';",
40039                          scanner);
40040 
40041   if (password == NULL)
40042     {
40043       gchar *secret;
40044       lsc_crypt_ctx_t crypt_ctx;
40045       crypt_ctx = lsc_crypt_new ();
40046 
40047       secret = sql_string ("SELECT credentials_data.value"
40048                            " FROM scanners, credentials_data"
40049                            " WHERE scanners.id = %llu"
40050                            "   AND credentials_data.credential"
40051                            "         = scanners.credential"
40052                            "   AND credentials_data.type = 'secret';",
40053                            scanner);
40054 
40055       password = g_strdup (lsc_crypt_get_password (crypt_ctx, secret));
40056       lsc_crypt_release (crypt_ctx);
40057       g_free (secret);
40058     }
40059 
40060   return password;
40061 }
40062 
40063 /**
40064  * @brief Return the name of a scanner in the trashcan.
40065  *
40066  * @param[in]  scanner  Scanner.
40067  *
40068  * @return Newly allocated name if available, else NULL.
40069  */
40070 char*
trash_scanner_name(scanner_t scanner)40071 trash_scanner_name (scanner_t scanner)
40072 {
40073   return sql_string ("SELECT name FROM scanners_trash WHERE id = %llu;",
40074                      scanner);
40075 }
40076 
40077 /**
40078  * @brief Return the UUID of a scanner in the trashcan.
40079  *
40080  * @param[in]  scanner  Scanner.
40081  *
40082  * @return Newly allocated UUID.
40083  */
40084 char *
trash_scanner_uuid(scanner_t scanner)40085 trash_scanner_uuid (scanner_t scanner)
40086 {
40087   return sql_string ("SELECT uuid FROM scanners_trash WHERE id = %llu;",
40088                      scanner);
40089 }
40090 
40091 /**
40092  * @brief Count number of scanners.
40093  *
40094  * @param[in]  get  GET params.
40095  *
40096  * @return Total number of scanners in filtered set.
40097  */
40098 int
scanner_count(const get_data_t * get)40099 scanner_count (const get_data_t *get)
40100 {
40101   static const char *extra_columns[] = SCANNER_ITERATOR_FILTER_COLUMNS;
40102   static column_t columns[] = SCANNER_ITERATOR_COLUMNS;
40103   static column_t trash_columns[] = SCANNER_ITERATOR_TRASH_COLUMNS;
40104 
40105   return count ("scanner", get, columns, trash_columns, extra_columns,
40106                   0, 0, 0, TRUE);
40107 }
40108 
40109 /**
40110  * @brief Get the default scanner path or host.
40111  *
40112  * @return Newly allocated scanner path or host.
40113  */
40114 char *
openvas_default_scanner_host()40115 openvas_default_scanner_host ()
40116 {
40117   return sql_string ("SELECT host FROM scanners WHERE uuid = '%s'",
40118                      SCANNER_UUID_DEFAULT);
40119 }
40120 
40121 /**
40122  * @brief Create a new connection to an OSP scanner relay.
40123  *
40124  * @param[in]   host     Original host name or IP address.
40125  * @param[in]   port     Original port.
40126  * @param[in]   ca_pub   Original CA certificate.
40127  * @param[in]   key_pub  Public key for authentication.
40128  * @param[in]   key_priv Private key for authentication.
40129  *
40130  * @return New connection if success, NULL otherwise.
40131  */
40132 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)40133 osp_scanner_relay_connect (const char *host, int port, const char *ca_pub,
40134                            const char *key_pub, const char *key_priv)
40135 {
40136   int ret, new_port;
40137   gchar *new_host, *new_ca_pub;
40138   osp_connection_t *connection;
40139 
40140   new_host = NULL;
40141   new_ca_pub = NULL;
40142   new_port = 0;
40143 
40144   ret = slave_get_relay (host,
40145                          port,
40146                          ca_pub,
40147                          "OSP",
40148                          &new_host,
40149                          &new_port,
40150                          &new_ca_pub);
40151 
40152   switch (ret)
40153     {
40154       case 0:
40155         break;
40156       case 1:
40157         g_warning ("No relay found for Scanner at %s:%d", host, port);
40158         return NULL;
40159       default:
40160         g_warning ("%s: Error getting relay for Scanner at %s:%d",
40161                    __func__, host, port);
40162         return NULL;
40163     }
40164 
40165   connection
40166     = osp_connection_new (new_host, new_port, new_ca_pub, key_pub, key_priv);
40167 
40168   if (connection == NULL)
40169     {
40170       if (new_port)
40171         g_warning ("Could not connect to relay at %s:%d"
40172                     " for Scanner at %s:%d",
40173                     new_host, new_port, host, port);
40174       else
40175         g_warning ("Could not connect to relay at %s"
40176                     " for Scanner at %s:%d",
40177                     new_host, host, port);
40178     }
40179 
40180   g_free (new_host);
40181   g_free (new_ca_pub);
40182 
40183   return connection;
40184 }
40185 
40186 /**
40187  * @brief Create a new connection to an OSP scanner using the scanner data.
40188  *
40189  * @param[in]   host     Host name or IP address.
40190  * @param[in]   port     Port.
40191  * @param[in]   ca_pub   CA certificate.
40192  * @param[in]   key_pub  Public key.
40193  * @param[in]   key_priv Private key.
40194  *
40195  * @return New connection if success, NULL otherwise.
40196  */
40197 osp_connection_t *
osp_connect_with_data(const char * host,int port,const char * ca_pub,const char * key_pub,const char * key_priv)40198 osp_connect_with_data (const char *host,
40199                        int port,
40200                        const char *ca_pub,
40201                        const char *key_pub,
40202                        const char *key_priv)
40203 {
40204   osp_connection_t *connection;
40205   int is_unix_socket = (host && *host == '/') ? 1 : 0;
40206 
40207   if (is_unix_socket == 0
40208       && get_relay_mapper_path ())
40209     {
40210       connection
40211         = osp_scanner_relay_connect (host, port, ca_pub, key_pub, key_priv);
40212     }
40213   else
40214     {
40215       connection = osp_connection_new (host, port, ca_pub, key_pub, key_priv);
40216 
40217       if (connection == NULL)
40218         {
40219           if (is_unix_socket)
40220             g_warning ("Could not connect to Scanner at %s", host);
40221           else
40222             g_warning ("Could not connect to Scanner at %s:%d", host, port);
40223         }
40224     }
40225   return connection;
40226 }
40227 
40228 /**
40229  * @brief Create a new connection to an OSP scanner.
40230  *
40231  * @param[in]   scanner     Scanner.
40232  *
40233  * @return New connection if success, NULL otherwise.
40234  */
40235 osp_connection_t *
osp_scanner_connect(scanner_t scanner)40236 osp_scanner_connect (scanner_t scanner)
40237 {
40238   int port;
40239   osp_connection_t *connection;
40240   char *host, *ca_pub, *key_pub, *key_priv;
40241 
40242   assert (scanner);
40243   host = scanner_host (scanner);
40244   if (host && *host == '/')
40245     {
40246       port = 0;
40247       ca_pub = NULL;
40248       key_pub = NULL;
40249       key_priv = NULL;
40250     }
40251   else
40252     {
40253       port = scanner_port (scanner);
40254       ca_pub = scanner_ca_pub (scanner);
40255       key_pub = scanner_key_pub (scanner);
40256       key_priv = scanner_key_priv (scanner);
40257     }
40258 
40259   connection = osp_connect_with_data (host, port, ca_pub, key_pub, key_priv);
40260 
40261   g_free (host);
40262   g_free (ca_pub);
40263   g_free (key_pub);
40264   g_free (key_priv);
40265   return connection;
40266 }
40267 
40268 /**
40269  * @brief Get an OSP Scanner's get_version info.
40270  *
40271  * @param[in]   iterator    Scanner object iterator.
40272  * @param[out]  s_name      Scanner name.
40273  * @param[out]  s_ver       Scanner version.
40274  * @param[out]  d_name      Daemon name.
40275  * @param[out]  d_ver       Daemon version.
40276  * @param[out]  p_name      Protocol name.
40277  * @param[out]  p_ver       Protocol version.
40278  *
40279  * @return 0 success, 1 for failure.
40280  */
40281 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)40282 osp_get_version_from_iterator (iterator_t *iterator, char **s_name,
40283                                char **s_ver, char **d_name, char **d_ver,
40284                                char **p_name, char **p_ver)
40285 {
40286   osp_connection_t *connection;
40287 
40288   assert (iterator);
40289   connection = osp_connect_with_data (scanner_iterator_host (iterator),
40290                                       scanner_iterator_port (iterator),
40291                                       scanner_iterator_ca_pub (iterator),
40292                                       scanner_iterator_key_pub (iterator),
40293                                       scanner_iterator_key_priv (iterator));
40294   if (!connection)
40295     return 1;
40296   if (osp_get_version (connection, s_name, s_ver, d_name, d_ver, p_name, p_ver))
40297     return 1;
40298   osp_connection_close (connection);
40299   return 0;
40300 }
40301 
40302 /**
40303  * @brief Get an OSP Scanner's get_scanner_details info.
40304  *
40305  * @param[in]   iterator    Scanner object iterator.
40306  * @param[out]  desc        Scanner description.
40307  * @param[out]  params      Scanner parameters.
40308  *
40309  * @return 0 success, 1 for failure.
40310  */
40311 int
osp_get_details_from_iterator(iterator_t * iterator,char ** desc,GSList ** params)40312 osp_get_details_from_iterator (iterator_t *iterator, char **desc,
40313                                GSList **params)
40314 {
40315   osp_connection_t *connection;
40316 
40317   assert (iterator);
40318   connection = osp_connect_with_data (scanner_iterator_host (iterator),
40319                                       scanner_iterator_port (iterator),
40320                                       scanner_iterator_ca_pub (iterator),
40321                                       scanner_iterator_key_pub (iterator),
40322                                       scanner_iterator_key_priv (iterator));
40323   if (!connection)
40324     return 1;
40325   if (osp_get_scanner_details (connection, desc, params))
40326     return 1;
40327   osp_connection_close (connection);
40328   return 0;
40329 }
40330 
40331 /**
40332  * @brief Verify a scanner.
40333  *
40334  * @param[in]   scanner_id  Scanner UUID.
40335  * @param[out]  version     Version returned by the scanner.
40336  *
40337  * @return 0 success, 1 failed to find scanner, 2 failed to get version,
40338  *         3 authentication failed, 99 if permission denied, -1 error.
40339  */
40340 int
verify_scanner(const char * scanner_id,char ** version)40341 verify_scanner (const char *scanner_id, char **version)
40342 {
40343   get_data_t get;
40344   iterator_t scanner;
40345 
40346   if (acl_user_may ("verify_scanner") == 0)
40347     return 99;
40348   memset (&get, '\0', sizeof (get));
40349   get.id = g_strdup (scanner_id);
40350   if (init_scanner_iterator (&scanner, &get) || !next (&scanner))
40351     {
40352       g_free (get.id);
40353       return 1;
40354     }
40355   g_free (get.id);
40356   if (scanner_iterator_type (&scanner) == SCANNER_TYPE_OSP
40357       || scanner_iterator_type (&scanner) == SCANNER_TYPE_OPENVAS
40358       || scanner_iterator_type (&scanner) == SCANNER_TYPE_OSP_SENSOR)
40359     {
40360       int ret = osp_get_version_from_iterator (&scanner, NULL, version, NULL,
40361                                                NULL, NULL, NULL);
40362       cleanup_iterator (&scanner);
40363       if (ret)
40364         return 2;
40365       return 0;
40366     }
40367   else if (scanner_iterator_type (&scanner) == SCANNER_TYPE_CVE)
40368     {
40369       if (version)
40370         *version = g_strdup ("GVM/" GVMD_VERSION);
40371       cleanup_iterator (&scanner);
40372       return 0;
40373     }
40374   assert (0);
40375   return -1;
40376 }
40377 
40378 /**
40379  * @brief List scanners.
40380  *
40381  * @param[in]  log_config  Log configuration.
40382  * @param[in]  database    Location of manage database.
40383  *
40384  * @return 0 success, -1 error.
40385  */
40386 int
manage_get_scanners(GSList * log_config,const db_conn_info_t * database)40387 manage_get_scanners (GSList *log_config, const db_conn_info_t *database)
40388 {
40389   iterator_t scanners;
40390   int ret;
40391 
40392   g_info ("   Getting scanners.");
40393 
40394   ret = manage_option_setup (log_config, database);
40395   if (ret)
40396     return ret;
40397 
40398   init_iterator (&scanners,
40399                  "SELECT uuid, type, host, port, name FROM scanners;");
40400 
40401   while (next (&scanners))
40402     {
40403       const char *scanner_id, *scanner_host, *scanner_port, *scanner_name;
40404       scanner_type_t scanner_type;
40405       const char *scanner_type_str;
40406 
40407       scanner_id = iterator_string (&scanners, 0);
40408       scanner_type = iterator_int (&scanners, 1);
40409       scanner_host = iterator_string (&scanners, 2);
40410       scanner_port = iterator_string (&scanners, 3);
40411       scanner_name = iterator_string (&scanners, 4);
40412 
40413       switch (scanner_type)
40414         {
40415           case SCANNER_TYPE_OSP:
40416             scanner_type_str = "OSP";
40417             break;
40418           case SCANNER_TYPE_OPENVAS:
40419             scanner_type_str = "OpenVAS";
40420             break;
40421           case SCANNER_TYPE_CVE:
40422             scanner_type_str = "CVE";
40423             break;
40424           case SCANNER_TYPE_OSP_SENSOR:
40425             scanner_type_str = "OSP-Sensor";
40426             break;
40427           default:
40428             scanner_type_str = NULL;
40429         }
40430 
40431       if (scanner_type_str)
40432         printf ("%s  %s  %s  %s  %s\n",
40433                 scanner_id,
40434                 scanner_type_str,
40435                 scanner_host,
40436                 scanner_port,
40437                 scanner_name);
40438       else
40439         printf ("%s  Unknown-%d  %s  %s  %s\n",
40440                 scanner_id,
40441                 scanner_type,
40442                 scanner_host,
40443                 scanner_port,
40444                 scanner_name);
40445     }
40446   cleanup_iterator (&scanners);
40447 
40448   manage_option_cleanup ();
40449 
40450   return 0;
40451 }
40452 
40453 
40454 /* Schedules. */
40455 
40456 /**
40457  * @brief Find a schedule for a specific permission, given a UUID.
40458  *
40459  * @param[in]   uuid        UUID of schedule.
40460  * @param[out]  schedule    Schedule return, 0 if successfully failed to find schedule.
40461  * @param[in]   permission  Permission.
40462  *
40463  * @return FALSE on success (including if failed to find schedule), TRUE on error.
40464  */
40465 gboolean
find_schedule_with_permission(const char * uuid,schedule_t * schedule,const char * permission)40466 find_schedule_with_permission (const char* uuid, schedule_t* schedule,
40467                              const char *permission)
40468 {
40469   return find_resource_with_permission ("schedule", uuid, schedule, permission, 0);
40470 }
40471 
40472 /**
40473  * @brief Create a schedule.
40474  *
40475  * @param[in]   name        Name of schedule.
40476  * @param[in]   comment     Comment on schedule.
40477  * @param[in]   ical_string iCalendar string.  Overrides first_time, period,
40478  *                           period_months, byday and duration.
40479  * @param[in]   zone        Timezone.
40480  * @param[out]  schedule    Created schedule.
40481  * @param[out]  error_out   Output for iCalendar errors and warnings.
40482  *
40483  * @return 0 success, 1 schedule exists already,
40484  *         3 error in iCal string, 4 error in timezone, 99 permission denied.
40485  */
40486 int
create_schedule(const char * name,const char * comment,const char * ical_string,const char * zone,schedule_t * schedule,gchar ** error_out)40487 create_schedule (const char* name, const char *comment,
40488                  const char *ical_string, const char* zone,
40489                  schedule_t *schedule, gchar **error_out)
40490 {
40491   gchar *quoted_comment, *quoted_name, *quoted_timezone;
40492   gchar *insert_timezone;
40493   int byday_mask;
40494   icalcomponent *ical_component;
40495   icaltimezone *ical_timezone;
40496   gchar *quoted_ical;
40497   time_t first_time, period, period_months, duration;
40498 
40499   assert (current_credentials.uuid);
40500   assert (ical_string && strcmp (ical_string, ""));
40501 
40502   sql_begin_immediate ();
40503 
40504   if (acl_user_may ("create_schedule") == 0)
40505     {
40506       sql_rollback ();
40507       return 99;
40508     }
40509 
40510   if (resource_with_name_exists (name, "schedule", 0))
40511     {
40512       sql_rollback ();
40513       return 1;
40514     }
40515 
40516   quoted_name = sql_quote (name);
40517 
40518   if (zone && strcmp (zone, ""))
40519     insert_timezone = g_strdup (zone);
40520   else
40521     insert_timezone = sql_string ("SELECT timezone FROM users"
40522                                   " WHERE users.uuid = '%s';",
40523                                   current_credentials.uuid);
40524 
40525   if (insert_timezone == NULL)
40526     insert_timezone = g_strdup ("UTC");
40527   else
40528     {
40529       insert_timezone = g_strstrip (insert_timezone);
40530       if (strcmp (insert_timezone, "") == 0)
40531         {
40532           g_free (insert_timezone);
40533           insert_timezone = g_strdup ("UTC");
40534         }
40535     }
40536 
40537   ical_timezone = icalendar_timezone_from_string (insert_timezone);
40538   if (ical_timezone == NULL)
40539     {
40540       g_free (insert_timezone);
40541       return 4;
40542     }
40543 
40544   quoted_comment = sql_quote (comment ? comment : "");
40545   quoted_timezone = sql_quote (insert_timezone);
40546 
40547   ical_component = icalendar_from_string (ical_string, ical_timezone,
40548                                           error_out);
40549   if (ical_component == NULL)
40550     {
40551       g_free (quoted_name);
40552       g_free (quoted_comment);
40553       g_free (insert_timezone);
40554       g_free (quoted_timezone);
40555       return 3;
40556     }
40557   quoted_ical = sql_quote (icalcomponent_as_ical_string (ical_component));
40558   first_time = icalendar_first_time_from_vcalendar (ical_component,
40559                                                     ical_timezone);
40560   duration = icalendar_duration_from_vcalendar (ical_component);
40561 
40562   icalendar_approximate_rrule_from_vcalendar (ical_component,
40563                                               &period,
40564                                               &period_months,
40565                                               &byday_mask);
40566 
40567   sql ("INSERT INTO schedules"
40568        " (uuid, name, owner, comment, first_time, period, period_months,"
40569        "  byday, duration, timezone, icalendar,"
40570        "  creation_time, modification_time)"
40571        " VALUES"
40572        " (make_uuid (), '%s',"
40573        "  (SELECT id FROM users WHERE users.uuid = '%s'),"
40574        "  '%s', %i, %i, %i, %i, %i,"
40575        "  '%s', '%s',"
40576        "  m_now (), m_now ());",
40577        quoted_name, current_credentials.uuid, quoted_comment, first_time,
40578        period, period_months, byday_mask, duration, quoted_timezone,
40579        quoted_ical);
40580 
40581   if (schedule)
40582     *schedule = sql_last_insert_id ();
40583 
40584   g_free (quoted_name);
40585   g_free (quoted_comment);
40586   g_free (insert_timezone);
40587   g_free (quoted_timezone);
40588   g_free (quoted_ical);
40589 
40590   sql_commit ();
40591 
40592   return 0;
40593 }
40594 
40595 /**
40596  * @brief Create a schedule from an existing schedule.
40597  *
40598  * @param[in]  name          Name of new schedule. NULL to copy from existing.
40599  * @param[in]  comment       Comment on new schedule. NULL to copy from
40600  *                           existing.
40601  * @param[in]  schedule_id   UUID of existing schedule.
40602  * @param[out] new_schedule  New schedule.
40603  *
40604  * @return 0 success, 1 schedule exists already, 2 failed to find existing
40605  *         schedule, -1 error.
40606  */
40607 int
copy_schedule(const char * name,const char * comment,const char * schedule_id,schedule_t * new_schedule)40608 copy_schedule (const char* name, const char* comment, const char *schedule_id,
40609                schedule_t* new_schedule)
40610 {
40611   return copy_resource ("schedule", name, comment, schedule_id,
40612                         "first_time, period, period_months, byday, duration,"
40613                         " timezone, icalendar",
40614                         1, new_schedule, NULL);
40615 }
40616 
40617 /**
40618  * @brief Delete a schedule.
40619  *
40620  * @param[in]  schedule_id  Schedule.
40621  * @param[in]  ultimate     Whether to remove entirely, or to trashcan.
40622  *
40623  * @return 0 success, 1 fail because a task refers to the schedule,
40624  *         2 failed to find schedule, 99 permission denied, -1 error.
40625  */
40626 int
delete_schedule(const char * schedule_id,int ultimate)40627 delete_schedule (const char *schedule_id, int ultimate)
40628 {
40629   schedule_t schedule = 0;
40630 
40631   sql_begin_immediate ();
40632 
40633   if (acl_user_may ("delete_schedule") == 0)
40634     {
40635       sql_rollback ();
40636       return 99;
40637     }
40638 
40639   if (find_schedule_with_permission (schedule_id, &schedule, "delete_schedule"))
40640     {
40641       sql_rollback ();
40642       return -1;
40643     }
40644 
40645   if (schedule == 0)
40646     {
40647       if (find_trash ("schedule", schedule_id, &schedule))
40648         {
40649           sql_rollback ();
40650           return -1;
40651         }
40652       if (schedule == 0)
40653         {
40654           sql_rollback ();
40655           return 2;
40656         }
40657       if (ultimate == 0)
40658         {
40659           /* It's already in the trashcan. */
40660           sql_commit ();
40661           return 0;
40662         }
40663 
40664       /* Check if it's in use by a task in the trashcan. */
40665       if (sql_int ("SELECT count(*) FROM tasks"
40666                    " WHERE schedule = %llu"
40667                    " AND schedule_location = " G_STRINGIFY (LOCATION_TRASH) ";",
40668                    schedule))
40669         {
40670           sql_rollback ();
40671           return 1;
40672         }
40673 
40674       permissions_set_orphans ("schedule", schedule, LOCATION_TRASH);
40675       tags_remove_resource ("schedule", schedule, LOCATION_TRASH);
40676 
40677       sql ("DELETE FROM schedules_trash WHERE id = %llu;", schedule);
40678       sql_commit ();
40679       return 0;
40680     }
40681 
40682   if (ultimate == 0)
40683     {
40684       if (sql_int ("SELECT count(*) FROM tasks"
40685                    " WHERE schedule = %llu"
40686                    " AND schedule_location = " G_STRINGIFY (LOCATION_TABLE)
40687                    " AND hidden = 0;",
40688                    schedule))
40689         {
40690           sql_rollback ();
40691           return 1;
40692         }
40693 
40694       sql ("INSERT INTO schedules_trash"
40695            " (uuid, owner, name, comment, first_time, period, period_months,"
40696            "  byday, duration, timezone, creation_time,"
40697            "  modification_time, icalendar)"
40698            " SELECT uuid, owner, name, comment, first_time, period, period_months,"
40699            "        byday, duration, timezone, creation_time,"
40700            "        modification_time, icalendar"
40701            " FROM schedules WHERE id = %llu;",
40702            schedule);
40703 
40704       /* Update the location of the schedule in any trashcan tasks. */
40705       sql ("UPDATE tasks"
40706            " SET schedule = %llu,"
40707            "     schedule_location = " G_STRINGIFY (LOCATION_TRASH)
40708            " WHERE schedule = %llu"
40709            " AND schedule_location = " G_STRINGIFY (LOCATION_TABLE) ";",
40710            sql_last_insert_id (),
40711            schedule);
40712 
40713       permissions_set_locations ("schedule", schedule,
40714                                  sql_last_insert_id (),
40715                                  LOCATION_TRASH);
40716       tags_set_locations ("schedule", schedule,
40717                           sql_last_insert_id (),
40718                           LOCATION_TRASH);
40719     }
40720   else if (sql_int ("SELECT count(*) FROM tasks"
40721                     " WHERE schedule = %llu"
40722                     " AND schedule_location = " G_STRINGIFY (LOCATION_TABLE),
40723                     schedule))
40724     {
40725       sql_rollback ();
40726       return 1;
40727     }
40728   else
40729     {
40730       permissions_set_orphans ("schedule", schedule, LOCATION_TABLE);
40731       tags_remove_resource ("schedule", schedule, LOCATION_TABLE);
40732     }
40733 
40734   sql ("DELETE FROM schedules WHERE id = %llu;", schedule);
40735 
40736   sql_commit ();
40737   return 0;
40738 }
40739 
40740 /**
40741  * @brief Return whether a schedule is in use by a task.
40742  *
40743  * @param[in]  schedule  Schedule.
40744  *
40745  * @return 1 if in use, else 0.
40746  */
40747 int
schedule_in_use(schedule_t schedule)40748 schedule_in_use (schedule_t schedule)
40749 {
40750   return !!sql_int ("SELECT count(*) FROM tasks WHERE schedule = %llu"
40751                     " AND hidden = 0;", schedule);
40752 }
40753 
40754 /**
40755  * @brief Return whether a trashcan schedule is in use by a task.
40756  *
40757  * @param[in]  schedule  schedule.
40758  *
40759  * @return 1 if in use, else 0.
40760  */
40761 int
trash_schedule_in_use(schedule_t schedule)40762 trash_schedule_in_use (schedule_t schedule)
40763 {
40764   return !!sql_int ("SELECT count(*) FROM tasks"
40765                     " WHERE schedule = %llu"
40766                     " AND schedule_location = " G_STRINGIFY (LOCATION_TRASH),
40767                     schedule);
40768 }
40769 
40770 /**
40771  * @brief Return whether a schedule is writable.
40772  *
40773  * @param[in]  schedule  Schedule.
40774  *
40775  * @return 1 if writable, else 0.
40776  */
40777 int
schedule_writable(schedule_t schedule)40778 schedule_writable (schedule_t schedule)
40779 {
40780   return 1;
40781 }
40782 
40783 /**
40784  * @brief Return whether a trashcan schedule is writable.
40785  *
40786  * @param[in]  schedule  Schedule.
40787  *
40788  * @return 1 if writable, else 0.
40789  */
40790 int
trash_schedule_writable(schedule_t schedule)40791 trash_schedule_writable (schedule_t schedule)
40792 {
40793   return trash_schedule_in_use (schedule) == 0;
40794 }
40795 
40796 /**
40797  * @brief Return whether a trashcan schedule is readable.
40798  *
40799  * @param[in]  schedule  Schedule.
40800  *
40801  * @return 1 if readable, else 0.
40802  */
40803 int
trash_schedule_readable(schedule_t schedule)40804 trash_schedule_readable (schedule_t schedule)
40805 {
40806   char *uuid;
40807   schedule_t found = 0;
40808 
40809   if (schedule == 0)
40810     return 0;
40811   uuid = schedule_uuid (schedule);
40812   if (find_trash ("schedule", uuid, &found))
40813     {
40814       g_free (uuid);
40815       return 0;
40816     }
40817   g_free (uuid);
40818   return found > 0;
40819 }
40820 
40821 /**
40822  * @brief Return the UUID of a schedule.
40823  *
40824  * @param[in]  schedule  Schedule.
40825  *
40826  * @return Newly allocated UUID.
40827  */
40828 char *
schedule_uuid(schedule_t schedule)40829 schedule_uuid (schedule_t schedule)
40830 {
40831   return sql_string ("SELECT uuid FROM schedules WHERE id = %llu;",
40832                      schedule);
40833 }
40834 
40835 /**
40836  * @brief Return the UUID of a trash schedule.
40837  *
40838  * @param[in]  schedule  Schedule.
40839  *
40840  * @return Newly allocated UUID.
40841  */
40842 char *
trash_schedule_uuid(schedule_t schedule)40843 trash_schedule_uuid (schedule_t schedule)
40844 {
40845   return sql_string ("SELECT uuid FROM schedules_trash WHERE id = %llu;",
40846                      schedule);
40847 }
40848 
40849 /**
40850  * @brief Return the name of a schedule.
40851  *
40852  * @param[in]  schedule  Schedule.
40853  *
40854  * @return Newly allocated name.
40855  */
40856 char *
schedule_name(schedule_t schedule)40857 schedule_name (schedule_t schedule)
40858 {
40859   return sql_string ("SELECT name FROM schedules WHERE id = %llu;",
40860                      schedule);
40861 }
40862 
40863 /**
40864  * @brief Return the name of a trash schedule.
40865  *
40866  * @param[in]  schedule  Schedule.
40867  *
40868  * @return Newly allocated name.
40869  */
40870 char *
trash_schedule_name(schedule_t schedule)40871 trash_schedule_name (schedule_t schedule)
40872 {
40873   return sql_string ("SELECT name FROM schedules_trash WHERE id = %llu;",
40874                      schedule);
40875 }
40876 
40877 /**
40878  * @brief Return the period of a schedule.
40879  *
40880  * @param[in]  schedule  Schedule.
40881  *
40882  * @return Period in seconds.
40883  */
40884 int
schedule_period(schedule_t schedule)40885 schedule_period (schedule_t schedule)
40886 {
40887   return sql_int ("SELECT period FROM schedules WHERE id = %llu;",
40888                   schedule);
40889 }
40890 
40891 /**
40892  * @brief Return the duration of a schedule.
40893  *
40894  * @param[in]  schedule  Schedule.
40895  *
40896  * @return Duration in seconds.
40897  */
40898 int
schedule_duration(schedule_t schedule)40899 schedule_duration (schedule_t schedule)
40900 {
40901   return sql_int ("SELECT duration FROM schedules WHERE id = %llu;",
40902                   schedule);
40903 }
40904 
40905 /**
40906  * @brief Return info about a schedule.
40907  *
40908  * @param[in]  schedule    Schedule.
40909  * @param[in]  trash       Whether to get schedule from trash.
40910  * @param[out] icalendar      iCalendar string.
40911  * @param[out] zone           Timezone string.
40912  *
40913  * @return 0 success, -1 error.
40914  */
40915 int
schedule_info(schedule_t schedule,int trash,gchar ** icalendar,gchar ** zone)40916 schedule_info (schedule_t schedule, int trash, gchar **icalendar, gchar **zone)
40917 {
40918   iterator_t schedules;
40919 
40920   init_iterator (&schedules,
40921                  "SELECT icalendar, timezone FROM schedules%s"
40922                  " WHERE id = %llu;",
40923                  trash ? "_trash" : "",
40924                  schedule);
40925   if (next (&schedules))
40926     {
40927       *icalendar = g_strdup (iterator_string (&schedules, 0));
40928       *zone = g_strdup (iterator_string (&schedules, 1));
40929       cleanup_iterator (&schedules);
40930       return 0;
40931     }
40932   cleanup_iterator (&schedules);
40933   return -1;
40934 }
40935 
40936 /**
40937  * @brief Filter columns for schedule iterator.
40938  */
40939 #define SCHEDULE_ITERATOR_FILTER_COLUMNS                                      \
40940  { GET_ITERATOR_FILTER_COLUMNS, "first_time", "period", "period_months",      \
40941    "duration", "timezone", "first_run", "next_run", NULL }
40942 
40943 /**
40944  * @brief Schedule iterator columns.
40945  */
40946 #define SCHEDULE_ITERATOR_COLUMNS                                          \
40947  {                                                                         \
40948    GET_ITERATOR_COLUMNS (schedules),                                       \
40949    { "first_time", NULL, KEYWORD_TYPE_INTEGER },                           \
40950    { "period", NULL, KEYWORD_TYPE_INTEGER },                               \
40951    { "period_months", NULL, KEYWORD_TYPE_INTEGER },                        \
40952    { "duration", NULL, KEYWORD_TYPE_INTEGER },                             \
40953    { "timezone", NULL, KEYWORD_TYPE_STRING },                              \
40954    { "icalendar", NULL, KEYWORD_TYPE_STRING },                             \
40955    { "next_time_ical (icalendar, timezone)",                               \
40956      "next_run",                                                           \
40957      KEYWORD_TYPE_INTEGER },                                               \
40958    { "first_time", "first_run", KEYWORD_TYPE_INTEGER },                    \
40959    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                    \
40960  }
40961 
40962 /**
40963  * @brief Schedule iterator columns for trash case.
40964  */
40965 #define SCHEDULE_ITERATOR_TRASH_COLUMNS                                    \
40966  {                                                                         \
40967    GET_ITERATOR_COLUMNS (schedules_trash),                                 \
40968    { "first_time", NULL, KEYWORD_TYPE_INTEGER },                           \
40969    { "period", NULL, KEYWORD_TYPE_INTEGER },                               \
40970    { "period_months", NULL, KEYWORD_TYPE_INTEGER },                        \
40971    { "duration", NULL, KEYWORD_TYPE_INTEGER },                             \
40972    { "timezone", NULL, KEYWORD_TYPE_STRING },                              \
40973    { "icalendar", NULL, KEYWORD_TYPE_STRING },                             \
40974    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                    \
40975  }
40976 
40977 /**
40978  * @brief Count the number of schedules.
40979  *
40980  * @param[in]  get  GET params.
40981  *
40982  * @return Total number of schedules filtered set.
40983  */
40984 int
schedule_count(const get_data_t * get)40985 schedule_count (const get_data_t *get)
40986 {
40987   static const char *filter_columns[] = SCHEDULE_ITERATOR_FILTER_COLUMNS;
40988   static column_t columns[] = SCHEDULE_ITERATOR_COLUMNS;
40989   static column_t trash_columns[] = SCHEDULE_ITERATOR_TRASH_COLUMNS;
40990   return count ("schedule", get, columns, trash_columns, filter_columns,
40991                 0, 0, 0, TRUE);
40992 }
40993 
40994 /**
40995  * @brief Initialise a schedule iterator.
40996  *
40997  * @param[in]  iterator  Iterator.
40998  * @param[in]  get         GET data.
40999  *
41000  * @return 0 success, 1 failed to find filter, 2 failed to find"
41001  *         filter (filt_id), -1 error.
41002  */
41003 int
init_schedule_iterator(iterator_t * iterator,const get_data_t * get)41004 init_schedule_iterator (iterator_t* iterator, const get_data_t *get)
41005 {
41006   static const char *filter_columns[] = SCHEDULE_ITERATOR_FILTER_COLUMNS;
41007   static column_t columns[] = SCHEDULE_ITERATOR_COLUMNS;
41008   static column_t trash_columns[] = SCHEDULE_ITERATOR_TRASH_COLUMNS;
41009 
41010   return init_get_iterator (iterator,
41011                             "schedule",
41012                             get,
41013                             columns,
41014                             trash_columns,
41015                             filter_columns,
41016                             0,
41017                             NULL,
41018                             NULL,
41019                             TRUE);
41020 }
41021 
41022 /**
41023  * @brief Get the timezone from a schedule iterator.
41024  *
41025  * @param[in]  iterator  Iterator.
41026  *
41027  * @return Timezone, or NULL if iteration is complete.  Freed by
41028  *         cleanup_iterator.
41029  */
41030 DEF_ACCESS (schedule_iterator_timezone, GET_ITERATOR_COLUMN_COUNT + 4);
41031 
41032 /**
41033  * @brief Get the iCalendar string from a schedule iterator.
41034  *
41035  * @param[in]  iterator  Iterator.
41036  *
41037  * @return The iCalendar string or NULL if iteration is complete.  Freed by
41038  *         cleanup_iterator.
41039  */
41040 DEF_ACCESS (schedule_iterator_icalendar, GET_ITERATOR_COLUMN_COUNT + 5);
41041 
41042 /**
41043  * @brief Initialise a task schedule iterator.
41044  *
41045  * Lock the database before initialising.
41046  *
41047  * @param[in]  iterator        Iterator.
41048  *
41049  * @return 0 success, 1 failed to get lock, -1 error.
41050  */
41051 int
init_task_schedule_iterator(iterator_t * iterator)41052 init_task_schedule_iterator (iterator_t* iterator)
41053 {
41054   int ret;
41055 
41056   ret = sql_begin_immediate_giveup ();
41057   if (ret)
41058     return ret;
41059 
41060   init_iterator (iterator,
41061                  "SELECT tasks.id, tasks.uuid,"
41062                  " schedules.id, tasks.schedule_next_time,"
41063                  " schedules.icalendar, schedules.timezone,"
41064                  " schedules.duration,"
41065                  " users.uuid, users.name"
41066                  " FROM tasks, schedules, users"
41067                  " WHERE tasks.schedule = schedules.id"
41068                  " AND tasks.hidden = 0"
41069                  " AND (tasks.owner = (users.id))"
41070                  /* Sort by task and prefer owner of task or schedule as user */
41071                  " ORDER BY tasks.id,"
41072                  "          (users.id = tasks.owner) DESC,"
41073                  "          (users.id = schedules.owner) DESC;");
41074 
41075   return 0;
41076 }
41077 
41078 /**
41079  * @brief Cleanup a task schedule iterator.
41080  *
41081  * @param[in]  iterator  Iterator.
41082  */
41083 void
cleanup_task_schedule_iterator(iterator_t * iterator)41084 cleanup_task_schedule_iterator (iterator_t* iterator)
41085 {
41086   cleanup_iterator (iterator);
41087   sql_commit ();
41088 }
41089 
41090 /**
41091  * @brief Get the task from a task schedule iterator.
41092  *
41093  * @param[in]  iterator  Iterator.
41094  *
41095  * @return task.
41096  */
41097 task_t
task_schedule_iterator_task(iterator_t * iterator)41098 task_schedule_iterator_task (iterator_t* iterator)
41099 {
41100   if (iterator->done) return 0;
41101   return (task_t) iterator_int64 (iterator, 0);
41102 }
41103 
41104 /**
41105  * @brief Get the task UUID from a task schedule iterator.
41106  *
41107  * @param[in]  iterator  Iterator.
41108  *
41109  * @return Task UUID, or NULL if iteration is complete.  Freed by
41110  *         cleanup_iterator.
41111  */
41112 DEF_ACCESS (task_schedule_iterator_task_uuid, 1);
41113 
41114 /**
41115  * @brief Get the next time from a task schedule iterator.
41116  *
41117  * @param[in]  iterator  Iterator.
41118  *
41119  * @return Next time.
41120  */
41121 static time_t
task_schedule_iterator_next_time(iterator_t * iterator)41122 task_schedule_iterator_next_time (iterator_t* iterator)
41123 {
41124   if (iterator->done) return 0;
41125   return (time_t) iterator_int64 (iterator, 3);
41126 }
41127 
41128 /**
41129  * @brief Get the iCalendar string from a task schedule iterator.
41130  *
41131  * @param[in]  iterator  Iterator.
41132  *
41133  * @return period.
41134  */
41135 DEF_ACCESS (task_schedule_iterator_icalendar, 4);
41136 
41137 /**
41138  * @brief Get the timezone from a task schedule iterator.
41139  *
41140  * @param[in]  iterator  Iterator.
41141  *
41142  * @return Timezone, or NULL if iteration is complete.  Freed by
41143  *         cleanup_iterator.
41144  */
41145 DEF_ACCESS (task_schedule_iterator_timezone, 5);
41146 
41147 /**
41148  * @brief Get the next time from a task schedule iterator.
41149  *
41150  * @param[in]  iterator  Iterator.
41151  *
41152  * @return Next time.
41153  */
41154 static time_t
task_schedule_iterator_duration(iterator_t * iterator)41155 task_schedule_iterator_duration (iterator_t* iterator)
41156 {
41157   if (iterator->done) return 0;
41158   return (time_t) iterator_int64 (iterator, 6);
41159 }
41160 
41161 /**
41162  * @brief Get the task owner uuid from a task schedule iterator.
41163  *
41164  * @param[in]  iterator  Iterator.
41165  *
41166  * @return Owner UUID, or NULL if iteration is complete.  Freed by
41167  *         cleanup_iterator.
41168  */
41169 DEF_ACCESS (task_schedule_iterator_owner_uuid, 7);
41170 
41171 /**
41172  * @brief Get the task owner name from a task schedule iterator.
41173  *
41174  * @param[in]  iterator  Iterator.
41175  *
41176  * @return Owner name, or NULL if iteration is complete.  Freed by
41177  *         cleanup_iterator.
41178  */
41179 DEF_ACCESS (task_schedule_iterator_owner_name, 8);
41180 
41181 /**
41182  * @brief Get the start due state from a task schedule iterator.
41183  *
41184  * @param[in]  iterator  Iterator.
41185  *
41186  * @return Start due flag.
41187  */
41188 gboolean
task_schedule_iterator_start_due(iterator_t * iterator)41189 task_schedule_iterator_start_due (iterator_t* iterator)
41190 {
41191   task_status_t run_status;
41192   time_t start_time;
41193 
41194   if (iterator->done) return FALSE;
41195 
41196   if (task_schedule_iterator_next_time (iterator) == 0)
41197     return FALSE;
41198 
41199   run_status = task_run_status (task_schedule_iterator_task (iterator));
41200   start_time = task_schedule_iterator_next_time (iterator);
41201 
41202   if ((run_status == TASK_STATUS_DONE
41203        || run_status == TASK_STATUS_INTERRUPTED
41204        || run_status == TASK_STATUS_NEW
41205        || run_status == TASK_STATUS_STOPPED)
41206       && (start_time > 0)
41207       && (start_time <= time (NULL)))
41208     return TRUE;
41209 
41210   return FALSE;
41211 }
41212 
41213 /**
41214  * @brief Get the stop due state from a task schedule iterator.
41215  *
41216  * @param[in]  iterator  Iterator.
41217  *
41218  * @return Stop due flag.
41219  */
41220 gboolean
task_schedule_iterator_stop_due(iterator_t * iterator)41221 task_schedule_iterator_stop_due (iterator_t* iterator)
41222 {
41223   const char *icalendar, *zone;
41224   time_t duration;
41225 
41226   if (iterator->done) return FALSE;
41227 
41228   icalendar = task_schedule_iterator_icalendar (iterator);
41229   zone = task_schedule_iterator_timezone (iterator);
41230   duration = task_schedule_iterator_duration (iterator);
41231 
41232   if (duration)
41233     {
41234       report_t report;
41235       task_status_t run_status;
41236 
41237       report = task_running_report (task_schedule_iterator_task (iterator));
41238       if (report && (report_scheduled (report) == 0))
41239         return FALSE;
41240 
41241       run_status = task_run_status (task_schedule_iterator_task (iterator));
41242 
41243       if (run_status == TASK_STATUS_RUNNING
41244           || run_status == TASK_STATUS_REQUESTED
41245           || run_status == TASK_STATUS_QUEUED)
41246         {
41247           time_t now, start;
41248 
41249           now = time (NULL);
41250 
41251           start = icalendar_next_time_from_string (icalendar, zone, -1);
41252           if ((start + duration) < now)
41253             return TRUE;
41254         }
41255     }
41256 
41257   return FALSE;
41258 }
41259 
41260 /**
41261  * @brief Get if schedule of task in iterator is timed out.
41262  *
41263  * @param[in]  iterator  Iterator.
41264  *
41265  * @return Whether task schedule is timed out.
41266  */
41267 gboolean
task_schedule_iterator_timed_out(iterator_t * iterator)41268 task_schedule_iterator_timed_out (iterator_t* iterator)
41269 {
41270   time_t schedule_timeout_secs;
41271   int duration;
41272   task_status_t run_status;
41273   time_t start_time, timeout_time;
41274 
41275   if (get_schedule_timeout () < 0)
41276     return FALSE;
41277 
41278   if (iterator->done) return FALSE;
41279 
41280   start_time = task_schedule_iterator_next_time (iterator);
41281 
41282   if (start_time == 0)
41283     return FALSE;
41284 
41285   schedule_timeout_secs = get_schedule_timeout () * 60;
41286   if (schedule_timeout_secs < SCHEDULE_TIMEOUT_MIN_SECS)
41287     schedule_timeout_secs = SCHEDULE_TIMEOUT_MIN_SECS;
41288 
41289   run_status = task_run_status (task_schedule_iterator_task (iterator));
41290   duration = task_schedule_iterator_duration (iterator);
41291 
41292   if (duration && (duration < schedule_timeout_secs))
41293     timeout_time = start_time + duration;
41294   else
41295     timeout_time = start_time + schedule_timeout_secs;
41296 
41297   if ((run_status == TASK_STATUS_DONE
41298        || run_status == TASK_STATUS_INTERRUPTED
41299        || run_status == TASK_STATUS_NEW
41300        || run_status == TASK_STATUS_STOPPED)
41301       && (timeout_time <= time (NULL)))
41302     return TRUE;
41303 
41304   return FALSE;
41305 }
41306 
41307 /**
41308  * @brief Initialise a schedule task iterator.
41309  *
41310  * @param[in]  iterator  Iterator.
41311  * @param[in]  schedule  Schedule.
41312  */
41313 void
init_schedule_task_iterator(iterator_t * iterator,schedule_t schedule)41314 init_schedule_task_iterator (iterator_t* iterator, schedule_t schedule)
41315 {
41316   gchar *available, *with_clause;
41317   get_data_t get;
41318   array_t *permissions;
41319 
41320   assert (current_credentials.uuid);
41321 
41322   get.trash = 0;
41323   permissions = make_array ();
41324   array_add (permissions, g_strdup ("get_tasks"));
41325   available = acl_where_owned ("task", &get, 1, "any", 0, permissions, 0,
41326                                &with_clause);
41327   array_free (permissions);
41328   init_iterator (iterator,
41329                  "%s"
41330                  " SELECT id, uuid, name, %s FROM tasks"
41331                  " WHERE schedule = %llu AND hidden = 0"
41332                  " ORDER BY name ASC;",
41333                  with_clause ? with_clause : "",
41334                  available,
41335                  schedule,
41336                  current_credentials.uuid);
41337   g_free (with_clause);
41338   g_free (available);
41339 }
41340 
41341 /**
41342  * @brief Get the UUID from a schedule task iterator.
41343  *
41344  * @param[in]  iterator  Iterator.
41345  *
41346  * @return UUID, or NULL if iteration is complete.  Freed by
41347  *         cleanup_iterator.
41348  */
41349 DEF_ACCESS (schedule_task_iterator_uuid, 1);
41350 
41351 /**
41352  * @brief Get the name from a schedule task iterator.
41353  *
41354  * @param[in]  iterator  Iterator.
41355  *
41356  * @return Name, or NULL if iteration is complete.  Freed by
41357  *         cleanup_iterator.
41358  */
41359 DEF_ACCESS (schedule_task_iterator_name, 2);
41360 
41361 /**
41362  * @brief Get the read permission status from a GET iterator.
41363  *
41364  * @param[in]  iterator  Iterator.
41365  *
41366  * @return 1 if may read, else 0.
41367  */
41368 int
schedule_task_iterator_readable(iterator_t * iterator)41369 schedule_task_iterator_readable (iterator_t* iterator)
41370 {
41371   if (iterator->done) return 0;
41372   return iterator_int (iterator, 3);
41373 }
41374 
41375 /**
41376  * @brief Modify a schedule.
41377  *
41378  * @param[in]   schedule_id  UUID of schedule.
41379  * @param[in]   name         Name of schedule.
41380  * @param[in]   comment      Comment on schedule.
41381  * @param[in]   ical_string  iCalendar string.  Overrides first_time, period,
41382  *                           period_months, byday and duration.
41383  * @param[in]   zone         Timezone.
41384  * @param[out]  error_out    Output for iCalendar errors and warnings.
41385  *
41386  * @return 0 success, 1 failed to find schedule, 2 schedule with new name exists,
41387  *         3 error in type name, 4 schedule_id required,
41388  *         6 error in iCalendar, 7 error in zone,
41389  *         99 permission denied, -1 internal error.
41390  */
41391 int
modify_schedule(const char * schedule_id,const char * name,const char * comment,const char * ical_string,const char * zone,gchar ** error_out)41392 modify_schedule (const char *schedule_id, const char *name, const char *comment,
41393                  const char *ical_string, const char *zone, gchar **error_out)
41394 {
41395   gchar *quoted_name, *quoted_comment, *quoted_timezone, *quoted_icalendar;
41396   icalcomponent *ical_component;
41397   icaltimezone *ical_timezone;
41398   schedule_t schedule;
41399   time_t new_next_time, ical_first_time, ical_duration;
41400   gchar *real_timezone;
41401 
41402   assert (ical_string && strcmp (ical_string, ""));
41403 
41404   if (schedule_id == NULL)
41405     return 4;
41406 
41407   sql_begin_immediate ();
41408 
41409   assert (current_credentials.uuid);
41410 
41411   if (acl_user_may ("modify_schedule") == 0)
41412     {
41413       sql_rollback ();
41414       return 99;
41415     }
41416 
41417   schedule = 0;
41418   if (find_schedule_with_permission (schedule_id, &schedule, "modify_schedule"))
41419     {
41420       sql_rollback ();
41421       return -1;
41422     }
41423 
41424   if (schedule == 0)
41425     {
41426       sql_rollback ();
41427       return 1;
41428     }
41429 
41430   /* Check whether a schedule with the same name exists already. */
41431   if (name)
41432     {
41433       if (resource_with_name_exists (name, "schedule", schedule))
41434         {
41435           sql_rollback ();
41436           return 2;
41437         }
41438       quoted_name = sql_quote (name);
41439     }
41440   else
41441     quoted_name = NULL;
41442 
41443   /* Update basic data */
41444   quoted_comment = comment ? sql_quote (comment) : NULL;
41445 
41446   if (zone == NULL)
41447     quoted_timezone = NULL;
41448   else
41449     {
41450       quoted_timezone = g_strstrip (sql_quote (zone));
41451       if (strcmp (quoted_timezone, "") == 0)
41452         {
41453           g_free (quoted_timezone);
41454           quoted_timezone = NULL;
41455         }
41456     }
41457 
41458   sql ("UPDATE schedules SET"
41459        " name = %s%s%s,"
41460        " comment = %s%s%s,"
41461        " timezone = %s%s%s"
41462        " WHERE id = %llu;",
41463        quoted_name ? "'" : "",
41464        quoted_name ? quoted_name : "name",
41465        quoted_name ? "'" : "",
41466        quoted_comment ? "'" : "",
41467        quoted_comment ? quoted_comment : "comment",
41468        quoted_comment ? "'" : "",
41469        quoted_timezone ? "'" : "",
41470        quoted_timezone ? quoted_timezone : "timezone",
41471        quoted_timezone ? "'" : "",
41472        schedule);
41473 
41474   real_timezone
41475     = sql_string ("SELECT timezone FROM schedules WHERE id = %llu;",
41476                   schedule);
41477 
41478   /* Update times */
41479 
41480   ical_timezone = icalendar_timezone_from_string (real_timezone);
41481   if (ical_timezone == NULL)
41482     {
41483       g_free (real_timezone);
41484       sql_rollback ();
41485       return 7;
41486     }
41487 
41488   ical_component = icalendar_from_string (ical_string, ical_timezone,
41489                                           error_out);
41490   if (ical_component == NULL)
41491     {
41492       g_free (real_timezone);
41493       sql_rollback ();
41494       return 6;
41495     }
41496 
41497   quoted_icalendar = sql_quote (icalcomponent_as_ical_string
41498                                   (ical_component));
41499 
41500   ical_first_time = icalendar_first_time_from_vcalendar (ical_component,
41501                                                          ical_timezone);
41502   ical_duration = icalendar_duration_from_vcalendar (ical_component);
41503 
41504   sql ("UPDATE schedules SET"
41505        " icalendar = '%s',"
41506        " first_time = %ld,"
41507        " period = 0,"
41508        " period_months = 0,"
41509        " byday = 0,"
41510        " duration = %d,"
41511        " modification_time = m_now ()"
41512        " WHERE id = %llu;",
41513        quoted_icalendar,
41514        ical_first_time,
41515        ical_duration,
41516        schedule);
41517 
41518   // Update scheduled next times for tasks
41519 
41520   new_next_time = icalendar_next_time_from_vcalendar (ical_component,
41521                                                       real_timezone, 0);
41522 
41523   sql ("UPDATE tasks SET schedule_next_time = %ld"
41524        " WHERE schedule = %llu;",
41525        new_next_time,
41526        schedule);
41527 
41528   g_free (quoted_comment);
41529   g_free (quoted_name);
41530   g_free (quoted_timezone);
41531   g_free (quoted_icalendar);
41532 
41533   free (real_timezone);
41534   icalcomponent_free (ical_component);
41535 
41536   sql_commit ();
41537 
41538   return 0;
41539 }
41540 
41541 
41542 /* Groups. */
41543 
41544 /**
41545  * @brief Find a group for a specific permission, given a UUID.
41546  *
41547  * @param[in]   uuid        UUID of group.
41548  * @param[out]  group       Group return, 0 if successfully failed to find group.
41549  * @param[in]   permission  Permission.
41550  *
41551  * @return FALSE on success (including if failed to find group), TRUE on error.
41552  */
41553 static gboolean
find_group_with_permission(const char * uuid,group_t * group,const char * permission)41554 find_group_with_permission (const char* uuid, group_t* group,
41555                             const char *permission)
41556 {
41557   return find_resource_with_permission ("group", uuid, group, permission, 0);
41558 }
41559 
41560 /**
41561  * @brief Create a group from an existing group.
41562  *
41563  * @param[in]  name       Name of new group.  NULL to copy from existing.
41564  * @param[in]  comment    Comment on new group.  NULL to copy from existing.
41565  * @param[in]  group_id   UUID of existing group.
41566  * @param[out] new_group_return  New group.
41567  *
41568  * @return 0 success, 1 group exists already, 2 failed to find existing
41569  *         group, 99 permission denied, -1 error.
41570  */
41571 int
copy_group(const char * name,const char * comment,const char * group_id,group_t * new_group_return)41572 copy_group (const char *name, const char *comment, const char *group_id,
41573             group_t *new_group_return)
41574 {
41575   int ret;
41576   group_t new, old;
41577 
41578   sql_begin_immediate ();
41579 
41580   ret = copy_resource_lock ("group", name, comment, group_id, NULL, 1, &new,
41581                             &old);
41582   if (ret)
41583     {
41584       sql_rollback ();
41585       return ret;
41586     }
41587 
41588   sql ("INSERT INTO group_users (\"group\", \"user\")"
41589        " SELECT %llu, \"user\" FROM group_users"
41590        " WHERE \"group\" = %llu;",
41591        new,
41592        old);
41593 
41594   sql_commit ();
41595   if (new_group_return)
41596     *new_group_return = new;
41597   return 0;
41598 }
41599 
41600 /**
41601  * @brief Add users to a group.
41602  *
41603  * Caller must take care of transaction.
41604  *
41605  * @param[in]  type      Type.
41606  * @param[in]  resource  Group or role.
41607  * @param[in]  users     List of users.
41608  *
41609  * @return 0 success, 2 failed to find user, 4 user name validation failed,
41610  *         99 permission denied, -1 error.
41611  */
41612 static int
add_users(const gchar * type,resource_t resource,const char * users)41613 add_users (const gchar *type, resource_t resource, const char *users)
41614 {
41615   if (users)
41616     {
41617       gchar **split, **point;
41618       GList *added;
41619 
41620       /* Add each user. */
41621 
41622       added = NULL;
41623       split = g_strsplit_set (users, " ,", 0);
41624       point = split;
41625 
41626       while (*point)
41627         {
41628           user_t user;
41629           gchar *name;
41630 
41631           name = *point;
41632 
41633           g_strstrip (name);
41634 
41635           if (strcmp (name, "") == 0)
41636             {
41637               point++;
41638               continue;
41639             }
41640 
41641           if (g_list_find_custom (added, name, (GCompareFunc) strcmp))
41642             {
41643               point++;
41644               continue;
41645             }
41646 
41647           added = g_list_prepend (added, name);
41648 
41649           if (user_exists (name) == 0)
41650             {
41651               g_list_free (added);
41652               g_strfreev (split);
41653               return 2;
41654             }
41655 
41656           if (find_user_by_name (name, &user))
41657             {
41658               g_list_free (added);
41659               g_strfreev (split);
41660               return -1;
41661             }
41662 
41663           if (user == 0)
41664             {
41665               gchar *uuid;
41666 
41667               if (validate_username (name))
41668                 {
41669                   g_list_free (added);
41670                   g_strfreev (split);
41671                   return 4;
41672                 }
41673 
41674               uuid = user_uuid_any_method (name);
41675 
41676               if (uuid == NULL)
41677                 {
41678                   g_list_free (added);
41679                   g_strfreev (split);
41680                   return -1;
41681                 }
41682 
41683               if (sql_int ("SELECT count(*) FROM users WHERE uuid = '%s';",
41684                            uuid)
41685                   == 0)
41686                 {
41687                   gchar *quoted_name;
41688                   quoted_name = sql_quote (name);
41689                   sql ("INSERT INTO users"
41690                        " (uuid, name, creation_time, modification_time)"
41691                        " VALUES"
41692                        " ('%s', '%s', m_now (), m_now ());",
41693                        uuid,
41694                        quoted_name);
41695                   g_free (quoted_name);
41696 
41697                   user = sql_last_insert_id ();
41698                 }
41699               else
41700                 {
41701                   /* find_user_by_name should have found it. */
41702                   assert (0);
41703                   g_free (uuid);
41704                   g_list_free (added);
41705                   g_strfreev (split);
41706                   return -1;
41707                 }
41708 
41709               g_free (uuid);
41710             }
41711 
41712           if (find_user_by_name_with_permission (name, &user, "get_users"))
41713             {
41714               g_list_free (added);
41715               g_strfreev (split);
41716               return -1;
41717             }
41718 
41719           if (user == 0)
41720             {
41721               g_list_free (added);
41722               g_strfreev (split);
41723               return 99;
41724             }
41725 
41726           sql ("INSERT INTO %s_users (\"%s\", \"user\") VALUES (%llu, %llu);",
41727                type,
41728                type,
41729                resource,
41730                user);
41731 
41732           point++;
41733         }
41734 
41735       g_list_free (added);
41736       g_strfreev (split);
41737     }
41738 
41739   return 0;
41740 }
41741 
41742 /**
41743  * @brief Create a group.
41744  *
41745  * @param[in]   group_name       Group name.
41746  * @param[in]   comment          Comment on group.
41747  * @param[in]   users            Users group applies to.
41748  * @param[in]   special_full     Whether to give group super on itself (full
41749  *                               sharing between members).
41750  * @param[out]  group            Group return.
41751  *
41752  * @return 0 success, 1 group exists already, 2 failed to find user, 4 user
41753  *         name validation failed, 99 permission denied, -1 error.
41754  */
41755 int
create_group(const char * group_name,const char * comment,const char * users,int special_full,group_t * group)41756 create_group (const char *group_name, const char *comment, const char *users,
41757               int special_full, group_t* group)
41758 {
41759   int ret;
41760   gchar *quoted_group_name, *quoted_comment;
41761 
41762   assert (current_credentials.uuid);
41763   assert (group_name);
41764   assert (group);
41765 
41766   sql_begin_immediate ();
41767 
41768   if (acl_user_may ("create_group") == 0)
41769     {
41770       sql_rollback ();
41771       return 99;
41772     }
41773 
41774   if (resource_with_name_exists (group_name, "group", 0))
41775     {
41776       sql_rollback ();
41777       return 1;
41778     }
41779   quoted_group_name = sql_quote (group_name);
41780   quoted_comment = comment ? sql_quote (comment) : g_strdup ("");
41781   sql ("INSERT INTO groups"
41782        " (uuid, name, owner, comment, creation_time, modification_time)"
41783        " VALUES"
41784        " (make_uuid (), '%s',"
41785        "  (SELECT id FROM users WHERE uuid = '%s'),"
41786        "  '%s', m_now (), m_now ());",
41787        quoted_group_name,
41788        current_credentials.uuid,
41789        quoted_comment);
41790   g_free (quoted_comment);
41791   g_free (quoted_group_name);
41792 
41793   *group = sql_last_insert_id ();
41794   ret = add_users ("group", *group, users);
41795 
41796   if (ret)
41797     sql_rollback ();
41798   else
41799     {
41800       if (special_full)
41801         {
41802           char *group_id;
41803 
41804           group_id = group_uuid (*group);
41805           ret = create_permission_internal (1, "Super", NULL, "group", group_id,
41806                                             "group", group_id, NULL);
41807           g_free (group_id);
41808           if (ret)
41809             {
41810               sql_rollback ();
41811               return ret;
41812             }
41813         }
41814       sql_commit ();
41815     }
41816 
41817   return ret;
41818 }
41819 
41820 /**
41821  * @brief Delete a group.
41822  *
41823  * @param[in]  group_id  UUID of group.
41824  * @param[in]  ultimate   Whether to remove entirely, or to trashcan.
41825  *
41826  * @return 0 success, 1 fail because a permission refers to the group, 2 failed
41827  *         to find group, 3 predefined group, 99 permission denied, -1 error.
41828  */
41829 int
delete_group(const char * group_id,int ultimate)41830 delete_group (const char *group_id, int ultimate)
41831 {
41832   group_t group = 0;
41833   GArray *affected_users;
41834   iterator_t users_iter;
41835 
41836   sql_begin_immediate ();
41837 
41838   if (acl_user_may ("delete_group") == 0)
41839     {
41840       sql_rollback ();
41841       return 99;
41842     }
41843 
41844   if (find_group_with_permission (group_id, &group, "delete_group"))
41845     {
41846       sql_rollback ();
41847       return -1;
41848     }
41849 
41850   if (group == 0)
41851     {
41852       if (find_trash ("group", group_id, &group))
41853         {
41854           sql_rollback ();
41855           return -1;
41856         }
41857       if (group == 0)
41858         {
41859           sql_rollback ();
41860           return 2;
41861         }
41862       if (ultimate == 0)
41863         {
41864           /* It's already in the trashcan. */
41865           sql_commit ();
41866           return 0;
41867         }
41868 
41869       if (trash_group_in_use (group))
41870         {
41871           sql_rollback ();
41872           return 1;
41873         }
41874 
41875       sql ("DELETE FROM permissions"
41876            " WHERE resource_type = 'group'"
41877            " AND resource = %llu"
41878            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
41879            group);
41880       sql ("DELETE FROM permissions_trash"
41881            " WHERE resource_type = 'group'"
41882            " AND resource = %llu"
41883            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
41884            group);
41885       sql ("DELETE FROM permissions"
41886            " WHERE subject_type = 'group'"
41887            " AND subject = %llu"
41888            " AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
41889            group);
41890       sql ("DELETE FROM permissions_trash"
41891            " WHERE subject_type = 'group'"
41892            " AND subject = %llu"
41893            " AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
41894            group);
41895 
41896       tags_remove_resource ("group", group, LOCATION_TRASH);
41897 
41898       sql ("DELETE FROM group_users_trash WHERE \"group\" = %llu;", group);
41899       sql ("DELETE FROM groups_trash WHERE id = %llu;", group);
41900       sql_commit ();
41901       return 0;
41902     }
41903 
41904   if (group_in_use (group))
41905     {
41906       sql_rollback ();
41907       return 1;
41908     }
41909 
41910   if (ultimate == 0)
41911     {
41912       group_t trash_group;
41913 
41914       sql ("INSERT INTO groups_trash"
41915            " (uuid, owner, name, comment, creation_time, modification_time)"
41916            " SELECT uuid, owner, name, comment, creation_time,"
41917            "  modification_time"
41918            " FROM groups WHERE id = %llu;",
41919            group);
41920 
41921       trash_group = sql_last_insert_id ();
41922 
41923       sql ("INSERT INTO group_users_trash"
41924            " (\"group\", \"user\")"
41925            " SELECT %llu, \"user\""
41926            " FROM group_users WHERE \"group\" = %llu;",
41927            trash_group,
41928            group);
41929 
41930       permissions_set_locations ("group", group, trash_group, LOCATION_TRASH);
41931       tags_set_locations ("group", group, trash_group, LOCATION_TRASH);
41932       permissions_set_subjects ("group", group, trash_group, LOCATION_TRASH);
41933     }
41934   else
41935     {
41936       sql ("DELETE FROM permissions"
41937            " WHERE resource_type = 'group'"
41938            " AND resource = %llu"
41939            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
41940            group);
41941       sql ("DELETE FROM permissions_trash"
41942            " WHERE resource_type = 'group'"
41943            " AND resource = %llu"
41944            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
41945            group);
41946       sql ("DELETE FROM permissions"
41947            " WHERE subject_type = 'group'"
41948            " AND subject = %llu"
41949            " AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
41950            group);
41951       sql ("DELETE FROM permissions_trash"
41952            " WHERE subject_type = 'group'"
41953            " AND subject = %llu"
41954            " AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
41955            group);
41956     }
41957 
41958   tags_remove_resource ("group", group, LOCATION_TABLE);
41959 
41960   affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
41961   init_iterator (&users_iter,
41962                   "SELECT \"user\" FROM group_users"
41963                   " WHERE \"group\" = %llu",
41964                   group);
41965   while (next (&users_iter))
41966     {
41967       user_t user = iterator_int64 (&users_iter, 0);
41968       g_array_append_val (affected_users, user);
41969     }
41970   cleanup_iterator (&users_iter);
41971 
41972   sql ("DELETE FROM group_users WHERE \"group\" = %llu;", group);
41973   sql ("DELETE FROM groups WHERE id = %llu;", group);
41974 
41975   cache_all_permissions_for_users (affected_users);
41976   g_array_free (affected_users, TRUE);
41977 
41978   sql_commit ();
41979   return 0;
41980 }
41981 
41982 /**
41983  * @brief Return the UUID of a group.
41984  *
41985  * @param[in]  group  Group.
41986  *
41987  * @return Newly allocated UUID if available, else NULL.
41988  */
41989 char*
group_uuid(group_t group)41990 group_uuid (group_t group)
41991 {
41992   return sql_string ("SELECT uuid FROM groups WHERE id = %llu;",
41993                      group);
41994 }
41995 
41996 /**
41997  * @brief Gets users of group as a string.
41998  *
41999  * @param[in]  group  Group.
42000  *
42001  * @return Users.
42002  */
42003 gchar *
group_users(group_t group)42004 group_users (group_t group)
42005 {
42006   return sql_string ("SELECT group_concat (name, ', ')"
42007                      " FROM (SELECT users.name FROM users, group_users"
42008                      "       WHERE group_users.\"group\" = %llu"
42009                      "       AND group_users.user = users.id"
42010                      "       GROUP BY users.name)"
42011                      "      AS sub;",
42012                      group);
42013 }
42014 
42015 /**
42016  * @brief Check whether a group is writable.
42017  *
42018  * @param[in]  group  Group.
42019  *
42020  * @return 1 yes, 0 no.
42021  */
42022 int
group_writable(group_t group)42023 group_writable (group_t group)
42024 {
42025   return 1;
42026 }
42027 
42028 /**
42029  * @brief Check whether a trashcan group is writable.
42030  *
42031  * @param[in]  group  Group.
42032  *
42033  * @return 1 yes, 0 no.
42034  */
42035 int
trash_group_writable(group_t group)42036 trash_group_writable (group_t group)
42037 {
42038   return 1;
42039 }
42040 
42041 /**
42042  * @brief Check whether a group is in use.
42043  *
42044  * @param[in]  group  Group.
42045  *
42046  * @return 1 yes, 0 no.
42047  */
42048 int
group_in_use(group_t group)42049 group_in_use (group_t group)
42050 {
42051   return 0;
42052 }
42053 
42054 /**
42055  * @brief Check whether a trashcan group is in use.
42056  *
42057  * @param[in]  group  Group.
42058  *
42059  * @return 1 yes, 0 no.
42060  */
42061 int
trash_group_in_use(group_t group)42062 trash_group_in_use (group_t group)
42063 {
42064   return 0;
42065 }
42066 
42067 /**
42068  * @brief Filter columns for group iterator.
42069  */
42070 #define GROUP_ITERATOR_FILTER_COLUMNS                                         \
42071  { GET_ITERATOR_FILTER_COLUMNS, NULL }
42072 
42073 /**
42074  * @brief Group iterator columns.
42075  */
42076 #define GROUP_ITERATOR_COLUMNS                                                \
42077  {                                                                            \
42078    GET_ITERATOR_COLUMNS (groups),                                             \
42079    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
42080  }
42081 
42082 /**
42083  * @brief Group iterator columns for trash case.
42084  */
42085 #define GROUP_ITERATOR_TRASH_COLUMNS                                          \
42086  {                                                                            \
42087    GET_ITERATOR_COLUMNS (groups_trash),                                       \
42088    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
42089  }
42090 
42091 /**
42092  * @brief Count number of groups.
42093  *
42094  * @param[in]  get  GET params.
42095  *
42096  * @return Total number of groups in grouped set.
42097  */
42098 int
group_count(const get_data_t * get)42099 group_count (const get_data_t *get)
42100 {
42101   static const char *filter_columns[] = GROUP_ITERATOR_FILTER_COLUMNS;
42102   static column_t columns[] = GROUP_ITERATOR_COLUMNS;
42103   static column_t trash_columns[] = GROUP_ITERATOR_TRASH_COLUMNS;
42104   return count ("group", get, columns, trash_columns, filter_columns,
42105                 0, 0, 0, TRUE);
42106 }
42107 
42108 /**
42109  * @brief Initialise a group iterator, including observed groups.
42110  *
42111  * @param[in]  iterator    Iterator.
42112  * @param[in]  get         GET data.
42113  *
42114  * @return 0 success, 1 failed to find group, 2 failed to find group (filt_id),
42115  *         -1 error.
42116  */
42117 int
init_group_iterator(iterator_t * iterator,const get_data_t * get)42118 init_group_iterator (iterator_t* iterator, const get_data_t *get)
42119 {
42120   static const char *filter_columns[] = GROUP_ITERATOR_FILTER_COLUMNS;
42121   static column_t columns[] = GROUP_ITERATOR_COLUMNS;
42122   static column_t trash_columns[] = GROUP_ITERATOR_TRASH_COLUMNS;
42123 
42124   return init_get_iterator (iterator,
42125                             "group",
42126                             get,
42127                             columns,
42128                             trash_columns,
42129                             filter_columns,
42130                             0,
42131                             NULL,
42132                             NULL,
42133                             TRUE);
42134 }
42135 
42136 /**
42137  * @brief Modify a group.
42138  *
42139  * @param[in]   group_id       UUID of group.
42140  * @param[in]   name           Name of group.
42141  * @param[in]   comment        Comment on group.
42142  * @param[in]   users          Group users.
42143  *
42144  * @return 0 success, 1 failed to find group, 2 failed to find user, 3 group_id
42145  *         required, 4 user name validation failed, 5 group with new name
42146  *         exists, 99 permission denied, -1 internal error.
42147  */
42148 int
modify_group(const char * group_id,const char * name,const char * comment,const char * users)42149 modify_group (const char *group_id, const char *name, const char *comment,
42150               const char *users)
42151 {
42152   int ret;
42153   gchar *quoted_name, *quoted_comment;
42154   group_t group;
42155   GArray *affected_users;
42156   iterator_t users_iter;
42157 
42158   assert (current_credentials.uuid);
42159 
42160   if (group_id == NULL)
42161     return 3;
42162 
42163   sql_begin_immediate ();
42164 
42165   if (acl_user_may ("modify_group") == 0)
42166     {
42167       sql_rollback ();
42168       return 99;
42169     }
42170 
42171   group = 0;
42172 
42173   if (find_group_with_permission (group_id, &group, "modify_group"))
42174     {
42175       sql_rollback ();
42176       return -1;
42177     }
42178 
42179   if (group == 0)
42180     {
42181       sql_rollback ();
42182       return 1;
42183     }
42184 
42185   /* Check whether a group with the same name exists already. */
42186   if (name)
42187     {
42188       if (resource_with_name_exists (name, "group", group))
42189         {
42190           sql_rollback ();
42191           return 5;
42192         }
42193     }
42194 
42195   quoted_name = sql_quote(name ?: "");
42196   quoted_comment = sql_quote (comment ? comment : "");
42197 
42198   sql ("UPDATE groups SET"
42199        " name = '%s',"
42200        " comment = '%s',"
42201        " modification_time = m_now ()"
42202        " WHERE id = %llu;",
42203        quoted_name,
42204        quoted_comment,
42205        group);
42206 
42207   g_free (quoted_comment);
42208   g_free (quoted_name);
42209 
42210   affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
42211   init_iterator (&users_iter,
42212                  "SELECT \"user\" FROM group_users"
42213                  " WHERE \"group\" = %llu",
42214                  group);
42215   while (next (&users_iter))
42216     {
42217       user_t user = iterator_int64 (&users_iter, 0);
42218       g_array_append_val (affected_users, user);
42219     }
42220   cleanup_iterator (&users_iter);
42221 
42222   sql ("DELETE FROM group_users WHERE \"group\" = %llu;", group);
42223 
42224   ret = add_users ("group", group, users);
42225 
42226   init_iterator (&users_iter,
42227                  "SELECT \"user\" FROM group_users"
42228                  " WHERE \"group\" = %llu",
42229                  group);
42230 
42231   // users not looked for in this above loop were removed
42232   //  -> possible permissions change
42233   while (next (&users_iter))
42234     {
42235       int index, found_user;
42236       user_t user = iterator_int64 (&users_iter, 0);
42237 
42238       found_user = 0;
42239       for (index = 0; index < affected_users->len && found_user == 0; index++)
42240         {
42241           if (g_array_index (affected_users, user_t, index) == user)
42242             {
42243               found_user = 1;
42244               break;
42245             }
42246         }
42247 
42248       if (found_user)
42249         {
42250           // users found here stay in the group -> no change in permissions
42251           g_array_remove_index_fast (affected_users, index);
42252         }
42253       else
42254         {
42255           // user added to group -> possible permissions change
42256           g_array_append_val (affected_users, user);
42257         }
42258     }
42259 
42260   cleanup_iterator (&users_iter);
42261 
42262   cache_all_permissions_for_users (affected_users);
42263 
42264   g_array_free (affected_users, TRUE);
42265 
42266   if (ret)
42267     sql_rollback ();
42268   else
42269     sql_commit ();
42270 
42271   return ret;
42272 }
42273 
42274 
42275 /* Permissions. */
42276 
42277 /**
42278  * @brief Adjust location of resource in permissions.
42279  *
42280  * @param[in]   type  Type.
42281  * @param[in]   old   Resource ID in old table.
42282  * @param[in]   new   Resource ID in new table.
42283  * @param[in]   to    Destination, trash or table.
42284  */
42285 void
permissions_set_locations(const char * type,resource_t old,resource_t new,int to)42286 permissions_set_locations (const char *type, resource_t old, resource_t new,
42287                            int to)
42288 {
42289   sql ("UPDATE permissions SET resource_location = %i, resource = %llu"
42290        " WHERE resource_type = '%s' AND resource = %llu"
42291        " AND resource_location = %i;",
42292        to,
42293        new,
42294        type,
42295        old,
42296        to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
42297   sql ("UPDATE permissions_trash SET resource_location = %i, resource = %llu"
42298        " WHERE resource_type = '%s' AND resource = %llu"
42299        " AND resource_location = %i;",
42300        to,
42301        new,
42302        type,
42303        old,
42304        to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
42305 }
42306 
42307 /**
42308  * @brief Set permissions to orphan.
42309  *
42310  * @param[in]  type      Type.
42311  * @param[in]  resource  Resource ID.
42312  * @param[in]  location  Location: table or trash.
42313  */
42314 void
permissions_set_orphans(const char * type,resource_t resource,int location)42315 permissions_set_orphans (const char *type, resource_t resource, int location)
42316 {
42317   sql ("UPDATE permissions SET resource = -1"
42318        " WHERE resource_type = '%s' AND resource = %llu"
42319        " AND resource_location = %i;",
42320        type,
42321        resource,
42322        location);
42323   sql ("UPDATE permissions_trash SET resource = -1"
42324        " WHERE resource_type = '%s' AND resource = %llu"
42325        " AND resource_location = %i;",
42326        type,
42327        resource,
42328        location);
42329 }
42330 
42331 /**
42332  * @brief Adjust subject in permissions.
42333  *
42334  * @param[in]   type  Subject type.
42335  * @param[in]   old   Resource ID in old table.
42336  * @param[in]   new   Resource ID in new table.
42337  * @param[in]   to    Destination, trash or table.
42338  */
42339 static void
permissions_set_subjects(const char * type,resource_t old,resource_t new,int to)42340 permissions_set_subjects (const char *type, resource_t old, resource_t new,
42341                           int to)
42342 {
42343   assert (type && (strcmp (type, "group") == 0 || strcmp (type, "role") == 0));
42344 
42345   sql ("UPDATE permissions"
42346        " SET subject_location = %i, subject = %llu"
42347        " WHERE subject_location = %i"
42348        " AND subject_type = '%s'"
42349        " AND subject = %llu;",
42350        to,
42351        new,
42352        to == LOCATION_TRASH ? LOCATION_TABLE : LOCATION_TRASH,
42353        type,
42354        old);
42355 
42356   sql ("UPDATE permissions_trash"
42357        " SET subject_location = %i, subject = %llu"
42358        " WHERE subject_location = %i"
42359        " AND subject_type = '%s'"
42360        " AND subject = %llu;",
42361        to,
42362        new,
42363        to == LOCATION_TRASH ? LOCATION_TABLE : LOCATION_TRASH,
42364        type,
42365        old);
42366 }
42367 
42368 /**
42369  * @brief Find a permission given a UUID.
42370  *
42371  * @param[in]   uuid        UUID of permission.
42372  * @param[out]  permission  Permission return, 0 if successfully failed to find
42373  *                          permission.
42374  *
42375  * @return FALSE on success (including if failed to find permission), TRUE on
42376  *         error.
42377  */
42378 static gboolean
find_permission(const char * uuid,permission_t * permission)42379 find_permission (const char* uuid, permission_t* permission)
42380 {
42381   return find_resource ("permission", uuid, permission);
42382 }
42383 
42384 /**
42385  * @brief Check args for create_permission or modify_permission.
42386  *
42387  * @param[in]   name_arg        Name of permission.
42388  * @param[in]   resource_type_arg  Type of resource, for special permissions.
42389  * @param[in]   resource_id_arg    UUID of resource.
42390  * @param[in]   subject_type    Type of subject.
42391  * @param[in]   subject_id      UUID of subject.
42392  * @param[out]  name            Name return.
42393  * @param[out]  resource        Resource return.
42394  * @param[out]  resource_type   Resource type return.
42395  * @param[out]  resource_id     Resource ID return.
42396  * @param[out]  subject         Subject return.
42397  *
42398  * @return 0 success, 2 failed to find subject, 3 failed to find resource,
42399  *         5 error in resource, 6 error in subject, 7 error in name,
42400  *         8 permission on permission, 9 permission does not accept resource,
42401  *         99 permission denied, -1 error.
42402  */
42403 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)42404 check_permission_args (const char *name_arg, const char *resource_type_arg,
42405                        const char *resource_id_arg, const char *subject_type,
42406                        const char *subject_id, gchar **name,
42407                        resource_t *resource, char **resource_type,
42408                        const char **resource_id, resource_t *subject)
42409 {
42410   if ((name_arg == NULL)
42411       || ((valid_gmp_command (name_arg) == 0)
42412           && strcasecmp (name_arg, "super"))
42413       || (strcasecmp (name_arg, "get_version") == 0))
42414     return 7;
42415 
42416   if (resource_id_arg
42417       && strcmp (resource_id_arg, "")
42418       && strcmp (resource_id_arg, "0")
42419       && (((gmp_command_takes_resource (name_arg) == 0)
42420            && strcasecmp (name_arg, "super"))))
42421     return 9;
42422 
42423   if (resource_type_arg
42424       && strcasecmp (name_arg, "super") == 0
42425       && strcmp (resource_type_arg, "group")
42426       && strcmp (resource_type_arg, "role")
42427       && strcmp (resource_type_arg, "user"))
42428     return 5;
42429 
42430   if (resource_type_arg
42431       && strcasecmp (name_arg, "super")
42432       && (valid_db_resource_type (resource_type_arg) == 0
42433           || gmp_command_takes_resource (name_arg) == 0))
42434     return 5;
42435 
42436   if (subject_type
42437       && strcmp (subject_type, "group")
42438       && strcmp (subject_type, "role")
42439       && strcmp (subject_type, "user"))
42440     return 6;
42441 
42442   if (subject_id == NULL)
42443     /* For now a permission must have a subject. */
42444     return 6;
42445 
42446   if (subject_id && (subject_type == NULL))
42447     return 6;
42448 
42449   assert (subject_type);
42450 
42451   *name = strcasecmp (name_arg, "super")
42452            ? g_ascii_strdown (name_arg, -1)
42453            : g_strdup ("Super");
42454   *resource = 0;
42455   if (resource_id_arg
42456       && strcmp (resource_id_arg, "")
42457       && strcmp (resource_id_arg, "0"))
42458     {
42459       *resource_type = strcasecmp (*name, "super")
42460                         ? gmp_command_type (*name)
42461                         : g_strdup (resource_type_arg);
42462 
42463       if (*resource_type == NULL)
42464         {
42465           g_free (*name);
42466           return 3;
42467         }
42468 
42469       if (strcasecmp (*resource_type, "asset") == 0)
42470         {
42471           g_free (*resource_type);
42472           *resource_type = g_strdup ("host");
42473           if (find_resource (*resource_type, resource_id_arg, resource))
42474             {
42475               g_free (*name);
42476               g_free (*resource_type);
42477               return -1;
42478             }
42479 
42480           if (*resource == 0)
42481             {
42482               g_free (*resource_type);
42483               *resource_type = g_strdup ("os");
42484               if (find_resource (*resource_type, resource_id_arg, resource))
42485                 {
42486                   g_free (*name);
42487                   g_free (*resource_type);
42488                   return -1;
42489                 }
42490             }
42491         }
42492       else
42493         {
42494           gchar *get_permission;
42495           get_permission = g_strdup_printf ("get_%ss", *resource_type);
42496           if (find_resource_with_permission (*resource_type, resource_id_arg,
42497                                              resource, get_permission, 0))
42498             {
42499               g_free (*name);
42500               g_free (*resource_type);
42501               g_free (get_permission);
42502               return -1;
42503             }
42504           g_free (get_permission);
42505         }
42506 
42507       if (*resource == 0)
42508         {
42509           g_free (*name);
42510           g_free (*resource_type);
42511           return 3;
42512         }
42513 
42514       *resource_id = resource_id_arg;
42515     }
42516   else
42517     {
42518       *resource_id = NULL;
42519       *resource_type = NULL;
42520     }
42521 
42522   if (strcasecmp (*name, "super") == 0)
42523     {
42524       if (*resource == 0)
42525         {
42526           g_free (*name);
42527           g_free (*resource_type);
42528           return 3;
42529         }
42530 
42531       if ((acl_user_is_owner (*resource_type, *resource_id) == 0)
42532           && (acl_user_can_super_everyone (current_credentials.uuid) == 0))
42533         {
42534           g_free (*name);
42535           g_free (*resource_type);
42536           return 99;
42537         }
42538     }
42539 
42540   /* For simplicity refuse to make permissions on permissions. */
42541   if (*resource && strcasestr (*name, "permission"))
42542     {
42543       g_free (*name);
42544       g_free (*resource_type);
42545       return 8;
42546     }
42547 
42548   /* Ensure the user may grant this permission. */
42549 
42550   if (((*resource == 0) || strcasecmp (*name, "super") == 0)
42551       && (acl_user_can_everything (current_credentials.uuid) == 0))
42552     {
42553       g_free (*name);
42554       g_free (*resource_type);
42555       return 99;
42556     }
42557 
42558   *subject = 0;
42559   assert (subject_id);
42560   if (*resource)
42561     {
42562       /* Permission on a particular resource.  Only need read access to the
42563        * subject. */
42564 
42565       if (find_resource_with_permission (subject_type,
42566                                          subject_id,
42567                                          subject,
42568                                          NULL, /* GET permission. */
42569                                          0))   /* Trash. */
42570         {
42571           g_free (*name);
42572           g_free (*resource_type);
42573           return -1;
42574         }
42575     }
42576   else
42577     {
42578       gchar *permission;
42579 
42580       /* Command level permission.  Must have write access to the subject. */
42581 
42582       /* However, modification of the predefined roles is forbidden. */
42583       if (subject_id
42584           && strcmp (subject_type, "role") == 0
42585           && role_is_predefined_id (subject_id))
42586         return 99;
42587 
42588       permission = g_strdup_printf ("modify_%s", subject_type);
42589       if (find_resource_with_permission (subject_type,
42590                                          subject_id,
42591                                          subject,
42592                                          permission,
42593                                          0)) /* Trash. */
42594         {
42595           g_free (*name);
42596           g_free (*resource_type);
42597           g_free (permission);
42598           return -1;
42599         }
42600       g_free (permission);
42601     }
42602 
42603   if (*subject == 0)
42604     {
42605       g_free (*name);
42606       g_free (*resource_type);
42607       return 2;
42608     }
42609 
42610   return 0;
42611 }
42612 
42613 /**
42614  * @brief Create a SQL clause to select the subject users.
42615  *
42616  * @param[in]  subject_type  Subject type.
42617  * @param[in]  subject       The subject.
42618  *
42619  * @return Newly allocated string containing the SQL clause.
42620  */
42621 static gchar*
subject_where_clause(const char * subject_type,resource_t subject)42622 subject_where_clause (const char* subject_type, resource_t subject)
42623 {
42624   gchar *subject_where = NULL;
42625   if (subject && subject_type)
42626     {
42627       if (strcmp (subject_type, "user") == 0)
42628         {
42629           subject_where
42630             = g_strdup_printf ("id = %llu", subject);
42631         }
42632       else if (strcmp (subject_type, "group") == 0)
42633         {
42634           subject_where
42635             = g_strdup_printf ("id IN (SELECT \"user\" FROM group_users"
42636                                "        WHERE \"group\" = %llu)",
42637                                subject);
42638         }
42639       else if (strcmp (subject_type, "role") == 0)
42640         {
42641           subject_where
42642             = g_strdup_printf ("id IN (SELECT \"user\" FROM role_users"
42643                                "        WHERE \"role\" = %llu)",
42644                                subject);
42645         }
42646       else
42647         {
42648           subject_where = strdup ("t()");
42649           g_warning ("%s: unknown subject_type %s",
42650                      __func__, subject_type);
42651         }
42652     }
42653   return subject_where;
42654 }
42655 
42656 /**
42657  * @brief Create a permission.
42658  *
42659  * Caller must organise the transaction.
42660  *
42661  * @param[in]   check_access    Whether to check if user may CREATE_PERMISSION.
42662  * @param[in]   name_arg        Name of permission.
42663  * @param[in]   comment         Comment on permission.
42664  * @param[in]   resource_type_arg  Type of resource, for special permissions.
42665  * @param[in]   resource_id_arg    UUID of resource.
42666  * @param[in]   subject_type    Type of subject.
42667  * @param[in]   subject_id      UUID of subject.
42668  * @param[out]  permission      Permission.
42669  *
42670  * @return 0 success, 2 failed to find subject, 3 failed to find resource,
42671  *         5 error in resource, 6 error in subject, 7 error in name,
42672  *         8 permission on permission, 9 permission does not accept resource,
42673  *         99 permission denied, -1 internal error.
42674  */
42675 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)42676 create_permission_internal (int check_access, const char *name_arg,
42677                             const char *comment, const char *resource_type_arg,
42678                             const char *resource_id_arg,
42679                             const char *subject_type, const char *subject_id,
42680                             permission_t *permission)
42681 {
42682   int ret;
42683   gchar *name, *quoted_name, *quoted_comment, *resource_type;
42684   resource_t resource, subject;
42685   const char *resource_id;
42686   GHashTable *reports = NULL;
42687   int clear_original = 0;
42688   gchar *subject_where;
42689 
42690   assert (current_credentials.uuid);
42691 
42692   if (check_access && (acl_user_may ("create_permission") == 0))
42693     return 99;
42694 
42695   ret = check_permission_args (name_arg, resource_type_arg, resource_id_arg,
42696                                subject_type, subject_id, &name, &resource,
42697                                &resource_type, &resource_id, &subject);
42698   if (ret)
42699     return ret;
42700 
42701   assert (subject);
42702   assert ((resource_id == resource_id_arg) || (resource_id == NULL));
42703 
42704   quoted_name = sql_quote (name);
42705   g_free (name);
42706   quoted_comment = sql_quote (comment ? comment : "");
42707 
42708   sql ("INSERT INTO permissions"
42709        " (uuid, owner, name, comment, resource_type, resource_uuid, resource,"
42710        "  resource_location, subject_type, subject, subject_location,"
42711        "  creation_time, modification_time)"
42712        " VALUES"
42713        " (make_uuid (),"
42714        "  (SELECT id FROM users WHERE users.uuid = '%s'),"
42715        "  '%s', '%s', '%s', '%s', %llu, " G_STRINGIFY (LOCATION_TABLE) ","
42716        "  %s%s%s, %llu, " G_STRINGIFY (LOCATION_TABLE) ", m_now (), m_now ());",
42717        current_credentials.uuid,
42718        quoted_name,
42719        quoted_comment,
42720        resource_id ? resource_type : "",
42721        resource_id ? resource_id : "",
42722        resource,
42723        subject_id ? "'" : "",
42724        subject_id ? subject_type : "NULL",
42725        subject_id ? "'" : "",
42726        subject);
42727 
42728   subject_where = subject_where_clause (subject_type, subject);
42729 
42730   if (permission)
42731     *permission = sql_last_insert_id ();
42732 
42733   /* Update Permissions cache */
42734   if (strcasecmp (quoted_name, "super") == 0)
42735     cache_all_permissions_for_users (NULL);
42736   else if (resource_type && resource)
42737     cache_permissions_for_resource (resource_type, resource, NULL);
42738 
42739   /* Update Reports cache */
42740   if (resource_type && resource_id && strcmp (resource_type, "override") == 0)
42741     {
42742       reports = reports_for_override (resource);
42743     }
42744   else if (strcasecmp (quoted_name, "super") == 0)
42745     {
42746       reports = reports_hashtable ();
42747       clear_original = 1;
42748     }
42749 
42750   if (reports && g_hash_table_size (reports))
42751     {
42752       GHashTableIter reports_iter;
42753       report_t *reports_ptr;
42754       int auto_cache_rebuild;
42755 
42756       reports_ptr = NULL;
42757       g_hash_table_iter_init (&reports_iter, reports);
42758       auto_cache_rebuild = setting_auto_cache_rebuild_int ();
42759       while (g_hash_table_iter_next (&reports_iter,
42760                                     ((gpointer*)&reports_ptr), NULL))
42761         {
42762           if (auto_cache_rebuild)
42763             report_cache_counts (*reports_ptr, clear_original, 1,
42764                                  subject_where);
42765           else
42766             report_clear_count_cache (*reports_ptr, clear_original, 1,
42767                                       subject_where);
42768         }
42769     }
42770 
42771   if (reports)
42772     g_hash_table_destroy (reports);
42773 
42774   g_free (quoted_comment);
42775   g_free (quoted_name);
42776   g_free (resource_type);
42777   g_free (subject_where);
42778 
42779   return 0;
42780 }
42781 
42782 /**
42783  * @brief Create a permission.
42784  *
42785  * @param[in]   name_arg        Name of permission.
42786  * @param[in]   comment         Comment on permission.
42787  * @param[in]   resource_type_arg  Type of resource, for special permissions.
42788  * @param[in]   resource_id_arg    UUID of resource.
42789  * @param[in]   subject_type    Type of subject.
42790  * @param[in]   subject_id      UUID of subject.
42791  * @param[out]  permission      Permission.
42792  *
42793  * @return 0 success, 2 failed to find subject, 3 failed to find resource,
42794  *         5 error in resource, 6 error in subject, 7 error in name,
42795  *         8 permission on permission, 9 permission does not accept resource,
42796  *         99 permission denied, -1 internal error.
42797  */
42798 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)42799 create_permission (const char *name_arg, const char *comment,
42800                    const char *resource_type_arg, const char *resource_id_arg,
42801                    const char *subject_type, const char *subject_id,
42802                    permission_t *permission)
42803 {
42804   int ret;
42805 
42806   sql_begin_immediate ();
42807 
42808   ret = create_permission_internal (1, name_arg, comment, resource_type_arg,
42809                                     resource_id_arg, subject_type, subject_id,
42810                                     permission);
42811   if (ret)
42812     sql_rollback ();
42813   else
42814     sql_commit ();
42815 
42816   return ret;
42817 }
42818 
42819 /**
42820  * @brief Create a permission.
42821  *
42822  * Does not require current user to have CREATE_PERMISSION access.
42823  *
42824  * @param[in]   name_arg        Name of permission.
42825  * @param[in]   comment         Comment on permission.
42826  * @param[in]   resource_type_arg  Type of resource, for special permissions.
42827  * @param[in]   resource_id_arg    UUID of resource.
42828  * @param[in]   subject_type    Type of subject.
42829  * @param[in]   subject_id      UUID of subject.
42830  * @param[out]  permission      Permission.
42831  *
42832  * @return 0 success, 2 failed to find subject, 3 failed to find resource,
42833  *         5 error in resource, 6 error in subject, 7 error in name,
42834  *         8 permission on permission, 9 permission does not accept resource,
42835  *         99 permission denied, -1 internal error.
42836  */
42837 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)42838 create_permission_no_acl (const char *name_arg, const char *comment,
42839                           const char *resource_type_arg,
42840                           const char *resource_id_arg,
42841                           const char *subject_type, const char *subject_id,
42842                           permission_t *permission)
42843 {
42844   return create_permission_internal (0, name_arg, comment, resource_type_arg,
42845                                      resource_id_arg, subject_type, subject_id,
42846                                      permission);
42847 }
42848 
42849 /**
42850  * @brief Create a permission from an existing permission.
42851  *
42852  * @param[in]  comment     Comment on new permission.  NULL to copy from existing.
42853  * @param[in]  permission_id   UUID of existing permission.
42854  * @param[out] new_permission  New permission.
42855  *
42856  * @return 0 success, 1 permission exists already, 2 failed to find existing
42857  *         permission, 99 permission denied, -1 error.
42858  */
42859 int
copy_permission(const char * comment,const char * permission_id,permission_t * new_permission)42860 copy_permission (const char* comment, const char *permission_id,
42861                  permission_t* new_permission)
42862 {
42863   int ret;
42864   permission_t permission, new, old;
42865   char *subject_type, *name;
42866   resource_t subject;
42867 
42868   sql_begin_immediate ();
42869 
42870   permission = 0;
42871   /* There are no permissions on permissions, so no need for the
42872    * "_with_permission" version. */
42873   if (find_permission (permission_id, &permission))
42874     {
42875       sql_rollback ();
42876       return -1;
42877     }
42878 
42879   if (permission == 0)
42880     {
42881       sql_rollback ();
42882       return 2;
42883     }
42884 
42885   /* Prevent copying of command level permissions for predefined roles. */
42886   subject_type = permission_subject_type (permission);
42887   subject = permission_subject (permission);
42888   if (permission_resource (permission) == 0
42889       && subject_type
42890       && strcmp (subject_type, "role") == 0
42891       && subject
42892       && role_is_predefined (subject))
42893     {
42894       free (subject_type);
42895       sql_rollback ();
42896       return 99;
42897     }
42898   free (subject_type);
42899 
42900   /* Refuse to copy Super On Everyone. */
42901   name = permission_name (permission);
42902   if ((strcmp (name, "Super") == 0)
42903       && (permission_resource (permission) == 0))
42904     {
42905       free (name);
42906       sql_rollback ();
42907       return 99;
42908     }
42909   free (name);
42910 
42911   ret = copy_resource_lock ("permission", NULL, comment, permission_id,
42912                             "resource_type, resource, resource_uuid,"
42913                             " resource_location, subject_type, subject,"
42914                             " subject_location",
42915                             0, &new, &old);
42916   if (ret)
42917     {
42918       sql_rollback ();
42919       return ret;
42920     }
42921 
42922   sql_commit ();
42923   if (new_permission) *new_permission = new;
42924   return 0;
42925 
42926 }
42927 
42928 /**
42929  * @brief Return the UUID of a permission.
42930  *
42931  * @param[in]  permission  Permission.
42932  *
42933  * @return Newly allocated UUID if available, else NULL.
42934  */
42935 char*
permission_uuid(permission_t permission)42936 permission_uuid (permission_t permission)
42937 {
42938   return sql_string ("SELECT uuid FROM permissions WHERE id = %llu;",
42939                      permission);
42940 }
42941 
42942 /**
42943  * @brief Return the resource of a permission.
42944  *
42945  * @param[in]  permission  Permission.
42946  *
42947  * @return Resource if there is one, else 0.
42948  */
42949 static resource_t
permission_resource(permission_t permission)42950 permission_resource (permission_t permission)
42951 {
42952   resource_t resource;
42953   sql_int64 (&resource,
42954              "SELECT resource FROM permissions WHERE id = %llu;",
42955              permission);
42956   return resource;
42957 }
42958 
42959 /**
42960  * @brief Return the name of a permission.
42961  *
42962  * @param[in]  permission  Permission.
42963  *
42964  * @return Newly allocated name if available, else NULL.
42965  */
42966 static char *
permission_name(permission_t permission)42967 permission_name (permission_t permission)
42968 {
42969   return sql_string ("SELECT name FROM permissions WHERE id = %llu;",
42970                      permission);
42971 }
42972 
42973 /**
42974  * @brief Return the subject type of a permission.
42975  *
42976  * @param[in]  permission  Permission.
42977  *
42978  * @return Newly allocated subject type if available, else NULL.
42979  */
42980 static char *
permission_subject_type(permission_t permission)42981 permission_subject_type (permission_t permission)
42982 {
42983   return sql_string ("SELECT subject_type FROM permissions WHERE id = %llu;",
42984                      permission);
42985 }
42986 
42987 /**
42988  * @brief Return the subject of a permission.
42989  *
42990  * @param[in]  permission  Permission.
42991  *
42992  * @return Subject if there is one, else 0.
42993  */
42994 static resource_t
permission_subject(permission_t permission)42995 permission_subject (permission_t permission)
42996 {
42997   resource_t subject;
42998   sql_int64 (&subject,
42999              "SELECT subject FROM permissions WHERE id = %llu;",
43000              permission);
43001   return subject;
43002 }
43003 
43004 /**
43005  * @brief Return the UUID of the subject of a permission.
43006  *
43007  * @param[in]  permission  Permission.
43008  *
43009  * @return Newly allocated subject ID if available, else NULL.
43010  */
43011 static char *
permission_subject_id(permission_t permission)43012 permission_subject_id (permission_t permission)
43013 {
43014   return sql_string ("SELECT subject_id FROM permissions WHERE id = %llu;",
43015                      permission);
43016 }
43017 
43018 /**
43019  * @brief Return the resource type of a permission.
43020  *
43021  * @param[in]  permission  Permission.
43022  *
43023  * @return Newly allocated resource type if available, else NULL.
43024  */
43025 static char *
permission_resource_type(permission_t permission)43026 permission_resource_type (permission_t permission)
43027 {
43028   return sql_string ("SELECT resource_type FROM permissions WHERE id = %llu;",
43029                      permission);
43030 }
43031 
43032 /**
43033  * @brief Return the UUID of the resource of a permission.
43034  *
43035  * @param[in]  permission  Permission.
43036  *
43037  * @return Newly allocated resource ID if available, else NULL.
43038  */
43039 static char *
permission_resource_id(permission_t permission)43040 permission_resource_id (permission_t permission)
43041 {
43042   return sql_string ("SELECT resource_id FROM permissions WHERE id = %llu;",
43043                      permission);
43044 }
43045 
43046 /**
43047  * @brief Return whether a permission is predefined.
43048  *
43049  * @param[in]  permission  Permission.
43050  *
43051  * @return 1 if predefined, else 0.
43052  */
43053 static int
permission_is_predefined(permission_t permission)43054 permission_is_predefined (permission_t permission)
43055 {
43056   return !!sql_int ("SELECT COUNT (*) FROM permissions"
43057                     " WHERE id = %llu"
43058                     " AND (uuid = '" PERMISSION_UUID_ADMIN_EVERYTHING "'"
43059                     "      OR (subject_type = 'role'"
43060                     "          AND resource = 0"
43061                     "          AND subject"
43062                     "              IN (SELECT id FROM roles"
43063                     "                  WHERE uuid = '" ROLE_UUID_ADMIN "'"
43064                     "                  OR uuid = '" ROLE_UUID_GUEST "'"
43065                     "                  OR uuid = '" ROLE_UUID_INFO "'"
43066                     "                  OR uuid = '" ROLE_UUID_MONITOR "'"
43067                     "                  OR uuid = '" ROLE_UUID_USER "'"
43068                     "                  OR uuid = '" ROLE_UUID_SUPER_ADMIN "'"
43069                     "                  OR uuid = '" ROLE_UUID_OBSERVER "')))",
43070                     permission);
43071 }
43072 
43073 /**
43074  * @brief Test whether a permission is the special Admin permission.
43075  *
43076  * @param[in]  permission_id  UUID of permission.
43077  *
43078  * @return 1 permission is Admin, else 0.
43079  */
43080 int
permission_is_admin(const char * permission_id)43081 permission_is_admin (const char *permission_id)
43082 {
43083   if (permission_id)
43084     return strcmp (permission_id, PERMISSION_UUID_ADMIN_EVERYTHING);
43085   return 0;
43086 }
43087 
43088 /**
43089  * @brief Return whether a permission is in use.
43090  *
43091  * @param[in]  permission  Permission.
43092  *
43093  * @return 1 if in use, else 0.
43094  */
43095 int
permission_in_use(permission_t permission)43096 permission_in_use (permission_t permission)
43097 {
43098   return 0;
43099 }
43100 
43101 /**
43102  * @brief Return whether a trashcan permission is referenced by a task.
43103  *
43104  * @param[in]  permission  Permission.
43105  *
43106  * @return 1 if in use, else 0.
43107  */
43108 int
trash_permission_in_use(permission_t permission)43109 trash_permission_in_use (permission_t permission)
43110 {
43111   return 0;
43112 }
43113 
43114 /**
43115  * @brief Return whether a permission is writable.
43116  *
43117  * @param[in]  permission  Permission.
43118  *
43119  * @return 1 if writable, else 0.
43120  */
43121 int
permission_writable(permission_t permission)43122 permission_writable (permission_t permission)
43123 {
43124   if (permission_is_predefined (permission))
43125     return 0;
43126   return 1;
43127 }
43128 
43129 /**
43130  * @brief Return whether a trashcan permission is writable.
43131  *
43132  * @param[in]  permission  Permission.
43133  *
43134  * @return 1 if writable, else 0.
43135  */
43136 int
trash_permission_writable(permission_t permission)43137 trash_permission_writable (permission_t permission)
43138 {
43139   return 1;
43140 }
43141 
43142 /**
43143  * @brief Filter columns for permission iterator.
43144  */
43145 #define PERMISSION_ITERATOR_FILTER_COLUMNS                               \
43146  { GET_ITERATOR_FILTER_COLUMNS, "type", "resource_uuid", "subject_type", \
43147    "_subject", "_resource", "subject_uuid", "orphan", NULL }
43148 
43149 /**
43150  * @brief Permission iterator columns.
43151  */
43152 #define PERMISSION_ITERATOR_COLUMNS                                          \
43153  {                                                                           \
43154    GET_ITERATOR_COLUMNS (permissions),                                       \
43155    { "resource_type", "type", KEYWORD_TYPE_STRING },                         \
43156    { "resource_uuid", NULL, KEYWORD_TYPE_STRING },                           \
43157    {                                                                         \
43158      "(CASE"                                                                 \
43159      " WHEN resource_type = '' OR resource_type IS NULL"                     \
43160      " THEN ''"                                                              \
43161      " ELSE resource_name (resource_type, resource_uuid, resource_location)" \
43162      " END)",                                                                \
43163      "_resource",                                                            \
43164      KEYWORD_TYPE_STRING                                                     \
43165    },                                                                        \
43166    { "CAST ((resource_location = " G_STRINGIFY (LOCATION_TRASH) ")"          \
43167      "      AS INTEGER)",                                                    \
43168      NULL,                                                                   \
43169      KEYWORD_TYPE_INTEGER },                                                 \
43170    {                                                                         \
43171      "(CASE"                                                                 \
43172      " WHEN resource = -1"                                                   \
43173      " THEN 1"                                                               \
43174      " ELSE 0"                                                               \
43175      " END)",                                                                \
43176      "orphan",                                                               \
43177      KEYWORD_TYPE_INTEGER                                                    \
43178    },                                                                        \
43179    { "subject_type", NULL, KEYWORD_TYPE_STRING },                            \
43180    {                                                                         \
43181      "(CASE"                                                                 \
43182      " WHEN subject_type = 'user'"                                           \
43183      " THEN (SELECT uuid FROM users WHERE users.id = subject)"               \
43184      " WHEN subject_type = 'group'"                                          \
43185      "      AND subject_location = " G_STRINGIFY (LOCATION_TRASH)            \
43186      " THEN (SELECT uuid FROM groups_trash"                                  \
43187      "       WHERE groups_trash.id = subject)"                               \
43188      " WHEN subject_type = 'group'"                                          \
43189      " THEN (SELECT uuid FROM groups WHERE groups.id = subject)"             \
43190      " WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH)                \
43191      " THEN (SELECT uuid FROM roles_trash"                                   \
43192      "       WHERE roles_trash.id = subject)"                                \
43193      " ELSE (SELECT uuid FROM roles WHERE roles.id = subject)"               \
43194      " END)",                                                                \
43195      "subject_uuid",                                                         \
43196      KEYWORD_TYPE_STRING                                                     \
43197    },                                                                        \
43198    {                                                                         \
43199      "(CASE"                                                                 \
43200      " WHEN subject_type = 'user'"                                           \
43201      " THEN (SELECT name FROM users WHERE users.id = subject)"               \
43202      " WHEN subject_type = 'group'"                                          \
43203      "      AND subject_location = " G_STRINGIFY (LOCATION_TRASH)            \
43204      " THEN (SELECT name FROM groups_trash"                                  \
43205      "       WHERE groups_trash.id = subject)"                               \
43206      " WHEN subject_type = 'group'"                                          \
43207      " THEN (SELECT name FROM groups WHERE groups.id = subject)"             \
43208      " WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH)                \
43209      " THEN (SELECT name FROM roles_trash"                                   \
43210      "       WHERE roles_trash.id = subject)"                                \
43211      " ELSE (SELECT name FROM roles WHERE roles.id = subject)"               \
43212      " END)",                                                                \
43213      "_subject",                                                             \
43214      KEYWORD_TYPE_STRING                                                     \
43215    },                                                                        \
43216    { "CAST ((subject_location = " G_STRINGIFY (LOCATION_TRASH) ")"           \
43217      "      AS INTEGER)",                                                    \
43218      NULL,                                                                   \
43219      KEYWORD_TYPE_INTEGER },                                                 \
43220    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
43221  }
43222 
43223 /**
43224  * @brief Permission iterator columns for trash case.
43225  */
43226 #define PERMISSION_ITERATOR_TRASH_COLUMNS                                    \
43227  {                                                                           \
43228    GET_ITERATOR_COLUMNS (permissions_trash),                                 \
43229    { "resource_type", "type", KEYWORD_TYPE_STRING },                         \
43230    { "resource_uuid", NULL, KEYWORD_TYPE_STRING },                           \
43231    {                                                                         \
43232      "(CASE"                                                                 \
43233      " WHEN resource_type = '' OR resource_type IS NULL"                     \
43234      " THEN ''"                                                              \
43235      " ELSE resource_name (resource_type, resource_uuid, resource_location)" \
43236      " END)",                                                                \
43237      "_resource",                                                            \
43238      KEYWORD_TYPE_STRING                                                     \
43239    },                                                                        \
43240    { "CAST ((resource_location = " G_STRINGIFY (LOCATION_TRASH) ")"          \
43241      "      AS INTEGER)",                                                    \
43242      NULL,                                                                   \
43243      KEYWORD_TYPE_INTEGER },                                                 \
43244    { "resource = -1", NULL, KEYWORD_TYPE_INTEGER },                          \
43245    { "subject_type", NULL, KEYWORD_TYPE_STRING },                            \
43246    {                                                                         \
43247      "(CASE"                                                                 \
43248      " WHEN subject_type = 'user'"                                           \
43249      " THEN (SELECT uuid FROM users WHERE users.id = subject)"               \
43250      " WHEN subject_type = 'group'"                                          \
43251      "      AND subject_location = " G_STRINGIFY (LOCATION_TRASH)            \
43252      " THEN (SELECT uuid FROM groups_trash"                                  \
43253      "       WHERE groups_trash.id = subject)"                               \
43254      " WHEN subject_type = 'group'"                                          \
43255      " THEN (SELECT uuid FROM groups WHERE groups.id = subject)"             \
43256      " WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH)                \
43257      " THEN (SELECT uuid FROM roles_trash"                                   \
43258      "       WHERE roles_trash.id = subject)"                                \
43259      " ELSE (SELECT uuid FROM roles WHERE roles.id = subject)"               \
43260      " END)",                                                                \
43261      "subject_uuid",                                                         \
43262      KEYWORD_TYPE_STRING                                                     \
43263    },                                                                        \
43264    {                                                                         \
43265      "(CASE"                                                                 \
43266      " WHEN subject_type = 'user'"                                           \
43267      " THEN (SELECT name FROM users WHERE users.id = subject)"               \
43268      " WHEN subject_type = 'group'"                                          \
43269      "      AND subject_location = " G_STRINGIFY (LOCATION_TRASH)            \
43270      " THEN (SELECT name FROM groups_trash"                                  \
43271      "       WHERE groups_trash.id = subject)"                               \
43272      " WHEN subject_type = 'group'"                                          \
43273      " THEN (SELECT name FROM groups WHERE groups.id = subject)"             \
43274      " WHEN subject_location = " G_STRINGIFY (LOCATION_TRASH)                \
43275      " THEN (SELECT name FROM roles_trash"                                   \
43276      "       WHERE roles_trash.id = subject)"                                \
43277      " ELSE (SELECT name FROM roles WHERE roles.id = subject)"               \
43278      " END)",                                                                \
43279      "_subject",                                                             \
43280      KEYWORD_TYPE_STRING                                                     \
43281    },                                                                        \
43282    { "CAST ((subject_location = " G_STRINGIFY (LOCATION_TRASH) ")"           \
43283      "      AS INTEGER)",                                                    \
43284      NULL,                                                                   \
43285      KEYWORD_TYPE_INTEGER },                                                 \
43286    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
43287  }
43288 
43289 /**
43290  * @brief Count number of permissions.
43291  *
43292  * @param[in]  get  GET params.
43293  *
43294  * @return Total number of permissions in filtered set.
43295  */
43296 int
permission_count(const get_data_t * get)43297 permission_count (const get_data_t *get)
43298 {
43299   static const char *filter_columns[] = PERMISSION_ITERATOR_FILTER_COLUMNS;
43300   static column_t columns[] = PERMISSION_ITERATOR_COLUMNS;
43301   static column_t trash_columns[] = PERMISSION_ITERATOR_TRASH_COLUMNS;
43302 
43303   return count ("permission", get, columns, trash_columns, filter_columns,
43304                 0, 0, 0, TRUE);
43305 }
43306 
43307 /**
43308  * @brief Initialise a permission iterator.
43309  *
43310  * @param[in]  iterator    Iterator.
43311  * @param[in]  get         GET data.
43312  *
43313  * @return 0 success, 1 failed to find target, 2 failed to find filter,
43314  *         -1 error.
43315  */
43316 int
init_permission_iterator(iterator_t * iterator,const get_data_t * get)43317 init_permission_iterator (iterator_t* iterator, const get_data_t *get)
43318 {
43319   static const char *filter_columns[] = PERMISSION_ITERATOR_FILTER_COLUMNS;
43320   static column_t columns[] = PERMISSION_ITERATOR_COLUMNS;
43321   static column_t trash_columns[] = PERMISSION_ITERATOR_TRASH_COLUMNS;
43322 
43323   return init_get_iterator (iterator,
43324                             "permission",
43325                             get,
43326                             columns,
43327                             trash_columns,
43328                             filter_columns,
43329                             0,
43330                             NULL,
43331                             NULL,
43332                             TRUE);
43333 }
43334 
43335 /**
43336  * @brief Get the type of resource from a permission iterator.
43337  *
43338  * @param[in]  iterator  Iterator.
43339  *
43340  * @return Type, or NULL if iteration is complete.
43341  */
43342 DEF_ACCESS (permission_iterator_resource_type, GET_ITERATOR_COLUMN_COUNT);
43343 
43344 /**
43345  * @brief Get the UUID of the resource from a permission iterator.
43346  *
43347  * @param[in]  iterator  Iterator.
43348  *
43349  * @return UUID, or NULL if iteration is complete.
43350  */
43351 DEF_ACCESS (permission_iterator_resource_uuid, GET_ITERATOR_COLUMN_COUNT + 1);
43352 
43353 /**
43354  * @brief Get the name of the resource from a permission iterator.
43355  *
43356  * @param[in]  iterator  Iterator.
43357  *
43358  * @return Name, or NULL if iteration is complete.
43359  */
43360 DEF_ACCESS (permission_iterator_resource_name, GET_ITERATOR_COLUMN_COUNT + 2);
43361 
43362 /**
43363  * @brief Return the permission resource location.
43364  *
43365  * @param[in]  iterator  Iterator.
43366  *
43367  * @return Whether the resource is in the trashcan
43368  */
43369 int
permission_iterator_resource_in_trash(iterator_t * iterator)43370 permission_iterator_resource_in_trash (iterator_t* iterator)
43371 {
43372   if (iterator->done) return 0;
43373   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
43374 }
43375 
43376 /**
43377  * @brief Check if the permission resource has been deleted.
43378  *
43379  * @param[in]  iterator  Iterator.
43380  *
43381  * @return Whether the resource has been deleted.
43382  */
43383 int
permission_iterator_resource_orphan(iterator_t * iterator)43384 permission_iterator_resource_orphan (iterator_t* iterator)
43385 {
43386   if (iterator->done) return 0;
43387   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
43388 }
43389 
43390 /**
43391  * @brief Get the readable status of a resource from a permission iterator.
43392  *
43393  * @param[in]  iterator  Iterator.
43394  *
43395  * @return 1 if readable, otherwise 0.
43396  */
43397 int
permission_iterator_resource_readable(iterator_t * iterator)43398 permission_iterator_resource_readable (iterator_t* iterator)
43399 {
43400   resource_t found;
43401   const char *type, *uuid;
43402   gchar *permission;
43403 
43404   if (iterator->done) return 0;
43405 
43406   type = permission_iterator_resource_type (iterator);
43407   uuid = permission_iterator_resource_uuid (iterator);
43408 
43409   if (type == NULL || uuid == NULL)
43410     return 0;
43411 
43412   if (type_is_info_subtype (type))
43413     permission = g_strdup ("get_info");
43414   else if (type_is_asset_subtype (type))
43415     permission = g_strdup ("get_assets");
43416   else
43417     permission = g_strdup_printf ("get_%ss", type);
43418 
43419   found = 0;
43420   find_resource_with_permission (type,
43421                                  uuid,
43422                                  &found,
43423                                  permission,
43424                                  permission_iterator_resource_in_trash
43425                                   (iterator));
43426   g_free (permission);
43427   return found > 0;
43428 }
43429 
43430 /**
43431  * @brief Get the type of subject from a permission iterator.
43432  *
43433  * @param[in]  iterator  Iterator.
43434  *
43435  * @return Type, or NULL if iteration is complete.
43436  */
43437 DEF_ACCESS (permission_iterator_subject_type, GET_ITERATOR_COLUMN_COUNT + 5);
43438 
43439 /**
43440  * @brief Get the subject UUID from a permission iterator.
43441  *
43442  * @param[in]  iterator  Iterator.
43443  *
43444  * @return UUID, or NULL if iteration is complete.
43445  */
43446 DEF_ACCESS (permission_iterator_subject_uuid, GET_ITERATOR_COLUMN_COUNT + 6);
43447 
43448 /**
43449  * @brief Get the subject name from a permission iterator.
43450  *
43451  * @param[in]  iterator  Iterator.
43452  *
43453  * @return Name, or NULL if iteration is complete.
43454  */
43455 DEF_ACCESS (permission_iterator_subject_name, GET_ITERATOR_COLUMN_COUNT + 7);
43456 
43457 /**
43458  * @brief Return the permission subject location.
43459  *
43460  * @param[in]  iterator  Iterator.
43461  *
43462  * @return Whether the subject is in the trashcan
43463  */
43464 int
permission_iterator_subject_in_trash(iterator_t * iterator)43465 permission_iterator_subject_in_trash (iterator_t* iterator)
43466 {
43467   if (iterator->done) return 0;
43468   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 8);
43469 }
43470 
43471 /**
43472  * @brief Get the readable status of a subject from a permission iterator.
43473  *
43474  * @param[in]  iterator  Iterator.
43475  *
43476  * @return 1 if readable, otherwise 0.
43477  */
43478 int
permission_iterator_subject_readable(iterator_t * iterator)43479 permission_iterator_subject_readable (iterator_t* iterator)
43480 {
43481   resource_t found;
43482   const char *type, *uuid;
43483   gchar *permission;
43484 
43485   if (iterator->done) return 0;
43486 
43487   type = permission_iterator_subject_type (iterator);
43488   uuid = permission_iterator_subject_uuid (iterator);
43489 
43490   if (type == NULL || uuid == NULL)
43491     return 0;
43492 
43493   if ((strcmp (type, "user") == 0)
43494       || (strcmp (type, "role") == 0)
43495       || (strcmp (type, "group") == 0))
43496     permission = g_strdup_printf ("get_%ss", type);
43497   else
43498     return 0;
43499 
43500   found = 0;
43501   find_resource_with_permission (type,
43502                                  uuid,
43503                                  &found,
43504                                  permission,
43505                                  permission_iterator_subject_in_trash
43506                                   (iterator));
43507   g_free (permission);
43508   return found > 0;
43509 }
43510 
43511 /**
43512  * @brief Find a permission with a given permission, given a UUID.
43513  *
43514  * @param[in]   uuid        UUID of permission.
43515  * @param[out]  resource    Permission return, 0 if successfully failed to find
43516  *                          permission.
43517  * @param[in]   permission  Required permission, for example "delete".
43518  *
43519  * @return FALSE on success (including if failed to find permission), TRUE on
43520  *         error.
43521  */
43522 static gboolean
find_permission_with_permission(const char * uuid,permission_t * resource,const char * permission)43523 find_permission_with_permission (const char *uuid, permission_t *resource,
43524                                  const char *permission)
43525 {
43526   gchar *quoted_uuid = sql_quote (uuid);
43527   if (acl_user_has_access_uuid ("permission", quoted_uuid, permission, 0) == 0)
43528     {
43529       g_free (quoted_uuid);
43530       *resource = 0;
43531       return FALSE;
43532     }
43533   switch (sql_int64 (resource,
43534                      "SELECT id FROM permissions WHERE uuid = '%s';",
43535                      quoted_uuid))
43536     {
43537       case 0:
43538         break;
43539       case 1:        /* Too few rows in result of query. */
43540         *resource = 0;
43541         break;
43542       default:       /* Programming error. */
43543         assert (0);
43544       case -1:
43545         g_free (quoted_uuid);
43546         return TRUE;
43547         break;
43548     }
43549 
43550   g_free (quoted_uuid);
43551   return FALSE;
43552 }
43553 
43554 /**
43555  * @brief Delete a permission.
43556  *
43557  * @param[in]  permission_id  UUID of permission.
43558  * @param[in]  ultimate       Whether to remove entirely, or to trashcan.
43559  *
43560  * @return 0 success, 2 failed to find permission, 3 predefined permission,
43561  *         99 permission denied, -1 error.
43562  */
43563 int
delete_permission(const char * permission_id,int ultimate)43564 delete_permission (const char *permission_id, int ultimate)
43565 {
43566   permission_t permission = 0;
43567   char *name, *subject_type, *resource_type;
43568   resource_t subject, resource;
43569   GHashTable *reports = NULL;
43570   int clear_original = 0;
43571   gchar *subject_where;
43572 
43573   if (strcasecmp (permission_id, PERMISSION_UUID_ADMIN_EVERYTHING) == 0)
43574     return 3;
43575 
43576   sql_begin_immediate ();
43577 
43578   if (acl_user_may ("delete_permission") == 0)
43579     {
43580       sql_rollback ();
43581       return 99;
43582     }
43583 
43584   if (find_permission_with_permission (permission_id, &permission,
43585                                        "delete_permission"))
43586     {
43587       sql_rollback ();
43588       return -1;
43589     }
43590 
43591   if (permission == 0)
43592     {
43593       if (find_trash ("permission", permission_id, &permission))
43594         {
43595           sql_rollback ();
43596           return -1;
43597         }
43598       if (permission == 0)
43599         {
43600           sql_rollback ();
43601           return 2;
43602         }
43603       if (ultimate == 0)
43604         {
43605           /* It's already in the trashcan. */
43606           sql_commit ();
43607           return 0;
43608         }
43609 
43610       tags_remove_resource ("permission", permission, LOCATION_TRASH);
43611       sql ("DELETE FROM permissions_trash WHERE id = %llu;", permission);
43612       sql_commit ();
43613       return 0;
43614     }
43615 
43616   /* Prevent deletion of command level permissions for predefined roles. */
43617   subject_type = permission_subject_type (permission);
43618   subject = permission_subject (permission);
43619   resource = permission_resource (permission);
43620   if (resource == 0
43621       && subject_type
43622       && strcmp (subject_type, "role") == 0
43623       && subject
43624       && role_is_predefined (subject))
43625     {
43626       free (subject_type);
43627       sql_rollback ();
43628       return 99;
43629     }
43630   subject_where = subject_where_clause (subject_type, subject);
43631   free (subject_type);
43632 
43633   if (ultimate == 0)
43634     {
43635       sql ("INSERT INTO permissions_trash"
43636           " (uuid, owner, name, comment, resource_type, resource,"
43637           "  resource_uuid, resource_location, subject_type, subject,"
43638           "  subject_location, creation_time, modification_time)"
43639           " SELECT uuid, owner, name, comment, resource_type, resource,"
43640           "  resource_uuid, resource_location, subject_type, subject,"
43641           "  subject_location, creation_time, modification_time"
43642           " FROM permissions"
43643           " WHERE id = %llu;",
43644           permission);
43645       tags_set_locations ("permission", permission,
43646                           sql_last_insert_id (),
43647                           LOCATION_TRASH);
43648     }
43649   else
43650     tags_remove_resource ("permission", permission, LOCATION_TABLE);
43651 
43652   name = permission_name (permission);
43653   resource_type = permission_resource_type (permission);
43654 
43655   sql ("DELETE FROM permissions WHERE id = %llu;", permission);
43656 
43657   /* Update Permissions cache */
43658   if (strcasecmp (name, "super") == 0)
43659     cache_all_permissions_for_users (NULL);
43660   else if (resource_type && resource)
43661     cache_permissions_for_resource (resource_type, resource, NULL);
43662 
43663   /* Update Reports cache */
43664   if (resource_type && (resource > 0) && strcmp (resource_type, "override")
43665       == 0)
43666     {
43667       reports = reports_for_override (resource);
43668     }
43669   else if (strcasecmp (name, "super") == 0)
43670     {
43671       reports = reports_hashtable ();
43672       clear_original = 1;
43673     }
43674   free (name);
43675   free (resource_type);
43676 
43677   if (reports && g_hash_table_size (reports))
43678     {
43679       GHashTableIter reports_iter;
43680       report_t *reports_ptr;
43681       int auto_cache_rebuild;
43682 
43683       reports_ptr = NULL;
43684       g_hash_table_iter_init (&reports_iter, reports);
43685       auto_cache_rebuild = setting_auto_cache_rebuild_int ();
43686       while (g_hash_table_iter_next (&reports_iter,
43687                                     ((gpointer*)&reports_ptr), NULL))
43688         {
43689           if (auto_cache_rebuild)
43690             report_cache_counts (*reports_ptr, clear_original, 1,
43691                                  subject_where);
43692           else
43693             report_clear_count_cache (*reports_ptr, clear_original, 1,
43694                                       subject_where);
43695         }
43696     }
43697 
43698   g_free (subject_where);
43699 
43700   if (reports)
43701     g_hash_table_destroy (reports);
43702 
43703   sql_commit ();
43704   return 0;
43705 }
43706 
43707 /**
43708  * @brief Modify a permission.
43709  *
43710  * @param[in]   permission_id      UUID of permission.
43711  * @param[in]   name_arg           Name of permission.
43712  * @param[in]   comment            Comment on permission.
43713  * @param[in]   resource_id_arg    UUID of resource.
43714  * @param[in]   resource_type_arg  Type of resource, for Super permissions.
43715  * @param[in]   subject_type       Type of subject.
43716  * @param[in]   subject_id         UUID of subject.
43717  *
43718  * @return 0 success, 1 failed to find permission, 2 failed to find subject,
43719  *         3 failed to find resource, 4 permission_id required, 5 error in
43720  *         resource, 6 error in subject, 7 error in name, 8 name required to
43721  *         find resource, 9 permission does not accept resource, 99 permission
43722  *         denied, -1 internal error.
43723  */
43724 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)43725 modify_permission (const char *permission_id, const char *name_arg,
43726                    const char *comment, const char *resource_id_arg,
43727                    const char *resource_type_arg, const char *subject_type,
43728                    const char *subject_id)
43729 {
43730   int ret;
43731   permission_t permission;
43732   resource_t resource, subject;
43733   char *existing_subject_type, *new_name, *new_resource_type;
43734   char *new_resource_id, *new_subject_type, *new_subject_id;
43735   gchar *name, *quoted_name, *resource_type;
43736   const char *resource_id;
43737   char *old_name, *old_resource_type;
43738   resource_t old_resource;
43739   GHashTable *reports = NULL;
43740   int clear_original = 0;
43741   gchar *subject_where_old, *subject_where_new, *subject_where;
43742 
43743   if (permission_id == NULL)
43744     return 4;
43745 
43746   sql_begin_immediate ();
43747 
43748   if (acl_user_may ("modify_permission") == 0)
43749     {
43750       sql_rollback ();
43751       return 99;
43752     }
43753 
43754   /* Find the permission. */
43755 
43756   permission = 0;
43757   /* There are no permissions on permissions, so no need for the
43758    * "_with_permission" version. */
43759   if (find_permission (permission_id, &permission))
43760     {
43761       sql_rollback ();
43762       return -1;
43763     }
43764 
43765   if (permission == 0)
43766     {
43767       sql_rollback ();
43768       return 1;
43769     }
43770 
43771   /* Refuse to modify command-level permissions on predefined roles. */
43772 
43773   existing_subject_type = permission_subject_type (permission);
43774   resource = permission_resource (permission);
43775   subject = permission_subject (permission);
43776   if (resource == 0
43777       && existing_subject_type
43778       && strcmp (existing_subject_type, "role") == 0
43779       && subject
43780       && role_is_predefined (subject))
43781     {
43782       free (existing_subject_type);
43783       sql_rollback ();
43784       return 99;
43785     }
43786 
43787   /* Get old subject clause */
43788   subject_where_old = subject_where_clause (existing_subject_type, subject);
43789 
43790   /* Set the comment first, to make things easier. */
43791 
43792   if (comment)
43793     {
43794       gchar *quoted_comment;
43795 
43796       quoted_comment = sql_quote (comment);
43797       sql ("UPDATE permissions SET"
43798            " comment = '%s',"
43799            " modification_time = m_now ()"
43800            " WHERE id = %llu;",
43801            quoted_comment,
43802            permission);
43803       g_free (quoted_comment);
43804     }
43805 
43806   /* Check the arguments. */
43807 
43808   new_name = name_arg ? NULL : permission_name (permission);
43809   if (resource_type_arg && resource_id_arg && strcmp (resource_id_arg, "0"))
43810     /* Given a resource. */
43811     new_resource_type = NULL;
43812   else if (resource_id_arg && (strcmp (resource_id_arg, "0") == 0))
43813     /* User wants to clear the resource. */
43814     new_resource_type = NULL;
43815   else
43816     {
43817       new_resource_type = permission_resource_type (permission);
43818       if (new_resource_type
43819           && strcmp (new_resource_type, "group")
43820           && strcmp (new_resource_type, "role")
43821           && strcmp (new_resource_type, "user"))
43822         /* Type will come from command name. */
43823         new_resource_type = NULL;
43824     }
43825 
43826   new_resource_id = (resource_id_arg && strcmp (resource_id_arg, ""))
43827                      ? NULL
43828                      : permission_resource_id (permission);
43829   new_subject_type = subject_type ? NULL : existing_subject_type;
43830   new_subject_id = subject_id ? NULL : permission_subject_id (permission);
43831 
43832   ret = check_permission_args
43833          (new_name ? new_name : name_arg,
43834           new_resource_type ? new_resource_type : resource_type_arg,
43835           new_resource_id ? new_resource_id : resource_id_arg,
43836           new_subject_type ? new_subject_type : subject_type,
43837           new_subject_id ? new_subject_id : subject_id,
43838           &name,
43839           &resource,
43840           &resource_type,
43841           &resource_id,
43842           &subject);
43843 
43844   free (new_name);
43845 
43846   if (ret)
43847     {
43848       free (new_resource_type);
43849       free (new_resource_id);
43850       free (existing_subject_type);
43851       free (new_subject_id);
43852       g_free (subject_where_old);
43853       sql_rollback ();
43854       return ret;
43855     }
43856 
43857   subject_where_new = subject_where_clause (new_subject_type
43858                                               ? new_subject_type
43859                                               : subject_type,
43860                                             subject);
43861 
43862   if (strcmp (subject_where_new, subject_where_old))
43863     {
43864       subject_where = g_strdup_printf ("(%s) OR (%s)",
43865                                        subject_where_new, subject_where_old);
43866       g_free (subject_where_new);
43867       g_free (subject_where_old);
43868     }
43869   else
43870     {
43871       subject_where = subject_where_old;
43872       g_free (subject_where_new);
43873     }
43874 
43875   /* Get old values and check if caches are affected by previous resource. */
43876 
43877   old_name = permission_name (permission);
43878   old_resource_type = permission_resource_type (permission);
43879   old_resource = permission_resource (permission);
43880 
43881 
43882   if (old_resource
43883       && strcmp (old_resource_type, "override") == 0)
43884     {
43885       reports = reports_for_override (resource);
43886     }
43887   else if (strcasecmp (old_name, "super"))
43888     {
43889       reports = reports_hashtable ();
43890       clear_original = 1;
43891     }
43892   else
43893     {
43894       reports = new_resources_hashtable ();
43895     }
43896 
43897   /* Modify the permission. */
43898 
43899   assert (subject);
43900   assert ((resource_id == new_resource_id)
43901           || (resource_id == resource_id_arg)
43902           || (resource_id == NULL));
43903 
43904   quoted_name = sql_quote (name);
43905 
43906   sql ("UPDATE permissions SET"
43907        " name = '%s',"
43908        " resource_type = '%s',"
43909        " resource_uuid = '%s',"
43910        " resource = %llu,"
43911        " resource_location = " G_STRINGIFY (LOCATION_TABLE) ","
43912        " subject_type = '%s',"
43913        " subject = %llu,"
43914        " modification_time = m_now ()"
43915        " WHERE id = %llu;",
43916        quoted_name,
43917        (resource_id && resource_type) ? resource_type : "",
43918        resource_id ? resource_id : "",
43919        resource,
43920        new_subject_type ? new_subject_type : subject_type,
43921        subject,
43922        permission);
43923 
43924   /* Update permission caches according to the modifications. */
43925 
43926   if (strcasecmp (name, "super") == 0 || strcasecmp (old_name, "super") == 0)
43927     cache_all_permissions_for_users (NULL);
43928   else
43929     {
43930       if (resource_type && resource_id && strcmp (resource_id, ""))
43931         cache_permissions_for_resource (resource_type, resource, NULL);
43932 
43933       if (old_resource
43934           && old_resource_type
43935           && ((resource != old_resource)
43936               || (resource_type
43937                   && strcmp (old_resource_type, resource_type))))
43938         cache_permissions_for_resource (old_resource_type, old_resource, NULL);
43939     }
43940 
43941   /* Check if caches are affected by the permission and update reports cache */
43942 
43943   if (resource_type
43944       && resource
43945       && (resource != old_resource
43946           || strcmp (old_resource_type, "override"))
43947       && strcmp (resource_type, "override") == 0)
43948     {
43949       reports_add_for_override (reports, resource);
43950     }
43951   else if (strcasecmp (quoted_name, "super") == 0
43952            && strcasecmp (old_name, quoted_name))
43953     {
43954       reports_add_all (reports);
43955       clear_original = 1;
43956     }
43957 
43958   if (reports)
43959     {
43960       GHashTableIter reports_iter;
43961       report_t *reports_ptr;
43962       int auto_cache_rebuild;
43963 
43964       g_hash_table_iter_init (&reports_iter, reports);
43965       reports_ptr = NULL;
43966       auto_cache_rebuild = setting_auto_cache_rebuild_int ();
43967       while (g_hash_table_iter_next (&reports_iter,
43968                                     ((gpointer*)&reports_ptr), NULL))
43969         {
43970           if (auto_cache_rebuild)
43971             report_cache_counts (*reports_ptr, clear_original, 1,
43972                                  subject_where);
43973           else
43974             report_clear_count_cache (*reports_ptr, clear_original, 1,
43975                                       subject_where);
43976         }
43977       g_hash_table_destroy (reports);
43978       reports = NULL;
43979     }
43980 
43981   /* Cleanup. */
43982 
43983   g_free (quoted_name);
43984   free (new_resource_type);
43985   free (new_resource_id);
43986   free (existing_subject_type);
43987   free (new_subject_id);
43988   g_free (name);
43989   free (old_name);
43990   free (old_resource_type);
43991   g_free (subject_where);
43992 
43993   sql_commit ();
43994 
43995   return 0;
43996 }
43997 
43998 /**
43999  * @brief Add role permissions to feed objects according to the
44000  *        'Feed Import Roles' setting.
44001  *
44002  * @param[in]  type             The object type, e.g. report_format.
44003  * @param[in]  type_cap         Capitalized type, e.g. "Report Format"
44004  * @param[out] permission_count Number of permissions added.
44005  * @param[out] object_count     Number of data objects affected.
44006  */
44007 static void
add_feed_role_permissions(const char * type,const char * type_cap,int * permission_count,int * object_count)44008 add_feed_role_permissions (const char *type,
44009                            const char *type_cap,
44010                            int *permission_count,
44011                            int *object_count)
44012 {
44013   char *roles_str;
44014   gchar **roles;
44015   iterator_t resources;
44016 
44017   roles_str = NULL;
44018   setting_value (SETTING_UUID_FEED_IMPORT_ROLES, &roles_str);
44019 
44020   if (roles_str == NULL || strlen (roles_str) == 0)
44021     {
44022       g_message ("%s: No feed import roles defined", __func__);
44023       g_free (roles_str);
44024       return;
44025     }
44026 
44027   roles = g_strsplit (roles_str, ",", 0);
44028   free (roles_str);
44029 
44030   init_iterator (&resources,
44031                  "SELECT id, uuid, name, owner FROM %ss"
44032                  " WHERE predefined = 1",
44033                  type);
44034   while (next (&resources))
44035     {
44036       gboolean added_permission = FALSE;
44037       resource_t permission_resource = iterator_int64 (&resources, 0);
44038       const char *permission_resource_id = iterator_string (&resources, 1);
44039       const char *permission_resource_name = iterator_string (&resources, 2);
44040       user_t owner = iterator_int64 (&resources, 3);
44041       gchar **role = roles;
44042 
44043       while (*role)
44044         {
44045           char *role_name = NULL;
44046           resource_name ("role", *role, LOCATION_TABLE, &role_name);
44047 
44048           if (sql_int ("SELECT count(*) FROM permissions"
44049                        " WHERE name = 'get_%ss'"
44050                        "   AND subject_type = 'role'"
44051                        "   AND subject"
44052                        "         = (SELECT id FROM roles WHERE uuid='%s')"
44053                        "   AND resource = %llu",
44054                        type,
44055                        *role,
44056                        permission_resource))
44057             {
44058               g_debug ("Role %s (%s) already has read permission"
44059                        " for %s %s (%s).",
44060                        role_name,
44061                        *role,
44062                        type_cap,
44063                        permission_resource_name,
44064                        permission_resource_id);
44065             }
44066           else
44067             {
44068               gchar *permission_name;
44069 
44070               g_info ("Creating read permission for role %s (%s)"
44071                       " on %s %s (%s).",
44072                       role_name,
44073                       *role,
44074                       type_cap,
44075                       permission_resource_name,
44076                       permission_resource_id);
44077 
44078               added_permission = TRUE;
44079               if (permission_count)
44080                 *permission_count = *permission_count + 1;
44081 
44082               permission_name = g_strdup_printf ("get_%ss", type);
44083 
44084               current_credentials.uuid = user_uuid (owner);
44085               switch (create_permission_internal
44086                        (0,
44087                         permission_name,
44088                         "Automatically created by"
44089                         " --optimize",
44090                         type,
44091                         permission_resource_id,
44092                         "role",
44093                         *role,
44094                         NULL))
44095                 {
44096                   case 0:
44097                     // success
44098                     break;
44099                   case 2:
44100                     g_warning ("%s: failed to find role %s for permission",
44101                                __func__, *role);
44102                     break;
44103                   case 3:
44104                     g_warning ("%s: failed to find %s %s for permission",
44105                                __func__, type_cap, permission_resource_id);
44106                     break;
44107                   case 5:
44108                     g_warning ("%s: error in resource when creating permission"
44109                                " for %s %s",
44110                                __func__, type_cap, permission_resource_id);
44111                     break;
44112                   case 6:
44113                     g_warning ("%s: error in subject (Role %s)",
44114                                __func__, *role);
44115                     break;
44116                   case 7:
44117                     g_warning ("%s: error in name %s",
44118                                __func__, permission_name);
44119                     break;
44120                   case 8:
44121                     g_warning ("%s: permission on permission", __func__);
44122                     break;
44123                   case 9:
44124                     g_warning ("%s: permission %s does not accept resource",
44125                                __func__, permission_name);
44126                     break;
44127                   case 99:
44128                     g_warning ("%s: permission denied to create %s permission"
44129                                " for role %s on %s %s",
44130                                __func__, permission_name, *role, type_cap,
44131                                permission_resource_id);
44132                     break;
44133                   default:
44134                     g_warning ("%s: internal error creating %s permission"
44135                                " for role %s on %s %s",
44136                                __func__, permission_name, *role, type_cap,
44137                                permission_resource_id);
44138                     break;
44139                 }
44140 
44141               free (current_credentials.uuid);
44142               current_credentials.uuid = NULL;
44143             }
44144 
44145           free (role_name);
44146           role ++;
44147         }
44148       if (object_count && added_permission)
44149         *object_count = *object_count + 1;
44150     }
44151 
44152   cleanup_iterator (&resources);
44153   g_strfreev (roles);
44154 
44155   return;
44156 }
44157 
44158 
44159 /**
44160  * @brief Delete permissions to feed objects for roles that are not set
44161  *        in the 'Feed Import Roles' setting.
44162  *
44163  * @param[in]  type  The object type, e.g. report_format.
44164  * @param[in]  type_cap         Capitalized type, e.g. "Report Format"
44165  * @param[out] permission_count Number of permissions added.
44166  * @param[out] object_count     Number of data objects affected.
44167  */
44168 static void
clean_feed_role_permissions(const char * type,const char * type_cap,int * permission_count,int * object_count)44169 clean_feed_role_permissions (const char *type,
44170                              const char *type_cap,
44171                              int *permission_count,
44172                              int *object_count)
44173 {
44174   char *roles_str;
44175   gchar **roles, **role;
44176   GString *sql_roles;
44177   iterator_t resources;
44178 
44179   roles_str = NULL;
44180   setting_value (SETTING_UUID_FEED_IMPORT_ROLES, &roles_str);
44181 
44182   if (roles_str == NULL || strlen (roles_str) == 0)
44183     {
44184       g_message ("%s: No feed import roles defined", __func__);
44185       g_free (roles_str);
44186       return;
44187     }
44188 
44189   sql_roles = g_string_new ("(");
44190 
44191   if (roles_str)
44192     {
44193       roles = g_strsplit (roles_str, ",", 0);
44194       role = roles;
44195       while (*role)
44196         {
44197           gchar *quoted_role = sql_insert (*role);
44198           g_string_append (sql_roles, quoted_role);
44199 
44200           role ++;
44201           if (*role)
44202             g_string_append (sql_roles, ", ");
44203         }
44204 
44205     }
44206 
44207   g_string_append (sql_roles, ")");
44208   g_debug ("%s: Keeping permissions for roles %s\n", __func__, sql_roles->str);
44209 
44210   init_iterator (&resources,
44211                  "SELECT id, uuid, name FROM %ss"
44212                  " WHERE predefined = 1",
44213                  type);
44214 
44215   while (next (&resources))
44216     {
44217       gboolean removed_permission = FALSE;
44218       resource_t permission_resource = iterator_int64 (&resources, 0);
44219       const char *permission_resource_id = iterator_string (&resources, 1);
44220       const char *permission_resource_name = iterator_string (&resources, 2);
44221       iterator_t permissions;
44222 
44223       init_iterator (&permissions,
44224                      "DELETE FROM permissions"
44225                      " WHERE name = 'get_%ss'"
44226                      "   AND resource = %llu"
44227                      "   AND subject_type = 'role'"
44228                      "   AND subject NOT IN"
44229                      "     (SELECT id FROM roles WHERE uuid IN %s)"
44230                      " RETURNING"
44231                      "   (SELECT uuid FROM roles WHERE id = subject),"
44232                      "   (SELECT name FROM roles WHERE id = subject)",
44233                      type,
44234                      permission_resource,
44235                      sql_roles->str);
44236 
44237       while (next (&permissions))
44238         {
44239           const char *role_id = iterator_string (&permissions, 0);
44240           const char *role_name = iterator_string (&permissions, 1);
44241           g_info ("Removed permission on %s %s (%s) for role %s (%s)",
44242                   type_cap,
44243                   permission_resource_name,
44244                   permission_resource_id,
44245                   role_name,
44246                   role_id);
44247 
44248           if (permission_count)
44249             *permission_count = *permission_count + 1;
44250           removed_permission = TRUE;
44251         }
44252 
44253       if (object_count && removed_permission)
44254         *object_count = *object_count + 1;
44255     }
44256 
44257   cleanup_iterator (&resources);
44258   g_strfreev (roles);
44259 
44260   return;
44261 }
44262 
44263 
44264 /* Roles. */
44265 
44266 /**
44267  * @brief List roles.
44268  *
44269  * @param[in]  log_config  Log configuration.
44270  * @param[in]  database    Location of manage database.
44271  * @param[in]  verbose     Whether to print UUID.
44272  *
44273  * @return 0 success, -1 error.
44274  */
44275 int
manage_get_roles(GSList * log_config,const db_conn_info_t * database,int verbose)44276 manage_get_roles (GSList *log_config, const db_conn_info_t *database,
44277                   int verbose)
44278 {
44279   iterator_t roles;
44280   int ret;
44281 
44282   g_info ("   Getting roles.");
44283 
44284   ret = manage_option_setup (log_config, database);
44285   if (ret)
44286     return ret;
44287 
44288   init_iterator (&roles, "SELECT name, uuid FROM roles;");
44289   while (next (&roles))
44290     if (verbose)
44291       printf ("%s %s\n", iterator_string (&roles, 0), iterator_string (&roles, 1));
44292     else
44293       printf ("%s\n", iterator_string (&roles, 0));
44294 
44295   cleanup_iterator (&roles);
44296 
44297   manage_option_cleanup ();
44298 
44299   return 0;
44300 }
44301 
44302 /**
44303  * @brief Create a role from an existing role.
44304  *
44305  * @param[in]  name       Name of new role.  NULL to copy from existing.
44306  * @param[in]  comment    Comment on new role.  NULL to copy from existing.
44307  * @param[in]  role_id    UUID of existing role.
44308  * @param[out] new_role_return  New role.
44309  *
44310  * @return 0 success, 1 role exists already, 2 failed to find existing
44311  *         role, 99 permission denied, -1 error.
44312  */
44313 int
copy_role(const char * name,const char * comment,const char * role_id,role_t * new_role_return)44314 copy_role (const char *name, const char *comment, const char *role_id,
44315            role_t *new_role_return)
44316 {
44317   int ret;
44318   role_t new_role, old_role;
44319 
44320   sql_begin_immediate ();
44321 
44322   if (acl_user_may ("create_role") == 0)
44323     return 99;
44324 
44325   if (acl_role_can_super_everyone (role_id))
44326     return 99;
44327 
44328   ret = copy_resource_lock ("role", name, comment, role_id, NULL, 1, &new_role,
44329                             &old_role);
44330   if (ret)
44331     {
44332       sql_rollback ();
44333       return ret;
44334     }
44335 
44336   sql ("INSERT INTO permissions"
44337        " (uuid, owner, name, comment, resource_type, resource_uuid, resource,"
44338        "  resource_location, subject_type, subject, subject_location,"
44339        "  creation_time, modification_time)"
44340        " SELECT make_uuid (),"
44341        "        (SELECT id FROM users WHERE users.uuid = '%s'),"
44342        "        name, comment, resource_type,"
44343        "        resource_uuid, resource, resource_location, subject_type, %llu,"
44344        "        subject_location, m_now (), m_now ()"
44345        " FROM permissions"
44346        " WHERE subject_type = 'role'"
44347        " AND subject = %llu"
44348        " AND subject_location = " G_STRINGIFY (LOCATION_TABLE)
44349        " AND (resource = 0 OR owner IS NULL);",
44350        current_credentials.uuid,
44351        new_role,
44352        old_role);
44353 
44354   sql_commit ();
44355   if (new_role_return)
44356     *new_role_return = new_role;
44357   return 0;
44358 }
44359 
44360 /**
44361  * @brief Create a role.
44362  *
44363  * @param[in]   role_name        Role name.
44364  * @param[in]   comment          Comment on role.
44365  * @param[in]   users            Users role applies to.
44366  * @param[in]   role             Role return.
44367  *
44368  * @return 0 success, 1 role exists already, 2 failed to find user, 4 user
44369  *         name validation failed, 99 permission denied, -1 error.
44370  */
44371 int
create_role(const char * role_name,const char * comment,const char * users,role_t * role)44372 create_role (const char *role_name, const char *comment, const char *users,
44373              role_t* role)
44374 {
44375   int ret;
44376   gchar *quoted_role_name, *quoted_comment;
44377 
44378   assert (current_credentials.uuid);
44379   assert (role_name);
44380   assert (role);
44381 
44382   sql_begin_immediate ();
44383 
44384   if (acl_user_may ("create_role") == 0)
44385     {
44386       sql_rollback ();
44387       return 99;
44388     }
44389 
44390   if (resource_with_name_exists (role_name, "role", 0))
44391     {
44392       sql_rollback ();
44393       return 1;
44394     }
44395 
44396   quoted_role_name = sql_quote (role_name);
44397   quoted_comment = comment ? sql_quote (comment) : g_strdup ("");
44398   sql ("INSERT INTO roles"
44399        " (uuid, name, owner, comment, creation_time, modification_time)"
44400        " VALUES"
44401        " (make_uuid (), '%s',"
44402        "  (SELECT id FROM users WHERE users.uuid = '%s'),"
44403        "  '%s', m_now (), m_now ());",
44404        quoted_role_name,
44405        current_credentials.uuid,
44406        quoted_comment);
44407   g_free (quoted_comment);
44408   g_free (quoted_role_name);
44409 
44410   *role = sql_last_insert_id ();
44411   ret = add_users ("role", *role, users);
44412 
44413   if (ret)
44414     sql_rollback ();
44415   else
44416     sql_commit ();
44417 
44418   return ret;
44419 }
44420 
44421 /**
44422  * @brief Return whether a role is predefined.
44423  *
44424  * @param[in]  role  Role.
44425  *
44426  * @return 1 if predefined, else 0.
44427  */
44428 static int
role_is_predefined(role_t role)44429 role_is_predefined (role_t role)
44430 {
44431   return sql_int ("SELECT COUNT (*) FROM roles"
44432                   " WHERE id = %llu"
44433                   " AND (uuid = '" ROLE_UUID_ADMIN "'"
44434                   "      OR uuid = '" ROLE_UUID_GUEST "'"
44435                   "      OR uuid = '" ROLE_UUID_MONITOR "'"
44436                   "      OR uuid = '" ROLE_UUID_INFO "'"
44437                   "      OR uuid = '" ROLE_UUID_USER "'"
44438                   "      OR uuid = '" ROLE_UUID_SUPER_ADMIN "'"
44439                   "      OR uuid = '" ROLE_UUID_OBSERVER "');",
44440                   role)
44441          != 0;
44442 }
44443 
44444 /**
44445  * @brief Return whether a role is predefined.
44446  *
44447  * @param[in]  uuid  UUID of role.
44448  *
44449  * @return 1 if predefined, else 0.
44450  */
44451 static int
role_is_predefined_id(const char * uuid)44452 role_is_predefined_id (const char *uuid)
44453 {
44454   return uuid && ((strcmp (uuid, ROLE_UUID_ADMIN) == 0)
44455                   || (strcmp (uuid, ROLE_UUID_GUEST) == 0)
44456                   || (strcmp (uuid, ROLE_UUID_MONITOR) == 0)
44457                   || (strcmp (uuid, ROLE_UUID_INFO) == 0)
44458                   || (strcmp (uuid, ROLE_UUID_USER) == 0)
44459                   || (strcmp (uuid, ROLE_UUID_SUPER_ADMIN) == 0)
44460                   || (strcmp (uuid, ROLE_UUID_OBSERVER) == 0));
44461 }
44462 
44463 /**
44464  * @brief Delete a role.
44465  *
44466  * @param[in]  role_id   UUID of role.
44467  * @param[in]  ultimate  Whether to remove entirely, or to trashcan.
44468  *
44469  * @return 0 success, 1 fail because a task refers to the role, 2 failed
44470  *         to find role, 3 predefined role, -1 error.
44471  */
44472 int
delete_role(const char * role_id,int ultimate)44473 delete_role (const char *role_id, int ultimate)
44474 {
44475   role_t role = 0;
44476   GArray *affected_users;
44477   iterator_t users_iter;
44478 
44479   sql_begin_immediate ();
44480 
44481   if (acl_user_may ("delete_role") == 0)
44482     {
44483       sql_rollback ();
44484       return 99;
44485     }
44486 
44487   if (find_role_with_permission (role_id, &role, "delete_role"))
44488     {
44489       sql_rollback ();
44490       return -1;
44491     }
44492 
44493   if (role == 0)
44494     {
44495       if (find_trash ("role", role_id, &role))
44496         {
44497           sql_rollback ();
44498           return -1;
44499         }
44500       if (role == 0)
44501         {
44502           sql_rollback ();
44503           return 2;
44504         }
44505       if (ultimate == 0)
44506         {
44507           /* It's already in the trashcan. */
44508           sql_commit ();
44509           return 0;
44510         }
44511 
44512       if (trash_role_in_use (role))
44513         {
44514           sql_rollback ();
44515           return 1;
44516         }
44517 
44518       sql ("DELETE FROM permissions"
44519            " WHERE resource_type = 'role'"
44520            " AND resource = %llu"
44521            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
44522            role);
44523       sql ("DELETE FROM permissions_trash"
44524            " WHERE resource_type = 'role'"
44525            " AND resource = %llu"
44526            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
44527            role);
44528       sql ("DELETE FROM permissions"
44529            " WHERE subject_type = 'role'"
44530            " AND subject = %llu"
44531            " AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
44532            role);
44533       sql ("DELETE FROM permissions_trash"
44534            " WHERE subject_type = 'role'"
44535            " AND subject = %llu"
44536            " AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
44537            role);
44538 
44539       tags_remove_resource ("role", role, LOCATION_TRASH);
44540 
44541       sql ("DELETE FROM role_users_trash WHERE role = %llu;", role);
44542       sql ("DELETE FROM roles_trash WHERE id = %llu;", role);
44543       sql_commit ();
44544       return 0;
44545     }
44546 
44547   if (role_is_predefined (role))
44548     {
44549       sql_rollback ();
44550       return 3;
44551     }
44552 
44553   if (role_in_use (role))
44554     {
44555       sql_rollback ();
44556       return 1;
44557     }
44558 
44559   if (ultimate == 0)
44560     {
44561       role_t trash_role;
44562 
44563       sql ("INSERT INTO roles_trash"
44564            " (uuid, owner, name, comment, creation_time, modification_time)"
44565            " SELECT uuid, owner, name, comment, creation_time,"
44566            "        modification_time"
44567            " FROM roles WHERE id = %llu;",
44568            role);
44569 
44570       trash_role = sql_last_insert_id ();
44571 
44572       sql ("INSERT INTO role_users_trash"
44573            " (\"role\", \"user\")"
44574            " SELECT %llu, \"user\""
44575            " FROM role_users WHERE \"role\" = %llu;",
44576            trash_role,
44577            role);
44578 
44579       permissions_set_locations ("role", role, trash_role, LOCATION_TRASH);
44580       tags_set_locations ("role", role, trash_role, LOCATION_TRASH);
44581       permissions_set_subjects ("role", role, trash_role, LOCATION_TRASH);
44582     }
44583   else
44584     {
44585       sql ("DELETE FROM permissions"
44586            " WHERE resource_type = 'role'"
44587            " AND resource = %llu"
44588            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
44589            role);
44590       sql ("DELETE FROM permissions_trash"
44591            " WHERE resource_type = 'role'"
44592            " AND resource = %llu"
44593            " AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
44594            role);
44595       sql ("DELETE FROM permissions"
44596            " WHERE subject_type = 'role'"
44597            " AND subject = %llu"
44598            " AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
44599            role);
44600       sql ("DELETE FROM permissions_trash"
44601            " WHERE subject_type = 'role'"
44602            " AND subject = %llu"
44603            " AND subject_location = " G_STRINGIFY (LOCATION_TABLE) ";",
44604            role);
44605       tags_remove_resource ("role", role, LOCATION_TABLE);
44606     }
44607 
44608   affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
44609   init_iterator (&users_iter,
44610                   "SELECT \"user\" FROM role_users"
44611                   " WHERE \"role\" = %llu",
44612                   role);
44613   while (next (&users_iter))
44614     {
44615       user_t user = iterator_int64 (&users_iter, 0);
44616       g_array_append_val (affected_users, user);
44617     }
44618   cleanup_iterator (&users_iter);
44619 
44620   sql ("DELETE FROM role_users WHERE \"role\" = %llu;", role);
44621   sql ("DELETE FROM roles WHERE id = %llu;", role);
44622 
44623   cache_all_permissions_for_users (affected_users);
44624   g_array_free (affected_users, TRUE);
44625 
44626   sql_commit ();
44627   return 0;
44628 }
44629 
44630 /**
44631  * @brief Find a role for a specific permission, given a UUID.
44632  *
44633  * @param[in]   uuid        UUID of role.
44634  * @param[out]  role        Role return, 0 if successfully failed to find role.
44635  * @param[in]   permission  Permission.
44636  *
44637  * @return FALSE on success (including if failed to find role), TRUE on error.
44638  */
44639 static gboolean
find_role_with_permission(const char * uuid,role_t * role,const char * permission)44640 find_role_with_permission (const char* uuid, role_t* role,
44641                            const char *permission)
44642 {
44643   return find_resource_with_permission ("role", uuid, role, permission, 0);
44644 }
44645 
44646 /**
44647  * @brief Find a role given a name.
44648  *
44649  * @param[in]   name  A role name.
44650  * @param[out]  role  Role return, 0 if successfully failed to find role.
44651  *
44652  * @return FALSE on success (including if failed to find role), TRUE on error.
44653  */
44654 static gboolean
find_role_by_name(const char * name,role_t * role)44655 find_role_by_name (const char* name, role_t *role)
44656 {
44657   return find_resource_by_name ("role", name, role);
44658 }
44659 
44660 /**
44661  * @brief Gets UUID of role.
44662  *
44663  * @param[in]  role  Role.
44664  *
44665  * @return Users.
44666  */
44667 gchar *
role_uuid(role_t role)44668 role_uuid (role_t role)
44669 {
44670   return sql_string ("SELECT uuid FROM roles WHERE id = %llu;",
44671                      role);
44672 }
44673 
44674 /**
44675  * @brief Gets users of role as a string.
44676  *
44677  * @param[in]  role  Role.
44678  *
44679  * @return Users.
44680  */
44681 gchar *
role_users(role_t role)44682 role_users (role_t role)
44683 {
44684   return sql_string ("SELECT group_concat (name, ', ')"
44685                      " FROM (SELECT users.name FROM users, role_users"
44686                      "       WHERE role_users.role = %llu"
44687                      "       AND role_users.user = users.id"
44688                      "       GROUP BY users.name)"
44689                      "      AS sub;",
44690                      role);
44691 }
44692 
44693 /**
44694  * @brief Check whether a role is writable.
44695  *
44696  * @param[in]  role  Role.
44697  *
44698  * @return 1 yes, 0 no.
44699  */
44700 int
role_writable(role_t role)44701 role_writable (role_t role)
44702 {
44703   if (role_is_predefined (role))
44704     return 0;
44705   return 1;
44706 }
44707 
44708 /**
44709  * @brief Check whether a trashcan role is writable.
44710  *
44711  * @param[in]  role  Role.
44712  *
44713  * @return 1 yes, 0 no.
44714  */
44715 int
trash_role_writable(role_t role)44716 trash_role_writable (role_t role)
44717 {
44718   return 1;
44719 }
44720 
44721 /**
44722  * @brief Check whether a role is in use.
44723  *
44724  * @param[in]  role  Role.
44725  *
44726  * @return 1 yes, 0 no.
44727  */
44728 int
role_in_use(role_t role)44729 role_in_use (role_t role)
44730 {
44731   return 0;
44732 }
44733 
44734 /**
44735  * @brief Check whether a trashcan role is in use.
44736  *
44737  * @param[in]  role  Role.
44738  *
44739  * @return 1 yes, 0 no.
44740  */
44741 int
trash_role_in_use(role_t role)44742 trash_role_in_use (role_t role)
44743 {
44744   return 0;
44745 }
44746 
44747 /**
44748  * @brief Modify a role.
44749  *
44750  * @param[in]   role_id  UUID of role.
44751  * @param[in]   name     Name of role.
44752  * @param[in]   comment  Comment on role.
44753  * @param[in]   users    Role users.
44754  *
44755  * @return 0 success, 1 failed to find role, 2 failed to find user, 3 role_id
44756  *         required, 4 user name validation failed, 5 role with new name
44757  *         exists, 99 permission denied, -1 internal error.
44758  */
44759 int
modify_role(const char * role_id,const char * name,const char * comment,const char * users)44760 modify_role (const char *role_id, const char *name, const char *comment,
44761              const char *users)
44762 {
44763   int ret;
44764   gchar *quoted_name, *quoted_comment;
44765   role_t role;
44766   GArray *affected_users;
44767   iterator_t users_iter;
44768 
44769   assert (current_credentials.uuid);
44770 
44771   if (role_id == NULL)
44772     return 3;
44773 
44774   sql_begin_immediate ();
44775 
44776   if (acl_user_may ("modify_role") == 0)
44777     {
44778       sql_rollback ();
44779       return 99;
44780     }
44781 
44782   role = 0;
44783 
44784   if (find_role_with_permission (role_id, &role, "modify_role"))
44785     {
44786       sql_rollback ();
44787       return -1;
44788     }
44789 
44790   if (role == 0)
44791     {
44792       sql_rollback ();
44793       return 1;
44794     }
44795 
44796   /* Check whether a role with the same name exists already. */
44797   if (name)
44798     {
44799       if (resource_with_name_exists (name, "role", role))
44800         {
44801           sql_rollback ();
44802           return 5;
44803         }
44804     }
44805 
44806   quoted_name = sql_quote (name ?: "");
44807   quoted_comment = sql_quote (comment ?: "");
44808 
44809   sql ("UPDATE roles SET"
44810        " name = '%s',"
44811        " comment = '%s',"
44812        " modification_time = m_now ()"
44813        " WHERE id = %llu;",
44814        quoted_name,
44815        quoted_comment,
44816        role);
44817 
44818   g_free (quoted_comment);
44819   g_free (quoted_name);
44820 
44821   affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
44822   init_iterator (&users_iter,
44823                  "SELECT \"user\" FROM role_users"
44824                  " WHERE \"role\" = %llu",
44825                  role);
44826   while (next (&users_iter))
44827     {
44828       user_t user = iterator_int64 (&users_iter, 0);
44829       g_array_append_val (affected_users, user);
44830     }
44831   cleanup_iterator (&users_iter);
44832 
44833   sql ("DELETE FROM role_users WHERE \"role\" = %llu;", role);
44834 
44835   ret = add_users ("role", role, users);
44836 
44837   init_iterator (&users_iter,
44838                  "SELECT \"user\" FROM role_users"
44839                  " WHERE \"role\" = %llu",
44840                  role);
44841 
44842   // users not looked for in this loop were removed
44843   //  -> possible permissions change
44844   while (next (&users_iter))
44845     {
44846       int index, found_user;
44847       user_t user = iterator_int64 (&users_iter, 0);
44848 
44849       found_user = 0;
44850       for (index = 0; index < affected_users->len && found_user == 0; index++)
44851         {
44852           if (g_array_index (affected_users, user_t, index) == user)
44853             {
44854               found_user = 1;
44855               break;
44856             }
44857         }
44858 
44859       if (found_user)
44860         {
44861           // users found here stay in the role -> no change in permissions
44862           g_array_remove_index_fast (affected_users, index);
44863         }
44864       else
44865         {
44866           // user added to role -> possible permissions change
44867           g_array_append_val (affected_users, user);
44868         }
44869     }
44870 
44871   cleanup_iterator (&users_iter);
44872 
44873   cache_all_permissions_for_users (affected_users);
44874 
44875   g_array_free (affected_users, TRUE);
44876 
44877   if (ret)
44878     sql_rollback ();
44879   else
44880     sql_commit ();
44881 
44882   return ret;
44883 }
44884 
44885 /**
44886  * @brief Filter columns for role iterator.
44887  */
44888 #define ROLE_ITERATOR_FILTER_COLUMNS                                         \
44889  { GET_ITERATOR_FILTER_COLUMNS, NULL }
44890 
44891 /**
44892  * @brief Role iterator columns.
44893  */
44894 #define ROLE_ITERATOR_COLUMNS                                                \
44895  {                                                                           \
44896    GET_ITERATOR_COLUMNS (roles),                                             \
44897    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
44898  }
44899 
44900 /**
44901  * @brief Role iterator columns for trash case.
44902  */
44903 #define ROLE_ITERATOR_TRASH_COLUMNS                                          \
44904  {                                                                           \
44905    GET_ITERATOR_COLUMNS (roles_trash),                                       \
44906    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
44907  }
44908 
44909 /**
44910  * @brief Count number of roles.
44911  *
44912  * @param[in]  get  GET params.
44913  *
44914  * @return Total number of roles in roleed set.
44915  */
44916 int
role_count(const get_data_t * get)44917 role_count (const get_data_t *get)
44918 {
44919   static const char *extra_columns[] = ROLE_ITERATOR_FILTER_COLUMNS;
44920   static column_t columns[] = ROLE_ITERATOR_COLUMNS;
44921   static column_t trash_columns[] = ROLE_ITERATOR_TRASH_COLUMNS;
44922   return count ("role", get, columns, trash_columns, extra_columns,
44923                 0, 0, 0, TRUE);
44924 }
44925 
44926 /**
44927  * @brief Initialise a role iterator, including observed roles.
44928  *
44929  * @param[in]  iterator    Iterator.
44930  * @param[in]  get         GET data.
44931  *
44932  * @return 0 success, 1 failed to find role, 2 failed to find role (filt_id),
44933  *         -1 error.
44934  */
44935 int
init_role_iterator(iterator_t * iterator,const get_data_t * get)44936 init_role_iterator (iterator_t* iterator, const get_data_t *get)
44937 {
44938   static const char *filter_columns[] = ROLE_ITERATOR_FILTER_COLUMNS;
44939   static column_t columns[] = ROLE_ITERATOR_COLUMNS;
44940   static column_t trash_columns[] = ROLE_ITERATOR_TRASH_COLUMNS;
44941 
44942   return init_get_iterator (iterator,
44943                             "role",
44944                             get,
44945                             columns,
44946                             trash_columns,
44947                             filter_columns,
44948                             0,
44949                             NULL,
44950                             NULL,
44951                             TRUE);
44952 }
44953 
44954 
44955 /* Filters. */
44956 
44957 /**
44958  * @brief Find a filter for a specific permission, given a UUID.
44959  *
44960  * @param[in]   uuid        UUID of filter.
44961  * @param[out]  filter      Filter return, 0 if successfully failed to find
44962  *                          filter.
44963  * @param[in]   permission  Permission.
44964  *
44965  * @return FALSE on success (including if failed to find filter), TRUE on error.
44966  */
44967 gboolean
find_filter_with_permission(const char * uuid,filter_t * filter,const char * permission)44968 find_filter_with_permission (const char* uuid, filter_t* filter,
44969                              const char *permission)
44970 {
44971   return find_resource_with_permission ("filter", uuid, filter, permission, 0);
44972 }
44973 
44974 /**
44975  * @brief Return the UUID of a filter.
44976  *
44977  * @param[in]  filter  Filter.
44978  *
44979  * @return Newly allocated UUID if available, else NULL.
44980  */
44981 char*
filter_uuid(filter_t filter)44982 filter_uuid (filter_t filter)
44983 {
44984   return sql_string ("SELECT uuid FROM filters WHERE id = %llu;",
44985                      filter);
44986 }
44987 
44988 /**
44989  * @brief Return the UUID of a trashcan filter.
44990  *
44991  * @param[in]  filter  Filter.
44992  *
44993  * @return Newly allocated UUID if available, else NULL.
44994  */
44995 static char*
trash_filter_uuid(filter_t filter)44996 trash_filter_uuid (filter_t filter)
44997 {
44998   return sql_string ("SELECT uuid FROM filters_trash WHERE id = %llu;",
44999                      filter);
45000 }
45001 
45002 /**
45003  * @brief Return the name of a filter.
45004  *
45005  * @param[in]  filter  Filter.
45006  *
45007  * @return name of filter.
45008  */
45009 char*
filter_name(filter_t filter)45010 filter_name (filter_t filter)
45011 {
45012   return sql_string ("SELECT name FROM filters WHERE id = %llu;",
45013                      filter);
45014 }
45015 
45016 /**
45017  * @brief Return the name of a trashcan filter.
45018  *
45019  * @param[in]  filter  Filter.
45020  *
45021  * @return name of filter.
45022  */
45023 static char*
trash_filter_name(filter_t filter)45024 trash_filter_name (filter_t filter)
45025 {
45026   return sql_string ("SELECT name FROM filters_trash WHERE id = %llu;",
45027                      filter);
45028 }
45029 
45030 /**
45031  * @brief Return the term of a filter.
45032  *
45033  * @param[in]  uuid  Filter UUID.
45034  *
45035  * @return Newly allocated term if available, else NULL.
45036  */
45037 gchar*
filter_term(const char * uuid)45038 filter_term (const char *uuid)
45039 {
45040   gchar *quoted_uuid, *ret;
45041   quoted_uuid = sql_quote (uuid);
45042   ret = sql_string ("SELECT term FROM filters WHERE uuid = '%s';",
45043                     quoted_uuid);
45044   g_free (quoted_uuid);
45045   return ret;
45046 }
45047 
45048 /**
45049  * @brief Return the value of a column keyword of a filter term.
45050  *
45051  * @param[in]  term    Filter term.
45052  * @param[in]  column  Column name.
45053  *
45054  * @return Value of column keyword if one exists, else NULL.
45055  */
45056 gchar*
filter_term_value(const char * term,const char * column)45057 filter_term_value (const char *term, const char *column)
45058 {
45059   keyword_t **point;
45060   array_t *split;
45061 
45062   if (term == NULL)
45063     return NULL;
45064 
45065   split = split_filter (term);
45066   point = (keyword_t**) split->pdata;
45067   while (*point)
45068     {
45069       keyword_t *keyword;
45070 
45071       keyword = *point;
45072       if (keyword->column
45073           && ((strcasecmp (keyword->column, column) == 0)
45074               || (keyword->column[0] == '_'
45075                   && (strcasecmp (keyword->column + 1, column) == 0))))
45076         {
45077           gchar *ret = g_strdup (keyword->string);
45078           filter_free (split);
45079           return ret;
45080         }
45081       point++;
45082     }
45083   filter_free (split);
45084   return NULL;
45085 }
45086 
45087 /**
45088  * @brief Return the value of the apply_overrides keyword of a filter term.
45089  *
45090  * @param[in]  term    Filter term.
45091  *
45092  * @return Value of apply_overrides if it exists, else APPLY_OVERRIDES_DEFAULT.
45093  */
45094 int
filter_term_apply_overrides(const char * term)45095 filter_term_apply_overrides (const char *term)
45096 {
45097   if (term)
45098     {
45099       int ret;
45100       gchar *apply_overrides_str;
45101 
45102       apply_overrides_str = filter_term_value (term, "apply_overrides");
45103       ret = apply_overrides_str
45104               ? (strcmp (apply_overrides_str, "0") ? 1 : 0)
45105               : APPLY_OVERRIDES_DEFAULT;
45106 
45107       g_free (apply_overrides_str);
45108       return ret;
45109     }
45110   else
45111     return APPLY_OVERRIDES_DEFAULT;
45112 }
45113 
45114 /**
45115  * @brief Return the value of the min_qod keyword of a filter term.
45116  *
45117  * @param[in]  term    Filter term.
45118  *
45119  * @return Value of min_qod if it exists, else MIN_QOD_DEFAULT.
45120  */
45121 int
filter_term_min_qod(const char * term)45122 filter_term_min_qod (const char *term)
45123 {
45124   if (term)
45125     {
45126       int ret;
45127       gchar *min_qod_str;
45128 
45129       min_qod_str = filter_term_value (term, "min_qod");
45130       ret = (min_qod_str && strcmp (min_qod_str, ""))
45131               ? atoi (min_qod_str) : MIN_QOD_DEFAULT;
45132 
45133       g_free (min_qod_str);
45134       return ret;
45135     }
45136   else
45137     return MIN_QOD_DEFAULT;
45138 }
45139 
45140 
45141 /**
45142  * @brief Create a filter.
45143  *
45144  * @param[in]   name            Name of filter.
45145  * @param[in]   comment         Comment on filter.
45146  * @param[in]   type            Type of resource.
45147  * @param[in]   term            Filter term.
45148  * @param[out]  filter          Created filter.
45149  *
45150  * @return 0 success, 1 filter exists already, 2 error in type, 99 permission
45151  *         denied.
45152  */
45153 int
create_filter(const char * name,const char * comment,const char * type,const char * term,filter_t * filter)45154 create_filter (const char *name, const char *comment, const char *type,
45155                const char *term, filter_t* filter)
45156 {
45157   gchar *quoted_name, *quoted_comment, *quoted_term, *clean_term;
45158 
45159   assert (current_credentials.uuid);
45160 
45161   if (type && strlen (type))
45162     {
45163       type = type_db_name (type);
45164       if (type == NULL || !valid_type (type))
45165         return 2;
45166     }
45167 
45168   sql_begin_immediate ();
45169 
45170   if (acl_user_may ("create_filter") == 0)
45171     {
45172       sql_rollback ();
45173       return 99;
45174     }
45175 
45176   if (resource_with_name_exists (name, "filter", 0))
45177     {
45178       sql_rollback ();
45179       return 1;
45180     }
45181   quoted_name = sql_quote (name ?: "");
45182 
45183   clean_term = manage_clean_filter (term ? term : "");
45184   quoted_term = sql_quote (clean_term);
45185   g_free (clean_term);
45186 
45187   if (comment)
45188     {
45189       quoted_comment = sql_quote (comment);
45190       sql ("INSERT INTO filters"
45191            " (uuid, name, owner, comment, type, term, creation_time,"
45192            "  modification_time)"
45193            " VALUES (make_uuid (), '%s',"
45194            " (SELECT id FROM users WHERE users.uuid = '%s'),"
45195            " '%s', %s%s%s, '%s', m_now (), m_now ());",
45196            quoted_name,
45197            current_credentials.uuid,
45198            quoted_comment,
45199            type ? "lower ('" : "",
45200            type ? type : "''",
45201            type ? "')" : "",
45202            quoted_term);
45203       g_free (quoted_comment);
45204     }
45205   else
45206     sql ("INSERT INTO filters"
45207          " (uuid, name, owner, comment, type, term, creation_time,"
45208          "  modification_time)"
45209          " VALUES (make_uuid (), '%s',"
45210          " (SELECT id FROM users WHERE users.uuid = '%s'),"
45211          " '', %s%s%s, '%s', m_now (), m_now ());",
45212          quoted_name,
45213          current_credentials.uuid,
45214          type ? "lower ('" : "",
45215          type ? type : "''",
45216          type ? "')" : "",
45217          quoted_term);
45218 
45219   if (filter)
45220     *filter = sql_last_insert_id ();
45221 
45222   g_free (quoted_name);
45223   g_free (quoted_term);
45224 
45225   sql_commit ();
45226 
45227   return 0;
45228 }
45229 
45230 /**
45231  * @brief Create a filter from an existing filter.
45232  *
45233  * @param[in]  name        Name of new filter.  NULL to copy from existing.
45234  * @param[in]  comment     Comment on new filter.  NULL to copy from existing.
45235  * @param[in]  filter_id   UUID of existing filter.
45236  * @param[out] new_filter  New filter.
45237  *
45238  * @return 0 success, 1 filter exists already, 2 failed to find existing
45239  *         filter, -1 error.
45240  */
45241 int
copy_filter(const char * name,const char * comment,const char * filter_id,filter_t * new_filter)45242 copy_filter (const char* name, const char* comment, const char *filter_id,
45243              filter_t* new_filter)
45244 {
45245   return copy_resource ("filter", name, comment, filter_id, "term, type",
45246                         1, new_filter, NULL);
45247 }
45248 
45249 /**
45250  * @brief Delete a filter.
45251  *
45252  * @param[in]  filter_id  UUID of filter.
45253  * @param[in]  ultimate   Whether to remove entirely, or to trashcan.
45254  *
45255  * @return 0 success, 1 fail because a task refers to the filter, 2 failed
45256  *         to find filter, 99 permission denied, -1 error.
45257  */
45258 int
delete_filter(const char * filter_id,int ultimate)45259 delete_filter (const char *filter_id, int ultimate)
45260 {
45261   gchar *quoted_filter_id;
45262   filter_t filter = 0;
45263 
45264   sql_begin_immediate ();
45265 
45266   if (acl_user_may ("delete_filter") == 0)
45267     {
45268       sql_rollback ();
45269       return 99;
45270     }
45271 
45272   if (find_filter_with_permission (filter_id, &filter, "delete_filter"))
45273     {
45274       sql_rollback ();
45275       return -1;
45276     }
45277 
45278   if (filter == 0)
45279     {
45280       if (find_trash ("filter", filter_id, &filter))
45281         {
45282           sql_rollback ();
45283           return -1;
45284         }
45285       if (filter == 0)
45286         {
45287           sql_rollback ();
45288           return 2;
45289         }
45290       if (ultimate == 0)
45291         {
45292           /* It's already in the trashcan. */
45293           sql_commit ();
45294           return 0;
45295         }
45296 
45297       /* Check if it's in use by an alert in the trashcan. */
45298       if (sql_int ("SELECT count(*) FROM alerts_trash"
45299                    " WHERE filter = %llu"
45300                    " AND filter_location = " G_STRINGIFY (LOCATION_TRASH) ";",
45301                    filter))
45302         {
45303           sql_rollback ();
45304           return 1;
45305         }
45306 
45307       /* Check if it's in use by the condition of an alert in the trashcan. */
45308       if (sql_int ("SELECT count(*) FROM alert_condition_data_trash"
45309                    " WHERE name = 'filter_id'"
45310                    " AND data = (SELECT uuid FROM filters_trash"
45311                    "             WHERE id = %llu)"
45312                    " AND (SELECT condition = %i OR condition = %i"
45313                    "      FROM alerts_trash WHERE id = alert);",
45314                    filter,
45315                    ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45316                    ALERT_CONDITION_FILTER_COUNT_CHANGED))
45317         {
45318           sql_rollback ();
45319           return 1;
45320         }
45321 
45322       permissions_set_orphans ("filter", filter, LOCATION_TRASH);
45323       tags_remove_resource ("filter", filter, LOCATION_TRASH);
45324 
45325       sql ("DELETE FROM filters_trash WHERE id = %llu;", filter);
45326       sql_commit ();
45327       return 0;
45328     }
45329 
45330   /* Check if it's in use by an alert. */
45331   if (sql_int ("SELECT count(*) FROM alerts"
45332                " WHERE filter = %llu;",
45333                filter))
45334     {
45335       sql_rollback ();
45336       return 1;
45337     }
45338 
45339   /* Check if it's in use by the condition of an alert. */
45340   if (sql_int ("SELECT count(*) FROM alert_condition_data"
45341                " WHERE name = 'filter_id'"
45342                " AND data = (SELECT uuid FROM filters"
45343                "             WHERE id = %llu)"
45344                " AND (SELECT condition = %i OR condition = %i"
45345                "      FROM alerts WHERE id = alert);",
45346                filter,
45347                ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45348                ALERT_CONDITION_FILTER_COUNT_CHANGED))
45349     {
45350       sql_rollback ();
45351       return 1;
45352     }
45353 
45354   if (ultimate)
45355     {
45356       /* Check if it's in use by the condition of an alert in the trashcan. */
45357       if (sql_int ("SELECT count(*) FROM alert_condition_data_trash"
45358                    " WHERE name = 'filter_id'"
45359                    " AND data = (SELECT uuid FROM filters"
45360                    "             WHERE id = %llu)"
45361                    " AND (SELECT condition = %i OR condition = %i"
45362                    "      FROM alerts_trash WHERE id = alert);",
45363                    filter,
45364                    ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45365                    ALERT_CONDITION_FILTER_COUNT_CHANGED))
45366         {
45367           sql_rollback ();
45368           return 1;
45369         }
45370     }
45371 
45372   quoted_filter_id = sql_quote (filter_id);
45373   sql ("DELETE FROM settings WHERE name %s '%% Filter' AND value = '%s';",
45374        sql_ilike_op (),
45375        quoted_filter_id);
45376   g_free (quoted_filter_id);
45377 
45378   if (ultimate == 0)
45379     {
45380       sql ("INSERT INTO filters_trash"
45381            " (uuid, owner, name, comment, type, term, creation_time,"
45382            "  modification_time)"
45383            " SELECT uuid, owner, name, comment, type, term, creation_time,"
45384            "  modification_time"
45385            " FROM filters WHERE id = %llu;",
45386            filter);
45387 
45388       /* Update the location of the filter in any trashcan alerts. */
45389       sql ("UPDATE alerts_trash"
45390            " SET filter = %llu,"
45391            "     filter_location = " G_STRINGIFY (LOCATION_TRASH)
45392            " WHERE filter = %llu"
45393            " AND filter_location = " G_STRINGIFY (LOCATION_TABLE) ";",
45394            sql_last_insert_id (),
45395            filter);
45396 
45397       permissions_set_locations ("filter", filter,
45398                                  sql_last_insert_id (),
45399                                  LOCATION_TRASH);
45400       tags_set_locations ("filter", filter,
45401                           sql_last_insert_id (),
45402                           LOCATION_TRASH);
45403     }
45404   else
45405     {
45406       permissions_set_orphans ("filter", filter, LOCATION_TABLE);
45407       tags_remove_resource ("filter", filter, LOCATION_TABLE);
45408     }
45409 
45410   sql ("DELETE FROM filters WHERE id = %llu;", filter);
45411 
45412   sql_commit ();
45413   return 0;
45414 }
45415 
45416 /**
45417  * @brief Check whether a filter is in use.
45418  *
45419  * @param[in]  filter  Filter.
45420  *
45421  * @return 1 yes, 0 no.
45422  */
45423 int
filter_in_use(filter_t filter)45424 filter_in_use (filter_t filter)
45425 {
45426   return !!sql_int ("SELECT count (*) FROM alerts"
45427                     /* Filter applied to results passed to alert's "generate". */
45428                     " WHERE filter = %llu"
45429                     /* Filter applied to check alert condition. */
45430                     "   OR (EXISTS (SELECT * FROM alert_condition_data"
45431                     "             WHERE name = 'filter_id'"
45432                     "             AND data = (SELECT uuid FROM filters"
45433                     "                          WHERE id = %llu)"
45434                     "             AND alert = alerts.id)"
45435                     "       AND (condition = %i OR condition = %i))",
45436                     filter,
45437                     filter,
45438                     ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45439                     ALERT_CONDITION_FILTER_COUNT_CHANGED);
45440 }
45441 
45442 /**
45443  * @brief Check whether a filter is in use for the output of any alert.
45444  *
45445  * @param[in]  filter  Filter.
45446  *
45447  * @return 1 yes, 0 no.
45448  */
45449 static int
filter_in_use_for_output(filter_t filter)45450 filter_in_use_for_output (filter_t filter)
45451 {
45452   return !!sql_int ("SELECT count (*) FROM alerts"
45453                     " WHERE filter = %llu;",
45454                     filter);
45455 }
45456 
45457 /**
45458  * @brief Check whether a filter is in use by any result alert conditions.
45459  *
45460  * @param[in]  filter  Filter.
45461  *
45462  * @return 1 yes, 0 no.
45463  */
45464 static int
filter_in_use_for_result_event(filter_t filter)45465 filter_in_use_for_result_event (filter_t filter)
45466 {
45467   return !!sql_int ("SELECT count (*) FROM alerts"
45468                     " WHERE event = %llu"
45469                     " AND (EXISTS (SELECT * FROM alert_condition_data"
45470                     "              WHERE name = 'filter_id'"
45471                     "              AND data = (SELECT uuid FROM filters"
45472                     "                          WHERE id = %llu)"
45473                     "              AND alert = alerts.id)"
45474                     " AND (condition = %i OR condition = %i))",
45475                     EVENT_TASK_RUN_STATUS_CHANGED,
45476                     filter,
45477                     ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45478                     ALERT_CONDITION_FILTER_COUNT_CHANGED);
45479 }
45480 
45481 /**
45482  * @brief Check whether a filter is in use by any secinfo alert conditions.
45483  *
45484  * @param[in]  filter  Filter.
45485  *
45486  * @return 1 yes, 0 no.
45487  */
45488 static int
filter_in_use_for_secinfo_event(filter_t filter)45489 filter_in_use_for_secinfo_event (filter_t filter)
45490 {
45491   return !!sql_int ("SELECT count (*) FROM alerts"
45492                     " WHERE (event = %llu OR event = %llu)"
45493                     " AND (EXISTS (SELECT * FROM alert_condition_data"
45494                     "              WHERE name = 'filter_id'"
45495                     "              AND data = (SELECT uuid FROM filters"
45496                     "                          WHERE id = %llu)"
45497                     "              AND alert = alerts.id)"
45498                     " AND (condition = %i OR condition = %i))",
45499                     EVENT_NEW_SECINFO,
45500                     EVENT_UPDATED_SECINFO,
45501                     filter,
45502                     ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45503                     ALERT_CONDITION_FILTER_COUNT_CHANGED);
45504 }
45505 
45506 /**
45507  * @brief Check whether a trashcan filter is in use.
45508  *
45509  * @param[in]  filter  Filter.
45510  *
45511  * @return 1 yes, 0 no.
45512  */
45513 int
trash_filter_in_use(filter_t filter)45514 trash_filter_in_use (filter_t filter)
45515 {
45516   return !!sql_int ("SELECT count (*) FROM alerts_trash"
45517                     " WHERE (filter = %llu"
45518                     "        AND filter_location = "
45519                                     G_STRINGIFY (LOCATION_TRASH) ")"
45520                     "   OR (EXISTS (SELECT *"
45521                     "               FROM alert_condition_data_trash"
45522                     "               WHERE name = 'filter_id'"
45523                     "                 AND data = (SELECT uuid"
45524                     "                             FROM filters_trash"
45525                     "                             WHERE id = %llu)"
45526                     "                 AND alert = alerts_trash.id)"
45527                     "       AND (condition = %i OR condition = %i))",
45528                     filter,
45529                     filter,
45530                     ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45531                     ALERT_CONDITION_FILTER_COUNT_CHANGED);
45532 }
45533 
45534 /**
45535  * @brief Check whether a filter is writable.
45536  *
45537  * @param[in]  filter  Filter.
45538  *
45539  * @return 1 yes, 0 no.
45540  */
45541 int
filter_writable(filter_t filter)45542 filter_writable (filter_t filter)
45543 {
45544   return 1;
45545 }
45546 
45547 /**
45548  * @brief Check whether a trashcan filter is writable.
45549  *
45550  * @param[in]  filter  Filter.
45551  *
45552  * @return 1 yes, 0 no.
45553  */
45554 int
trash_filter_writable(filter_t filter)45555 trash_filter_writable (filter_t filter)
45556 {
45557   return 1;
45558 }
45559 
45560 /**
45561  * @brief Filter columns for filter iterator.
45562  */
45563 #define FILTER_ITERATOR_FILTER_COLUMNS                        \
45564  { GET_ITERATOR_FILTER_COLUMNS, "type", "term", NULL }
45565 
45566 /**
45567  * @brief Filter iterator columns.
45568  */
45569 #define FILTER_ITERATOR_COLUMNS                               \
45570  {                                                            \
45571    GET_ITERATOR_COLUMNS (filters),                            \
45572    { "type" , NULL, KEYWORD_TYPE_STRING },                    \
45573    { "term", NULL, KEYWORD_TYPE_STRING },                     \
45574    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                       \
45575  }
45576 
45577 /**
45578  * @brief Filter iterator columns for trash case.
45579  */
45580 #define FILTER_ITERATOR_TRASH_COLUMNS                         \
45581  {                                                            \
45582    GET_ITERATOR_COLUMNS (filters_trash),                      \
45583    { "type" , NULL, KEYWORD_TYPE_STRING },                    \
45584    { "term", NULL, KEYWORD_TYPE_STRING },                     \
45585    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                       \
45586  }
45587 
45588 /**
45589  * @brief Count number of filters.
45590  *
45591  * @param[in]  get  GET params.
45592  *
45593  * @return Total number of filters in filtered set.
45594  */
45595 int
filter_count(const get_data_t * get)45596 filter_count (const get_data_t *get)
45597 {
45598   static const char *filter_columns[] = FILTER_ITERATOR_FILTER_COLUMNS;
45599   static column_t columns[] = FILTER_ITERATOR_COLUMNS;
45600   static column_t trash_columns[] = FILTER_ITERATOR_TRASH_COLUMNS;
45601   return count ("filter", get, columns, trash_columns, filter_columns,
45602                 0, 0, 0, TRUE);
45603 }
45604 
45605 /**
45606  * @brief Initialise a filter iterator, including observed filters.
45607  *
45608  * @param[in]  iterator    Iterator.
45609  * @param[in]  get         GET data.
45610  *
45611  * @return 0 success, 1 failed to find filter, 2 failed to find filter (filt_id),
45612  *         -1 error.
45613  */
45614 int
init_filter_iterator(iterator_t * iterator,const get_data_t * get)45615 init_filter_iterator (iterator_t* iterator, const get_data_t *get)
45616 {
45617   static const char *filter_columns[] = FILTER_ITERATOR_FILTER_COLUMNS;
45618   static column_t columns[] = FILTER_ITERATOR_COLUMNS;
45619   static column_t trash_columns[] = FILTER_ITERATOR_TRASH_COLUMNS;
45620 
45621   return init_get_iterator (iterator,
45622                             "filter",
45623                             get,
45624                             columns,
45625                             trash_columns,
45626                             filter_columns,
45627                             0,
45628                             NULL,
45629                             NULL,
45630                             TRUE);
45631 }
45632 
45633 /**
45634  * @brief Get the type from a filter iterator.
45635  *
45636  * @param[in]  iterator  Iterator.
45637  *
45638  * @return The type of the filter, or NULL if iteration is complete.  Freed by
45639  *         cleanup_iterator.  "" for any type.
45640  */
45641 const char*
filter_iterator_type(iterator_t * iterator)45642 filter_iterator_type (iterator_t* iterator)
45643 {
45644   const char *ret;
45645   if (iterator->done) return NULL;
45646   ret = iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT);
45647   return ret ? ret : "";
45648 }
45649 
45650 /**
45651  * @brief Get the term from a filter iterator.
45652  *
45653  * @param[in]  iterator  Iterator.
45654  *
45655  * @return The term of the filter, or NULL if iteration is complete.  Freed by
45656  *         cleanup_iterator.
45657  */
45658 DEF_ACCESS (filter_iterator_term, GET_ITERATOR_COLUMN_COUNT + 1);
45659 
45660 /**
45661  * @brief Initialise a filter alert iterator.
45662  *
45663  * Iterates over all alerts that use the filter.
45664  *
45665  * @param[in]  iterator   Iterator.
45666  * @param[in]  filter     Filter.
45667  */
45668 void
init_filter_alert_iterator(iterator_t * iterator,filter_t filter)45669 init_filter_alert_iterator (iterator_t* iterator, filter_t filter)
45670 {
45671   gchar *available, *with_clause;
45672   get_data_t get;
45673   array_t *permissions;
45674 
45675   assert (filter);
45676 
45677   get.trash = 0;
45678   permissions = make_array ();
45679   array_add (permissions, g_strdup ("get_alerts"));
45680   available = acl_where_owned ("alert", &get, 1, "any", 0, permissions, 0,
45681                                &with_clause);
45682   array_free (permissions);
45683 
45684   init_iterator (iterator,
45685                  "%s"
45686                  " SELECT name, uuid, %s FROM alerts"
45687                  " WHERE filter = %llu"
45688                  " OR (EXISTS (SELECT * FROM alert_condition_data"
45689                  "             WHERE name = 'filter_id'"
45690                  "             AND data = (SELECT uuid FROM filters"
45691                  "                         WHERE id = %llu)"
45692                  "             AND alert = alerts.id)"
45693                  "     AND (condition = %i OR condition = %i))"
45694                  " ORDER BY name ASC;",
45695                  with_clause ? with_clause : "",
45696                  available,
45697                  filter,
45698                  filter,
45699                  ALERT_CONDITION_FILTER_COUNT_AT_LEAST,
45700                  ALERT_CONDITION_FILTER_COUNT_CHANGED);
45701 
45702   g_free (with_clause);
45703   g_free (available);
45704 }
45705 
45706 /**
45707  * @brief Get the name from a filter_alert iterator.
45708  *
45709  * @param[in]  iterator  Iterator.
45710  *
45711  * @return The name of the host, or NULL if iteration is complete.  Freed by
45712  *         cleanup_iterator.
45713  */
45714 DEF_ACCESS (filter_alert_iterator_name, 0);
45715 
45716 /**
45717  * @brief Get the UUID from a filter_alert iterator.
45718  *
45719  * @param[in]  iterator  Iterator.
45720  *
45721  * @return The UUID of the host, or NULL if iteration is complete.  Freed by
45722  *         cleanup_iterator.
45723  */
45724 DEF_ACCESS (filter_alert_iterator_uuid, 1);
45725 
45726 /**
45727  * @brief Get the read permission status from a GET iterator.
45728  *
45729  * @param[in]  iterator  Iterator.
45730  *
45731  * @return 1 if may read, else 0.
45732  */
45733 int
filter_alert_iterator_readable(iterator_t * iterator)45734 filter_alert_iterator_readable (iterator_t* iterator)
45735 {
45736   if (iterator->done) return 0;
45737   return iterator_int (iterator, 2);
45738 }
45739 
45740 /**
45741  * @brief Modify a filter.
45742  *
45743  * @param[in]   filter_id       UUID of filter.
45744  * @param[in]   name            Name of filter.
45745  * @param[in]   comment         Comment on filter.
45746  * @param[in]   term            Filter term.
45747  * @param[in]   type            Type of filter.
45748  *
45749  * @return 0 success, 1 failed to find filter, 2 filter with new name exists,
45750  *         3 error in type name, 4 filter_id required, 5 filter is in use so
45751  *         type must be "result", 6 filter is in use so type must be "info",
45752  *         99 permission denied, -1 internal error.
45753  */
45754 int
modify_filter(const char * filter_id,const char * name,const char * comment,const char * term,const char * type)45755 modify_filter (const char *filter_id, const char *name, const char *comment,
45756                const char *term, const char *type)
45757 {
45758   gchar *quoted_name, *quoted_comment, *quoted_term, *quoted_type, *clean_term;
45759   filter_t filter;
45760 
45761   if (filter_id == NULL)
45762     return 4;
45763 
45764   type = type_db_name (type);
45765   if (type && !((strcmp (type, "") == 0) || valid_type (type)))
45766     return 3;
45767 
45768   sql_begin_immediate ();
45769 
45770   assert (current_credentials.uuid);
45771 
45772   if (acl_user_may ("modify_filter") == 0)
45773     {
45774       sql_rollback ();
45775       return 99;
45776     }
45777 
45778   filter = 0;
45779   if (find_filter_with_permission (filter_id, &filter, "modify_filter"))
45780     {
45781       sql_rollback ();
45782       return -1;
45783     }
45784 
45785   if (filter == 0)
45786     {
45787       sql_rollback ();
45788       return 1;
45789     }
45790 
45791   /* If the filter is linked to an alert, check that the type is valid. */
45792 
45793   if ((filter_in_use_for_output (filter)
45794        || filter_in_use_for_result_event (filter))
45795       && type
45796       && strcasecmp (type, "result"))
45797     {
45798       sql_rollback ();
45799       return 5;
45800     }
45801 
45802   if (filter_in_use_for_secinfo_event (filter)
45803       && type
45804       && strcasecmp (type, "info"))
45805     {
45806       sql_rollback ();
45807       return 6;
45808     }
45809 
45810   /* Check whether a filter with the same name exists already. */
45811   if (name)
45812     {
45813       if (resource_with_name_exists (name, "filter", filter))
45814         {
45815           sql_rollback ();
45816           return 2;
45817         }
45818     }
45819 
45820   quoted_name = sql_quote(name ?: "");
45821   clean_term = manage_clean_filter (term ? term : "");
45822   quoted_term = sql_quote (clean_term);
45823   g_free (clean_term);
45824   quoted_comment = sql_quote (comment ? comment : "");
45825   quoted_type = sql_quote (type ? type : "");
45826 
45827   sql ("UPDATE filters SET"
45828        " name = '%s',"
45829        " comment = '%s',"
45830        " term = '%s',"
45831        " type = lower ('%s'),"
45832        " modification_time = m_now ()"
45833        " WHERE id = %llu;",
45834        quoted_name,
45835        quoted_comment,
45836        quoted_term,
45837        quoted_type,
45838        filter);
45839 
45840   g_free (quoted_comment);
45841   g_free (quoted_name);
45842   g_free (quoted_term);
45843   g_free (quoted_type);
45844 
45845   sql_commit ();
45846 
45847   return 0;
45848 }
45849 
45850 
45851 /* Schema. */
45852 
45853 /**
45854  * @brief Generate the GMP schema.
45855  *
45856  * @param[in]  format         Name of schema format, "XML" or NULL for XML.
45857  * @param[out] output_return  NULL or location for output.
45858  * @param[out] output_length  NULL or location for length of output.
45859  * @param[out] extension      NULL or location for schema extension.
45860  * @param[out] content_type   NULL or location for schema content type.
45861  *
45862  * @return 0 success, 1 failed to find schema format, -1 error.
45863  */
45864 int
manage_schema(gchar * format,gchar ** output_return,gsize * output_length,gchar ** extension,gchar ** content_type)45865 manage_schema (gchar *format, gchar **output_return, gsize *output_length,
45866                gchar **extension, gchar **content_type)
45867 {
45868   /* Pass the XML file to the schema generate script, sending the output
45869    * to a file. */
45870 
45871   {
45872     gchar *script, *script_dir;
45873     gchar *uuid_format;
45874     char output_dir[] = "/tmp/gvmd_schema_XXXXXX";
45875 
45876     if (mkdtemp (output_dir) == NULL)
45877       {
45878         g_warning ("%s: mkdtemp failed", __func__);
45879         return -1;
45880       }
45881 
45882     /* Setup file names. */
45883 
45884     if (format == NULL)
45885       {
45886         if (extension)
45887           *extension = g_strdup ("xml");
45888         if (content_type)
45889           *content_type = g_strdup ("text/xml");
45890         uuid_format = "18e826fc-dab6-11df-b913-002264764cea";
45891       }
45892     else if (strcasecmp (format, "HTML") == 0)
45893       {
45894         if (extension)
45895           *extension = g_strdup ("html");
45896         if (content_type)
45897           *content_type = g_strdup ("text/html");
45898         uuid_format = "02052818-dab6-11df-9be4-002264764cea";
45899       }
45900     else if (strcasecmp (format, "RNC") == 0)
45901       {
45902         if (extension)
45903           *extension = g_strdup ("rnc");
45904         if (content_type)
45905           *content_type = g_strdup ("text/x-rnc");
45906         uuid_format = "787a4a18-dabc-11df-9486-002264764cea";
45907       }
45908     else if (strcasecmp (format, "XML") == 0)
45909       {
45910         if (extension)
45911           *extension = g_strdup ("xml");
45912         if (content_type)
45913           *content_type = g_strdup ("text/xml");
45914         uuid_format = "18e826fc-dab6-11df-b913-002264764cea";
45915       }
45916     else
45917       return 1;
45918 
45919     script_dir = g_build_filename (GVMD_DATA_DIR,
45920                                    "global_schema_formats",
45921                                    uuid_format,
45922                                    NULL);
45923 
45924     script = g_build_filename (script_dir, "generate", NULL);
45925 
45926     if (!gvm_file_is_readable (script))
45927       {
45928         g_free (script);
45929         g_free (script_dir);
45930         if (extension) g_free (*extension);
45931         if (content_type) g_free (*content_type);
45932         return -1;
45933       }
45934 
45935     {
45936       gchar *output_file, *command;
45937       char *previous_dir;
45938       int ret;
45939 
45940       /* Change into the script directory. */
45941 
45942       previous_dir = getcwd (NULL, 0);
45943       if (previous_dir == NULL)
45944         {
45945           g_warning ("%s: Failed to getcwd: %s",
45946                      __func__,
45947                      strerror (errno));
45948           g_free (previous_dir);
45949           g_free (script);
45950           g_free (script_dir);
45951           if (extension) g_free (*extension);
45952           if (content_type) g_free (*content_type);
45953           return -1;
45954         }
45955 
45956       if (chdir (script_dir))
45957         {
45958           g_warning ("%s: Failed to chdir: %s",
45959                      __func__,
45960                      strerror (errno));
45961           g_free (previous_dir);
45962           g_free (script);
45963           g_free (script_dir);
45964           if (extension) g_free (*extension);
45965           if (content_type) g_free (*content_type);
45966           return -1;
45967         }
45968       g_free (script_dir);
45969 
45970       output_file = g_strdup_printf ("%s/report.out", output_dir);
45971 
45972       /* Call the script. */
45973 
45974       command = g_strdup_printf ("%s " GVMD_DATA_DIR
45975                                  "/global_schema_formats"
45976                                  "/18e826fc-dab6-11df-b913-002264764cea/GMP.xml"
45977                                  " > %s"
45978                                  " 2> /dev/null",
45979                                  script,
45980                                  output_file);
45981       g_free (script);
45982 
45983       g_debug ("   command: %s", command);
45984 
45985       ret = system (command);
45986       if ((ret == -1)
45987           /* The schema "generate" script must exit with 0. */
45988           || WEXITSTATUS (ret))
45989         {
45990           g_warning ("%s: system failed with ret %i, %i, %s",
45991                      __func__,
45992                      ret,
45993                      WEXITSTATUS (ret),
45994                      command);
45995           if (chdir (previous_dir))
45996             g_warning ("%s: and chdir failed",
45997                        __func__);
45998           g_free (previous_dir);
45999           g_free (command);
46000           g_free (output_file);
46001           if (extension) g_free (*extension);
46002           if (content_type) g_free (*content_type);
46003           return -1;
46004         }
46005 
46006       {
46007         GError *get_error;
46008         gchar *output;
46009         gsize output_len;
46010 
46011         g_free (command);
46012 
46013         /* Change back to the previous directory. */
46014 
46015         if (chdir (previous_dir))
46016           {
46017             g_warning ("%s: Failed to chdir back: %s",
46018                        __func__,
46019                        strerror (errno));
46020             g_free (previous_dir);
46021             if (extension) g_free (*extension);
46022             if (content_type) g_free (*content_type);
46023             return -1;
46024           }
46025         g_free (previous_dir);
46026 
46027         /* Read the script output from file. */
46028 
46029         get_error = NULL;
46030         g_file_get_contents (output_file,
46031                              &output,
46032                              &output_len,
46033                              &get_error);
46034         g_free (output_file);
46035         if (get_error)
46036           {
46037             g_warning ("%s: Failed to get output: %s",
46038                        __func__,
46039                        get_error->message);
46040             g_error_free (get_error);
46041             if (extension) g_free (*extension);
46042             if (content_type) g_free (*content_type);
46043             return -1;
46044           }
46045 
46046         /* Remove the output directory. */
46047 
46048         gvm_file_remove_recurse (output_dir);
46049 
46050         /* Return the output. */
46051 
46052         if (output_length) *output_length = output_len;
46053 
46054         if (output_return) *output_return = output;
46055         return 0;
46056       }
46057     }
46058   }
46059 }
46060 
46061 
46062 /* Trashcan. */
46063 
46064 /**
46065  * @brief Restore a resource from the trashcan.
46066  *
46067  * @param[in]  id  UUID of resource.
46068  *
46069  * @return 0 success, 1 fail because the resource refers to another resource
46070  *         in the trashcan, 2 failed to find resource in trashcan, 3 fail
46071  *         because resource with such name exists already, 4 fail because
46072  *         resource with UUID exists already, 99 permission denied, -1 error.
46073  */
46074 int
manage_restore(const char * id)46075 manage_restore (const char *id)
46076 {
46077   resource_t resource = 0;
46078   int ret;
46079 
46080   assert (current_credentials.uuid);
46081 
46082   sql_begin_immediate ();
46083 
46084   if (acl_user_may ("restore") == 0)
46085     {
46086       sql_rollback ();
46087       return 99;
46088     }
46089 
46090   /* Port List. */
46091   ret = restore_port_list (id);
46092   if (ret != 2)
46093     return ret;
46094 
46095   /* Report Format. */
46096   ret = restore_report_format (id);
46097   if (ret != 2)
46098     return ret;
46099 
46100   /* Ticket. */
46101   ret = restore_ticket (id);
46102   if (ret != 2)
46103     return ret;
46104 
46105   /* Config. */
46106 
46107   if (find_trash ("config", id, &resource))
46108     {
46109       sql_rollback ();
46110       return -1;
46111     }
46112 
46113   if (resource)
46114     {
46115       config_t config;
46116 
46117       if (sql_int ("SELECT count(*) FROM configs"
46118                    " WHERE name ="
46119                    " (SELECT name FROM configs_trash WHERE id = %llu)"
46120                    " AND " ACL_USER_OWNS () ";",
46121                    resource,
46122                    current_credentials.uuid))
46123         {
46124           sql_rollback ();
46125           return 3;
46126         }
46127 
46128       /* Check if it uses a scanner in the trashcan. */
46129       if (sql_int ("SELECT scanner_location = " G_STRINGIFY (LOCATION_TRASH)
46130                    " FROM configs_trash WHERE id = %llu;",
46131                    resource))
46132         {
46133           sql_rollback ();
46134           return 1;
46135         }
46136 
46137       sql ("INSERT INTO configs"
46138            " (uuid, owner, name, nvt_selector, comment, family_count,"
46139            "  nvt_count, families_growing, nvts_growing, type, scanner,"
46140            "  predefined, creation_time, modification_time, usage_type)"
46141            " SELECT uuid, owner, name, nvt_selector, comment, family_count,"
46142            "        nvt_count, families_growing, nvts_growing, type, scanner,"
46143            "        predefined, creation_time, modification_time, usage_type"
46144            " FROM configs_trash WHERE id = %llu;",
46145            resource);
46146 
46147       config = sql_last_insert_id ();
46148 
46149       sql ("INSERT INTO config_preferences"
46150            " (config, type, name, value, default_value, hr_name)"
46151            " SELECT %llu, type, name, value, default_value, hr_name"
46152            " FROM config_preferences_trash WHERE config = %llu;",
46153            config,
46154            resource);
46155 
46156       /* Update the config in any trashcan tasks. */
46157       sql ("UPDATE tasks"
46158            " SET config = %llu,"
46159            "     config_location = " G_STRINGIFY (LOCATION_TABLE)
46160            " WHERE config = %llu"
46161            " AND config_location = " G_STRINGIFY (LOCATION_TRASH),
46162            config,
46163            resource);
46164 
46165       permissions_set_locations ("config", resource, config,
46166                                  LOCATION_TABLE);
46167       tags_set_locations ("config", resource, config,
46168                           LOCATION_TABLE);
46169 
46170       sql ("DELETE FROM config_preferences_trash WHERE config = %llu;",
46171            resource);
46172       sql ("DELETE FROM configs_trash WHERE id = %llu;", resource);
46173       sql_commit ();
46174       return 0;
46175     }
46176 
46177   /* Alert. */
46178 
46179   if (find_trash ("alert", id, &resource))
46180     {
46181       sql_rollback ();
46182       return -1;
46183     }
46184 
46185   if (resource)
46186     {
46187       alert_t alert;
46188 
46189       if (sql_int ("SELECT count(*) FROM alerts"
46190                    " WHERE name ="
46191                    " (SELECT name FROM alerts_trash WHERE id = %llu)"
46192                    " AND " ACL_USER_OWNS () ";",
46193                    resource,
46194                    current_credentials.uuid))
46195         {
46196           sql_rollback ();
46197           return 3;
46198         }
46199 
46200       /* Check if it uses a filter in the trashcan. */
46201       if (sql_int ("SELECT filter_location = " G_STRINGIFY (LOCATION_TRASH)
46202                    " FROM alerts_trash WHERE id = %llu;",
46203                    resource))
46204         {
46205           sql_rollback ();
46206           return 1;
46207         }
46208 
46209       sql ("INSERT INTO alerts"
46210            " (uuid, owner, name, comment, event, condition, method, filter,"
46211            "  active, creation_time, modification_time)"
46212            " SELECT uuid, owner, name, comment, event, condition, method,"
46213            "        filter, active, creation_time, modification_time"
46214            " FROM alerts_trash WHERE id = %llu;",
46215            resource);
46216 
46217       alert = sql_last_insert_id ();
46218 
46219       sql ("INSERT INTO alert_condition_data"
46220            " (alert, name, data)"
46221            " SELECT %llu, name, data"
46222            " FROM alert_condition_data_trash WHERE alert = %llu;",
46223            alert,
46224            resource);
46225 
46226       sql ("INSERT INTO alert_event_data"
46227            " (alert, name, data)"
46228            " SELECT %llu, name, data"
46229            " FROM alert_event_data_trash WHERE alert = %llu;",
46230            alert,
46231            resource);
46232 
46233       sql ("INSERT INTO alert_method_data"
46234            " (alert, name, data)"
46235            " SELECT %llu, name, data"
46236            " FROM alert_method_data_trash WHERE alert = %llu;",
46237            alert,
46238            resource);
46239 
46240       /* Update the alert in any trashcan tasks. */
46241       sql ("UPDATE task_alerts"
46242            " SET alert = %llu,"
46243            "     alert_location = " G_STRINGIFY (LOCATION_TABLE)
46244            " WHERE alert = %llu"
46245            " AND alert_location = " G_STRINGIFY (LOCATION_TRASH),
46246            alert,
46247            resource);
46248 
46249       permissions_set_locations ("alert", resource, alert,
46250                                  LOCATION_TABLE);
46251       tags_set_locations ("alert", resource, alert,
46252                           LOCATION_TABLE);
46253 
46254       sql ("DELETE FROM alert_condition_data_trash WHERE alert = %llu;",
46255            resource);
46256       sql ("DELETE FROM alert_event_data_trash WHERE alert = %llu;",
46257            resource);
46258       sql ("DELETE FROM alert_method_data_trash WHERE alert = %llu;",
46259            resource);
46260       sql ("DELETE FROM alerts_trash WHERE id = %llu;",
46261            resource);
46262       sql_commit ();
46263       return 0;
46264     }
46265 
46266   /* Filter. */
46267 
46268   if (find_trash ("filter", id, &resource))
46269     {
46270       sql_rollback ();
46271       return -1;
46272     }
46273 
46274   if (resource)
46275     {
46276       if (sql_int ("SELECT count(*) FROM filters"
46277                    " WHERE name ="
46278                    " (SELECT name FROM filters_trash WHERE id = %llu)"
46279                    " AND " ACL_USER_OWNS () ";",
46280                    resource,
46281                    current_credentials.uuid))
46282         {
46283           sql_rollback ();
46284           return 3;
46285         }
46286 
46287       sql ("INSERT INTO filters"
46288            " (uuid, owner, name, comment, type, term, creation_time,"
46289            "  modification_time)"
46290            " SELECT uuid, owner, name, comment, type, term, creation_time,"
46291            "        modification_time"
46292            " FROM filters_trash WHERE id = %llu;",
46293            resource);
46294 
46295       /* Update the filter in any trashcan alerts. */
46296       sql ("UPDATE alerts_trash"
46297            " SET filter = %llu,"
46298            "     filter_location = " G_STRINGIFY (LOCATION_TABLE)
46299            " WHERE filter = %llu"
46300            " AND filter_location = " G_STRINGIFY (LOCATION_TRASH),
46301            sql_last_insert_id (),
46302            resource);
46303 
46304       permissions_set_locations ("filter", resource,
46305                                  sql_last_insert_id (),
46306                                  LOCATION_TABLE);
46307       tags_set_locations ("filter", resource,
46308                           sql_last_insert_id (),
46309                           LOCATION_TABLE);
46310 
46311       sql ("DELETE FROM filters_trash WHERE id = %llu;", resource);
46312       sql_commit ();
46313       return 0;
46314     }
46315 
46316   /* Group. */
46317 
46318   if (find_trash ("group", id, &resource))
46319     {
46320       sql_rollback ();
46321       return -1;
46322     }
46323 
46324   if (resource)
46325     {
46326       group_t group;
46327       GArray *affected_users;
46328       iterator_t users_iter;
46329 
46330       if (sql_int ("SELECT count(*) FROM groups"
46331                    " WHERE name ="
46332                    " (SELECT name FROM groups_trash WHERE id = %llu)"
46333                    " AND " ACL_USER_OWNS () ";",
46334                    resource,
46335                    current_credentials.uuid))
46336         {
46337           sql_rollback ();
46338           return 3;
46339         }
46340 
46341       sql ("INSERT INTO groups"
46342            " (uuid, owner, name, comment, creation_time,"
46343            "  modification_time)"
46344            " SELECT uuid, owner, name, comment, creation_time,"
46345            "        modification_time"
46346            " FROM groups_trash WHERE id = %llu;",
46347            resource);
46348 
46349       group = sql_last_insert_id ();
46350 
46351       sql ("INSERT INTO group_users"
46352            " (\"group\", \"user\")"
46353            " SELECT %llu, \"user\""
46354            " FROM group_users_trash WHERE \"group\" = %llu;",
46355            group,
46356            resource);
46357 
46358       permissions_set_locations ("group", resource, group, LOCATION_TABLE);
46359       tags_set_locations ("group", resource, group, LOCATION_TABLE);
46360 
46361       permissions_set_subjects ("group", resource, group, LOCATION_TABLE);
46362 
46363       sql ("DELETE FROM group_users_trash WHERE \"group\" = %llu;", resource);
46364       sql ("DELETE FROM groups_trash WHERE id = %llu;", resource);
46365 
46366       affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
46367       init_iterator (&users_iter,
46368                      "SELECT \"user\" FROM group_users"
46369                      " WHERE \"group\" = %llu",
46370                      group);
46371       while (next (&users_iter))
46372         {
46373           user_t user = iterator_int64 (&users_iter, 0);
46374           g_array_append_val (affected_users, user);
46375         }
46376       cleanup_iterator (&users_iter);
46377       cache_all_permissions_for_users (affected_users);
46378       g_array_free (affected_users, TRUE);
46379 
46380       sql_commit ();
46381       return 0;
46382     }
46383 
46384   /* Credential. */
46385 
46386   if (find_trash ("credential", id, &resource))
46387     {
46388       sql_rollback ();
46389       return -1;
46390     }
46391 
46392   if (resource)
46393     {
46394       credential_t credential;
46395 
46396       if (sql_int ("SELECT count(*) FROM credentials"
46397                    " WHERE name ="
46398                    " (SELECT name FROM credentials_trash WHERE id = %llu)"
46399                    " AND " ACL_USER_OWNS () ";",
46400                    resource,
46401                    current_credentials.uuid))
46402         {
46403           sql_rollback ();
46404           return 3;
46405         }
46406 
46407       sql ("INSERT INTO credentials"
46408            " (uuid, owner, name, comment, creation_time,"
46409            "  modification_time, type)"
46410            " SELECT uuid, owner, name, comment, creation_time,"
46411            "        modification_time, type"
46412            " FROM credentials_trash WHERE id = %llu;",
46413            resource);
46414 
46415       credential = sql_last_insert_id ();
46416 
46417       sql ("INSERT INTO credentials_data"
46418            " (credential, type, value)"
46419            " SELECT %llu, type, value"
46420            " FROM credentials_trash_data"
46421            " WHERE credential = %llu",
46422            credential,
46423            resource);
46424 
46425       /* Update the credentials in any trashcan targets. */
46426       sql ("UPDATE targets_trash_login_data"
46427            " SET credential_location = " G_STRINGIFY (LOCATION_TABLE) ","
46428            "     credential = %llu"
46429            " WHERE credential = %llu"
46430            " AND credential_location = " G_STRINGIFY (LOCATION_TRASH) ";",
46431            credential,
46432            resource);
46433       sql ("UPDATE scanners_trash"
46434            " SET credential_location = " G_STRINGIFY (LOCATION_TABLE) ","
46435            "     credential = %llu"
46436            " WHERE credential = %llu"
46437            " AND credential_location = " G_STRINGIFY (LOCATION_TRASH) ";",
46438            credential,
46439            resource);
46440 
46441       permissions_set_locations ("credential", resource, credential,
46442                                  LOCATION_TABLE);
46443       tags_set_locations ("credential", resource, credential,
46444                           LOCATION_TABLE);
46445 
46446       sql ("DELETE FROM credentials_trash_data WHERE credential = %llu;",
46447            resource);
46448       sql ("DELETE FROM credentials_trash WHERE id = %llu;", resource);
46449       sql_commit ();
46450       return 0;
46451     }
46452 
46453   /* Note. */
46454 
46455   if (find_trash ("note", id, &resource))
46456     {
46457       sql_rollback ();
46458       return -1;
46459     }
46460 
46461   if (resource)
46462     {
46463       sql ("INSERT INTO notes"
46464            " (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
46465            "  port, severity, task, result, end_time)"
46466            " SELECT uuid, owner, nvt, creation_time, modification_time, text,"
46467            "        hosts, port, severity, task, result, end_time"
46468            " FROM notes_trash WHERE id = %llu;",
46469            resource);
46470 
46471       permissions_set_locations ("note", resource,
46472                                  sql_last_insert_id (),
46473                                  LOCATION_TABLE);
46474       tags_set_locations ("note", resource,
46475                                  sql_last_insert_id (),
46476                                  LOCATION_TABLE);
46477 
46478       sql ("DELETE FROM notes_trash WHERE id = %llu;", resource);
46479       sql_commit ();
46480       return 0;
46481     }
46482 
46483   /* Override. */
46484 
46485   if (find_trash ("override", id, &resource))
46486     {
46487       sql_rollback ();
46488       return -1;
46489     }
46490 
46491   if (resource)
46492     {
46493       override_t override;
46494       GHashTable *reports;
46495       GHashTableIter reports_iter;
46496       report_t *reports_ptr;
46497       gchar *users_where;
46498       int auto_cache_rebuild;
46499 
46500       sql ("INSERT INTO overrides"
46501            " (uuid, owner, nvt, creation_time, modification_time, text, hosts,"
46502            "  port, severity, new_severity, task, result, end_time, result_nvt)"
46503            " SELECT uuid, owner, nvt, creation_time, modification_time, text,"
46504            "        hosts, port, severity, new_severity, task,"
46505            "        result, end_time, result_nvt"
46506            " FROM overrides_trash WHERE id = %llu;",
46507            resource);
46508 
46509       override = sql_last_insert_id ();
46510 
46511       permissions_set_locations ("override", resource,
46512                                  override,
46513                                  LOCATION_TABLE);
46514       tags_set_locations ("override", resource,
46515                           override,
46516                           LOCATION_TABLE);
46517       users_where = acl_users_with_access_where ("override", id, NULL,
46518                                                  "id");
46519 
46520       reports = reports_for_override (override);
46521       g_hash_table_iter_init (&reports_iter, reports);
46522       reports_ptr = NULL;
46523       auto_cache_rebuild = setting_auto_cache_rebuild_int ();
46524       while (g_hash_table_iter_next (&reports_iter,
46525                                     ((gpointer*)&reports_ptr), NULL))
46526         {
46527           if (auto_cache_rebuild)
46528             report_cache_counts (*reports_ptr, 0, 1, users_where);
46529           else
46530             report_clear_count_cache (*reports_ptr, 0, 1, users_where);
46531         }
46532       g_hash_table_destroy (reports);
46533       g_free (users_where);
46534 
46535       sql ("DELETE FROM overrides_trash WHERE id = %llu;", resource);
46536 
46537       sql_commit ();
46538       return 0;
46539     }
46540 
46541   /* Permission. */
46542 
46543   if (find_trash ("permission", id, &resource))
46544     {
46545       sql_rollback ();
46546       return -1;
46547     }
46548 
46549   if (resource)
46550     {
46551       permission_t permission;
46552       char *name, *resource_type;
46553       resource_t perm_resource;
46554       GHashTable *reports = NULL;
46555       int clear_original = 0;
46556       char *subject_type;
46557       resource_t subject;
46558       gchar *subject_where;
46559 
46560       sql ("INSERT INTO permissions"
46561            " (uuid, owner, name, comment, resource_type, resource,"
46562            "  resource_uuid, resource_location, subject_type, subject,"
46563            "  subject_location, creation_time, modification_time)"
46564            " SELECT uuid, owner, name, comment, resource_type, resource,"
46565            "  resource_uuid, resource_location, subject_type, subject,"
46566            "  subject_location, creation_time, modification_time"
46567            " FROM permissions_trash"
46568            " WHERE id = %llu;",
46569            resource);
46570 
46571       permission = sql_last_insert_id ();
46572       name = permission_name (permission);
46573       resource_type = permission_resource_type (permission);
46574       perm_resource = permission_resource (permission);
46575 
46576       subject_type = permission_subject_type (permission);
46577       subject = permission_subject (permission);
46578       subject_where = subject_where_clause (subject_type, subject);
46579       free (subject_type);
46580 
46581       tags_set_locations ("permission", resource, permission, LOCATION_TABLE);
46582 
46583       if (strcasecmp (name, "super") == 0)
46584         cache_all_permissions_for_users (NULL);
46585       else if (perm_resource != 0
46586                && resource_type && strcmp (resource_type, ""))
46587         cache_permissions_for_resource (resource_type, perm_resource, NULL);
46588 
46589       /* Update Reports cache */
46590       if (resource_type && perm_resource
46591           && strcmp (resource_type, "override") == 0)
46592         {
46593           reports = reports_for_override (perm_resource);
46594         }
46595       else if (strcasecmp (name, "super") == 0)
46596         {
46597           reports = reports_hashtable ();
46598           clear_original = 1;
46599         }
46600 
46601       if (reports && g_hash_table_size (reports))
46602         {
46603           GHashTableIter reports_iter;
46604           report_t *reports_ptr;
46605           int auto_cache_rebuild;
46606 
46607           reports_ptr = NULL;
46608           g_hash_table_iter_init (&reports_iter, reports);
46609           auto_cache_rebuild = setting_auto_cache_rebuild_int ();
46610           while (g_hash_table_iter_next (&reports_iter,
46611                                         ((gpointer*)&reports_ptr), NULL))
46612             {
46613               if (auto_cache_rebuild)
46614                 report_cache_counts (*reports_ptr, clear_original, 1,
46615                                      subject_where);
46616               else
46617                 report_clear_count_cache (*reports_ptr, clear_original, 1,
46618                                           subject_where);
46619             }
46620         }
46621       g_free (subject_where);
46622 
46623       if (reports)
46624         g_hash_table_destroy (reports);
46625 
46626       free (name);
46627       free (resource_type);
46628 
46629       sql ("DELETE FROM permissions_trash WHERE id = %llu;", resource);
46630       sql_commit ();
46631       return 0;
46632     }
46633 
46634   /* Role. */
46635 
46636   if (find_trash ("role", id, &resource))
46637     {
46638       sql_rollback ();
46639       return -1;
46640     }
46641 
46642   if (resource)
46643     {
46644       role_t role;
46645       GArray *affected_users;
46646       iterator_t users_iter;
46647 
46648       if (sql_int ("SELECT count(*) FROM roles"
46649                    " WHERE name ="
46650                    " (SELECT name FROM roles_trash WHERE id = %llu)"
46651                    " AND " ACL_USER_OWNS () ";",
46652                    resource,
46653                    current_credentials.uuid))
46654         {
46655           sql_rollback ();
46656           return 3;
46657         }
46658 
46659       sql ("INSERT INTO roles"
46660            " (uuid, owner, name, comment, creation_time,"
46661            "  modification_time)"
46662            " SELECT uuid, owner, name, comment, creation_time,"
46663            "        modification_time"
46664            " FROM roles_trash WHERE id = %llu;",
46665            resource);
46666 
46667       role = sql_last_insert_id ();
46668 
46669       sql ("INSERT INTO role_users"
46670            " (\"role\", \"user\")"
46671            " SELECT %llu, \"user\""
46672            " FROM role_users_trash WHERE role = %llu;",
46673            role,
46674            resource);
46675 
46676       permissions_set_locations ("role", resource, role, LOCATION_TABLE);
46677       tags_set_locations ("role", resource, role, LOCATION_TABLE);
46678 
46679       permissions_set_subjects ("role", resource, role, LOCATION_TABLE);
46680 
46681       sql ("DELETE FROM role_users_trash WHERE role = %llu;", resource);
46682       sql ("DELETE FROM roles_trash WHERE id = %llu;", resource);
46683 
46684       affected_users = g_array_new (TRUE, TRUE, sizeof (user_t));
46685       init_iterator (&users_iter,
46686                      "SELECT \"user\" FROM role_users"
46687                      " WHERE \"role\" = %llu",
46688                      role);
46689       while (next (&users_iter))
46690         {
46691           user_t user = iterator_int64 (&users_iter, 0);
46692           g_array_append_val (affected_users, user);
46693         }
46694       cleanup_iterator (&users_iter);
46695       cache_all_permissions_for_users (affected_users);
46696       g_array_free (affected_users, TRUE);
46697 
46698       sql_commit ();
46699       return 0;
46700     }
46701 
46702   /* Scanner. */
46703 
46704   if (find_trash ("scanner", id, &resource))
46705     {
46706       sql_rollback ();
46707       return -1;
46708     }
46709 
46710   if (resource)
46711     {
46712       scanner_t scanner;
46713       if (sql_int ("SELECT count(*) FROM scanners"
46714                    " WHERE name ="
46715                    " (SELECT name FROM scanners_trash WHERE id = %llu)"
46716                    " AND " ACL_USER_OWNS () ";",
46717                    resource, current_credentials.uuid))
46718         {
46719           sql_rollback ();
46720           return 3;
46721         }
46722 
46723       if (sql_int ("SELECT credential_location = " G_STRINGIFY (LOCATION_TRASH)
46724                    " FROM scanners_trash WHERE id = %llu;",
46725                    resource))
46726         {
46727           sql_rollback ();
46728           return 1;
46729         }
46730 
46731       sql ("INSERT INTO scanners"
46732            " (uuid, owner, name, comment, host, port, type, ca_pub,"
46733            "  credential, creation_time, modification_time)"
46734            " SELECT uuid, owner, name, comment, host, port, type, ca_pub,"
46735            "        credential, creation_time, modification_time"
46736            " FROM scanners_trash WHERE id = %llu;", resource);
46737 
46738       /* Update the scanner in any trashcan configs and tasks. */
46739       scanner = sql_last_insert_id ();
46740 
46741       sql ("UPDATE configs_trash"
46742            " SET scanner = %llu,"
46743            "     scanner_location = " G_STRINGIFY (LOCATION_TABLE)
46744            " WHERE scanner = %llu"
46745            " AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
46746            scanner, resource);
46747 
46748       sql ("UPDATE tasks"
46749            " SET scanner = %llu,"
46750            "     scanner_location = " G_STRINGIFY (LOCATION_TABLE)
46751            " WHERE scanner = %llu"
46752            " AND scanner_location = " G_STRINGIFY (LOCATION_TRASH),
46753            scanner, resource);
46754 
46755       permissions_set_locations ("scanner", resource,
46756                                  sql_last_insert_id (),
46757                                  LOCATION_TABLE);
46758       tags_set_locations ("scanner", resource,
46759                           sql_last_insert_id (), LOCATION_TABLE);
46760 
46761       sql ("DELETE FROM scanners_trash WHERE id = %llu;", resource);
46762       sql_commit ();
46763       return 0;
46764     }
46765 
46766   /* Schedule. */
46767 
46768   if (find_trash ("schedule", id, &resource))
46769     {
46770       sql_rollback ();
46771       return -1;
46772     }
46773 
46774   if (resource)
46775     {
46776       if (sql_int ("SELECT count(*) FROM schedules"
46777                    " WHERE name ="
46778                    " (SELECT name FROM schedules_trash WHERE id = %llu)"
46779                    " AND " ACL_USER_OWNS () ";",
46780                    resource,
46781                    current_credentials.uuid))
46782         {
46783           sql_rollback ();
46784           return 3;
46785         }
46786 
46787       sql ("INSERT INTO schedules"
46788            " (uuid, owner, name, comment, first_time, period, period_months,"
46789            "  byday, duration, timezone, creation_time,"
46790            "  modification_time, icalendar)"
46791            " SELECT uuid, owner, name, comment, first_time, period,"
46792            "        period_months, byday, duration, timezone,"
46793            "        creation_time, modification_time, icalendar"
46794            " FROM schedules_trash WHERE id = %llu;",
46795            resource);
46796 
46797       /* Update the schedule in any trashcan tasks. */
46798       sql ("UPDATE tasks"
46799            " SET schedule = %llu,"
46800            "     schedule_location = " G_STRINGIFY (LOCATION_TABLE)
46801            " WHERE schedule = %llu"
46802            " AND schedule_location = " G_STRINGIFY (LOCATION_TRASH),
46803            sql_last_insert_id (),
46804            resource);
46805 
46806       permissions_set_locations ("schedule", resource,
46807                                  sql_last_insert_id (),
46808                                  LOCATION_TABLE);
46809       tags_set_locations ("schedule", resource,
46810                           sql_last_insert_id (),
46811                           LOCATION_TABLE);
46812 
46813       sql ("DELETE FROM schedules_trash WHERE id = %llu;", resource);
46814       sql_commit ();
46815       return 0;
46816     }
46817 
46818   /* Tag */
46819 
46820   if (find_trash ("tag", id, &resource))
46821     {
46822       sql_rollback ();
46823       return -1;
46824     }
46825 
46826   if (resource)
46827     {
46828       tag_t restored_tag;
46829 
46830       sql ("INSERT INTO tags"
46831            " (uuid, owner, name, comment, creation_time,"
46832            "  modification_time, resource_type, active, value)"
46833            " SELECT uuid, owner, name, comment, creation_time,"
46834            "        modification_time, resource_type, active, value"
46835            " FROM tags_trash WHERE id = %llu;",
46836            resource);
46837 
46838       restored_tag = sql_last_insert_id ();
46839 
46840       sql ("INSERT INTO tag_resources"
46841            "  (tag, resource_type, resource, resource_uuid, resource_location)"
46842            " SELECT"
46843            "   %llu, resource_type, resource, resource_uuid, resource_location"
46844            " FROM tag_resources_trash WHERE tag = %llu;",
46845            restored_tag, resource);
46846 
46847       sql ("DELETE FROM tag_resources_trash WHERE tag = %llu",
46848            resource);
46849 
46850       permissions_set_locations ("tag", resource, restored_tag,
46851                                  LOCATION_TABLE);
46852 
46853       sql ("DELETE FROM tags_trash WHERE id = %llu;", resource);
46854       sql_commit ();
46855       return 0;
46856     }
46857 
46858   /* Target. */
46859 
46860   if (find_trash ("target", id, &resource))
46861     {
46862       sql_rollback ();
46863       return -1;
46864     }
46865 
46866   if (resource)
46867     {
46868       target_t restored_target;
46869 
46870       if (sql_int ("SELECT count(*) FROM targets"
46871                    " WHERE name ="
46872                    " (SELECT name FROM targets_trash WHERE id = %llu)"
46873                    " AND " ACL_USER_OWNS () ";",
46874                    resource,
46875                    current_credentials.uuid))
46876         {
46877           sql_rollback ();
46878           return 3;
46879         }
46880 
46881       /* Check if it uses a credential or port list in the trashcan. */
46882       if (sql_int ("SELECT port_list_location = " G_STRINGIFY (LOCATION_TRASH)
46883                    " OR EXISTS ("
46884                    "  SELECT *"
46885                    "  FROM targets_trash_login_data WHERE target = %llu"
46886                    "  AND credential_location = " G_STRINGIFY (LOCATION_TRASH)
46887                    " )"
46888                    " FROM targets_trash WHERE id = %llu;",
46889                    resource, resource))
46890         {
46891           sql_rollback ();
46892           return 1;
46893         }
46894 
46895       sql ("INSERT INTO targets"
46896            " (uuid, owner, name, hosts, exclude_hosts, comment,"
46897            "  port_list, reverse_lookup_only, reverse_lookup_unify,"
46898            "  alive_test, allow_simultaneous_ips,"
46899            "  creation_time, modification_time)"
46900            " SELECT uuid, owner, name, hosts, exclude_hosts, comment,"
46901            "        port_list, reverse_lookup_only, reverse_lookup_unify,"
46902            "        alive_test, allow_simultaneous_ips,"
46903            "        creation_time, modification_time"
46904            " FROM targets_trash WHERE id = %llu;",
46905            resource);
46906 
46907       restored_target = sql_last_insert_id ();
46908 
46909       /* Copy login data */
46910       sql ("INSERT INTO targets_login_data"
46911            " (target, type, credential, port)"
46912            "  SELECT %llu, type, credential, port"
46913            "   FROM targets_trash_login_data WHERE target = %llu;",
46914            restored_target, resource);
46915 
46916       sql ("DELETE FROM targets_trash_login_data"
46917            " WHERE target = %llu;",
46918            resource);
46919 
46920       /* Update the target in any trashcan tasks. */
46921       sql ("UPDATE tasks"
46922            " SET target = %llu,"
46923            "     target_location = " G_STRINGIFY (LOCATION_TABLE)
46924            " WHERE target = %llu"
46925            " AND target_location = " G_STRINGIFY (LOCATION_TRASH),
46926            restored_target,
46927            resource);
46928 
46929       permissions_set_locations ("target", resource,
46930                                  sql_last_insert_id (),
46931                                  LOCATION_TABLE);
46932       tags_set_locations ("target", resource,
46933                           sql_last_insert_id (),
46934                           LOCATION_TABLE);
46935 
46936       sql ("DELETE FROM targets_trash WHERE id = %llu;", resource);
46937       sql_commit ();
46938       return 0;
46939     }
46940 
46941   /* Task. */
46942 
46943   if (find_trash_task (id, &resource))
46944     {
46945       sql_rollback ();
46946       return -1;
46947     }
46948 
46949   if (resource)
46950     {
46951       /* Check if it's in use by a resource in the trashcan. */
46952       if (sql_int ("SELECT (target_location = " G_STRINGIFY (LOCATION_TRASH) ")"
46953                    " OR (config_location = " G_STRINGIFY (LOCATION_TRASH) ")"
46954                    " OR (schedule_location = " G_STRINGIFY (LOCATION_TRASH) ")"
46955                    " OR (scanner_location = " G_STRINGIFY (LOCATION_TRASH) ")"
46956                    " OR (SELECT count(*) > 0 FROM task_alerts"
46957                    "     WHERE task = tasks.id"
46958                    "     AND alert_location = " G_STRINGIFY (LOCATION_TRASH) ")"
46959                    " FROM tasks WHERE id = %llu;",
46960                    resource))
46961         {
46962           sql_rollback ();
46963           return 1;
46964         }
46965 
46966       permissions_set_locations ("task", resource, resource, LOCATION_TABLE);
46967       tags_set_locations ("task", resource, resource, LOCATION_TABLE);
46968       sql ("UPDATE tag_resources SET resource_location = "
46969            G_STRINGIFY (LOCATION_TABLE)
46970            " WHERE resource_type = 'report'"
46971            " AND resource IN (SELECT id FROM reports"
46972            "                  WHERE reports.task = %llu);",
46973            resource);
46974       sql ("UPDATE tag_resources SET resource_location = "
46975            G_STRINGIFY (LOCATION_TABLE)
46976            " WHERE resource_type = 'result'"
46977            " AND resource IN (SELECT results.id FROM results"
46978            "                  WHERE results.task = %llu);",
46979            resource);
46980 
46981       sql ("UPDATE tag_resources_trash SET resource_location = "
46982            G_STRINGIFY (LOCATION_TABLE)
46983            " WHERE resource_type = 'report'"
46984            " AND resource IN (SELECT id FROM reports"
46985            "                  WHERE reports.task = %llu);",
46986            resource);
46987       sql ("UPDATE tag_resources_trash SET resource_location = "
46988            G_STRINGIFY (LOCATION_TABLE)
46989            " WHERE resource_type = 'result'"
46990            " AND resource IN (SELECT results.id FROM results"
46991            "                  WHERE results.task = %llu);",
46992            resource);
46993 
46994       sql ("INSERT INTO results"
46995            " (uuid, task, host, port, nvt, result_nvt, type, description,"
46996            "  report, nvt_version, severity, qod, qod_type, owner, date,"
46997            "  hostname, path)"
46998            " SELECT uuid, task, host, port, nvt, result_nvt, type,"
46999            "        description, report, nvt_version, severity, qod,"
47000            "         qod_type, owner, date, hostname, path"
47001            " FROM results_trash"
47002            " WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
47003            resource);
47004 
47005       tickets_restore_task (resource);
47006 
47007       sql ("DELETE FROM results_trash"
47008            " WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
47009            resource);
47010 
47011       /* For safety, in case anything recounted this report while it was in
47012        * the trash (which would have resulted in the wrong counts, because
47013        * the counting does not know about results_trash). */
47014       sql ("DELETE FROM report_counts"
47015            " WHERE report IN (SELECT id FROM reports WHERE task = %llu);",
47016            resource);
47017 
47018       sql ("UPDATE tasks SET hidden = 0 WHERE id = %llu;", resource);
47019 
47020       cache_permissions_for_resource ("task", resource, NULL);
47021 
47022       sql_commit ();
47023       return 0;
47024     }
47025 
47026   sql_rollback ();
47027   return 2;
47028 }
47029 
47030 /**
47031  * @brief Owner SQL for manage_empty_trash.
47032  */
47033 #define WHERE_OWNER                                          \
47034  " WHERE owner = (SELECT id FROM users WHERE uuid = '%s')",  \
47035  current_credentials.uuid
47036 
47037 /**
47038  * @brief Empty the trashcan.
47039  *
47040  * @return 0 success, 99 permission denied, -1 error.
47041  */
47042 int
manage_empty_trashcan()47043 manage_empty_trashcan ()
47044 {
47045   sql_begin_immediate ();
47046 
47047   if (acl_user_may ("empty_trashcan") == 0)
47048     {
47049       sql_rollback ();
47050       return 99;
47051     }
47052 
47053   sql ("DELETE FROM nvt_selectors"
47054        " WHERE name != '" MANAGE_NVT_SELECTOR_UUID_ALL "'"
47055        " AND name IN (SELECT nvt_selector FROM configs_trash"
47056        "              WHERE owner = (SELECT id FROM users"
47057        "                             WHERE uuid = '%s'));",
47058        current_credentials.uuid);
47059   sql ("DELETE FROM config_preferences_trash"
47060        " WHERE config IN (SELECT id FROM configs_trash"
47061        "                  WHERE owner = (SELECT id FROM users"
47062        "                                 WHERE uuid = '%s'));",
47063        current_credentials.uuid);
47064   sql ("DELETE FROM configs_trash" WHERE_OWNER);
47065   empty_trashcan_tickets ();
47066   sql ("DELETE FROM permissions"
47067        " WHERE subject_type = 'group'"
47068        " AND subject IN (SELECT id from groups_trash"
47069        "                 WHERE owner = (SELECT id FROM users"
47070        "                                WHERE uuid = '%s'))"
47071        " AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
47072        current_credentials.uuid);
47073   sql ("DELETE FROM group_users_trash"
47074        " WHERE \"group\" IN (SELECT id from groups_trash"
47075        "                     WHERE owner = (SELECT id FROM users"
47076        "                                    WHERE uuid = '%s'));",
47077        current_credentials.uuid);
47078   sql ("DELETE FROM groups_trash" WHERE_OWNER);
47079   sql ("DELETE FROM alert_condition_data_trash"
47080        " WHERE alert IN (SELECT id from alerts_trash"
47081        "                 WHERE owner = (SELECT id FROM users"
47082        "                                WHERE uuid = '%s'));",
47083        current_credentials.uuid);
47084   sql ("DELETE FROM alert_event_data_trash"
47085        " WHERE alert IN (SELECT id from alerts_trash"
47086        "                 WHERE owner = (SELECT id FROM users"
47087        "                                WHERE uuid = '%s'));",
47088        current_credentials.uuid);
47089   sql ("DELETE FROM alert_method_data_trash"
47090        " WHERE alert IN (SELECT id from alerts_trash"
47091        "                 WHERE owner = (SELECT id FROM users"
47092        "                                WHERE uuid = '%s'));",
47093        current_credentials.uuid);
47094   sql ("DELETE FROM alerts_trash" WHERE_OWNER);
47095   sql ("DELETE FROM credentials_trash_data"
47096        " WHERE credential IN (SELECT id from credentials_trash"
47097        "                      WHERE owner = (SELECT id FROM users"
47098        "                                     WHERE uuid = '%s'));",
47099        current_credentials.uuid);
47100   sql ("DELETE FROM credentials_trash" WHERE_OWNER);
47101   sql ("DELETE FROM filters_trash" WHERE_OWNER);
47102   sql ("DELETE FROM notes_trash" WHERE_OWNER);
47103   sql ("DELETE FROM overrides_trash" WHERE_OWNER);
47104   sql ("DELETE FROM permissions_trash" WHERE_OWNER);
47105   empty_trashcan_port_lists ();
47106   sql ("DELETE FROM permissions"
47107        " WHERE subject_type = 'role'"
47108        " AND subject IN (SELECT id from roles_trash"
47109        "                 WHERE owner = (SELECT id FROM users"
47110        "                                WHERE uuid = '%s'))"
47111        " AND subject_location = " G_STRINGIFY (LOCATION_TRASH) ";",
47112        current_credentials.uuid);
47113   sql ("DELETE FROM role_users_trash"
47114        " WHERE role IN (SELECT id from roles_trash"
47115        "                WHERE owner = (SELECT id FROM users"
47116        "                               WHERE uuid = '%s'));",
47117        current_credentials.uuid);
47118   sql ("DELETE FROM roles_trash" WHERE_OWNER);
47119   sql ("DELETE FROM scanners_trash" WHERE_OWNER);
47120   sql ("DELETE FROM schedules_trash" WHERE_OWNER);
47121   // Remove resource data of all trash tags of the current user.
47122   sql ("DELETE FROM tag_resources_trash"
47123        " WHERE tag IN (SELECT id FROM tags_trash"
47124        "                WHERE owner = (SELECT id FROM users"
47125        "                               WHERE uuid = '%s'));",
47126        current_credentials.uuid);
47127   sql ("DELETE FROM tags_trash" WHERE_OWNER);
47128   sql ("DELETE FROM targets_trash_login_data"
47129        " WHERE target IN (SELECT id from targets_trash"
47130        "                  WHERE owner = (SELECT id FROM users"
47131        "                                 WHERE uuid = '%s'));",
47132        current_credentials.uuid);
47133   sql ("DELETE FROM targets_trash" WHERE_OWNER);
47134   if (delete_trash_tasks ())
47135     {
47136       sql_rollback ();
47137       return -1;
47138     }
47139 
47140   sql ("UPDATE permissions"
47141        " SET resource = -1"
47142        " WHERE resource > 0"
47143        " AND resource_location = " G_STRINGIFY (LOCATION_TRASH)
47144        " AND owner = (SELECT id FROM users WHERE uuid = '%s');",
47145        current_credentials.uuid);
47146   // Remove all trash resources from all tags of the current user.
47147   sql ("DELETE FROM tag_resources"
47148        " WHERE tag IN (SELECT id FROM tags_trash"
47149        "                WHERE owner = (SELECT id FROM users"
47150        "                               WHERE uuid = '%s'))"
47151        "   AND resource_location = " G_STRINGIFY (LOCATION_TRASH) ";",
47152        current_credentials.uuid);
47153 
47154   /* Remove report formats last, because dir deletion can't be rolled back. */
47155   if (empty_trashcan_report_formats ())
47156     {
47157       sql_rollback ();
47158       return -1;
47159     }
47160 
47161   sql_commit ();
47162   return 0;
47163 }
47164 
47165 
47166 /* Assets. */
47167 
47168 /**
47169  * \page asset_rules Ruleset for updating assets from scan detections
47170  *
47171  * During a scan various assets are identfied. The findings are by default
47172  * used to update the asset database. Since assets may already be present in
47173  * the database or even be present with contradictive properties, a ruleset
47174  * defines how the asset database is updated upon findings.
47175  *
47176  * Hosts
47177  * -----
47178  *
47179  * When a host is detected, and there is at least one asset host that has the
47180  * same name and owner as the detected host, and whose identifiers all have
47181  * the same values as the identifiers of the detected host, then the most
47182  * recent such asset host is used. Otherwise a new asset host is created.
47183  * Either way the identifiers are added to the asset host. It does not matter
47184  * if the asset host has fewer identifiers than detected, as long as the
47185  * existing identifiers match.
47186  *
47187  * At the beginning of a scan, when a host is first detected, the decision
47188  * about which asset host to use is made by \ref host_notice.  At the end
47189  * of the scan, if the host has identifiers, then this decision is revised
47190  * by \ref hosts_set_identifiers to take the identifiers into account.
47191  *
47192  * Host identifiers can be ip, hostname, MAC, OS or ssh-key.
47193  *
47194  * This documentation includes some pseudo-code and tabular definition.
47195  * Eventually one of them will repalce the other.
47196  *
47197  * Name    : The assigned name (usually the IP)
47198  * IP      : The detected IP
47199  * Hostname: The detected Hostname
47200  * OS:     : The detected OS
47201  *
47202  * If IP And Not Hostname:
47203  *   If Not Assets.Host(id=Name) And Not Assets.Host(attrib=IP, IP):
47204  *     Assets.Host.New(id=Name, ip=IP)
47205  *   If Assets.Host(id=Name) == 1:
47206  *     Assets.Host.Add(id=Name, ip=IP)
47207  *
47208  * This pseudo-code is equivalent to the first two rows of:
47209  *
47210  * Detection                     | Asset State                                                                 |     Asset Update
47211  * ----------------------------- | --------------------------------------------------------------------------- | -----------------------------
47212  * IP address X.                 | No host with Name=X or any ip=X.                                            | Create host with Name=X and ip=X.
47213  * IP address X.                 | Host A with Name=X.                                                         | Add ip=X to host A.
47214  * 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)).
47215  * IP address X with Hostname Y. | Host A with Name=X and ip=X.                                                | Add ip=X and hostname=Y to host A.
47216  * 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.
47217  * 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.
47218  * 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)).
47219  *
47220  * Follow up action: If a MAC, OS or ssh-key was detected, then the respective
47221  * identifiers are added to the asset host selected during asset update.
47222  *
47223  * Operating Systems
47224  * -----------------
47225  *
47226  * If OS:
47227  *   If Not Assets.OS(id=OS):
47228  *     Assets.OS.New(id=OS)
47229  *
47230  * This pseudo-code is equivalent to:
47231  *
47232  * Detection | Asset State        | Asset Update
47233  * --------- | ------------------ | ------------------------
47234  * OS X.     | No OS with Name=X. | Create OS with Name=X.
47235  * OS X.     | OS with Name=X.    | No action.
47236  */
47237 
47238 /**
47239  * @brief Return the UUID of the asset associated with a result host.
47240  *
47241  * @param[in]  host    Host value from result.
47242  * @param[in]  result  Result.
47243  *
47244  * @return Asset UUID.
47245  */
47246 char *
result_host_asset_id(const char * host,result_t result)47247 result_host_asset_id (const char *host, result_t result)
47248 {
47249   gchar *quoted_host;
47250   char *asset_id;
47251 
47252   quoted_host = sql_quote (host);
47253   asset_id = sql_string ("SELECT uuid FROM hosts"
47254                          " WHERE id = (SELECT host FROM host_identifiers"
47255                          "             WHERE source_type = 'Report Host'"
47256                          "             AND name = 'ip'"
47257                          "             AND source_id"
47258                          "                 = (SELECT uuid"
47259                          "                    FROM reports"
47260                          "                    WHERE id = (SELECT report"
47261                          "                                FROM results"
47262                          "                                WHERE id = %llu))"
47263                          "             AND value = '%s'"
47264                          "             LIMIT 1);",
47265                          result,
47266                          quoted_host);
47267   g_free (quoted_host);
47268   return asset_id;
47269 }
47270 
47271 /**
47272  * @brief Return the UUID of a host.
47273  *
47274  * @param[in]  host  Host.
47275  *
47276  * @return Host UUID.
47277  */
47278 char*
host_uuid(resource_t host)47279 host_uuid (resource_t host)
47280 {
47281   return sql_string ("SELECT uuid FROM hosts WHERE id = %llu;",
47282                      host);
47283 }
47284 
47285 /**
47286  * @brief Add a report host.
47287  *
47288  * @param[in]  report   UUID of resource.
47289  * @param[in]  host     Host.
47290  * @param[in]  start    Start time.
47291  * @param[in]  end      End time.
47292  *
47293  * @return Report host.
47294  */
47295 report_host_t
manage_report_host_add(report_t report,const char * host,time_t start,time_t end)47296 manage_report_host_add (report_t report, const char *host, time_t start,
47297                         time_t end)
47298 {
47299   char *quoted_host = sql_quote (host);
47300 
47301   sql ("INSERT INTO report_hosts"
47302        " (report, host, start_time, end_time, current_port, max_port)"
47303        " SELECT %llu, '%s', %lld, %lld, 0, 0"
47304        " WHERE NOT EXISTS (SELECT 1 FROM report_hosts WHERE report = %llu"
47305        "                   AND host = '%s');",
47306        report, quoted_host, (long long) start, (long long) end, report,
47307        quoted_host);
47308   g_free (quoted_host);
47309   return sql_last_insert_id ();
47310 }
47311 
47312 /**
47313  * @brief Tests if a report host is marked as dead.
47314  *
47315  * @param[in]  report_host  Report host.
47316  *
47317  * @return 1 if the host is marked as dead, 0 otherwise.
47318  */
47319 static int
report_host_dead(report_host_t report_host)47320 report_host_dead (report_host_t report_host)
47321 {
47322   return sql_int ("SELECT count(*) != 0 FROM report_host_details"
47323                   " WHERE report_host = %llu"
47324                   "   AND name = 'Host dead'"
47325                   "   AND value != '0';",
47326                   report_host);
47327 }
47328 
47329 /**
47330  * @brief Counts.
47331  *
47332  * @param[in]  report_host  Report host.
47333  *
47334  * @return 1 if the host is marked as dead, 0 otherwise.
47335  */
47336 static int
report_host_result_count(report_host_t report_host)47337 report_host_result_count (report_host_t report_host)
47338 {
47339   return sql_int ("SELECT count(*) FROM report_hosts, results"
47340                   " WHERE report_hosts.id = %llu"
47341                   "   AND results.report = report_hosts.report"
47342                   "   AND report_hosts.host = results.host;",
47343                   report_host);
47344 }
47345 
47346 /**
47347  * @brief Set end time of a report host.
47348  *
47349  * @param[in]  report_host  Report host.
47350  * @param[in]  end_time     End time.
47351  */
47352 void
report_host_set_end_time(report_host_t report_host,time_t end_time)47353 report_host_set_end_time (report_host_t report_host, time_t end_time)
47354 {
47355   sql ("UPDATE report_hosts SET end_time = %lld WHERE id = %llu;",
47356        end_time, report_host);
47357 }
47358 
47359 /**
47360  * @brief Host identifiers for the current scan.
47361  */
47362 array_t *identifiers = NULL;
47363 
47364 /**
47365  * @brief Unique hosts listed in host_identifiers.
47366  */
47367 static array_t *identifier_hosts = NULL;
47368 
47369 /**
47370  * @brief Host identifier type.
47371  */
47372 typedef struct
47373 {
47374   gchar *ip;                ///< IP of host.
47375   gchar *name;              ///< Name of identifier, like "hostname".
47376   gchar *value;             ///< Value of identifier.
47377   gchar *source_type;       ///< Type of identifier source, like "Report Host".
47378   gchar *source_id;         ///< ID of source.
47379   gchar *source_data;       ///< Extra data for source.
47380 } identifier_t;
47381 
47382 /**
47383  * @brief Free an identifier.
47384  *
47385  * @param[in]  identifier  Identifier.
47386  */
47387 static void
identifier_free(identifier_t * identifier)47388 identifier_free (identifier_t *identifier)
47389 {
47390   if (identifier)
47391     {
47392       g_free (identifier->ip);
47393       g_free (identifier->name);
47394       g_free (identifier->value);
47395       g_free (identifier->source_type);
47396       g_free (identifier->source_id);
47397       g_free (identifier->source_data);
47398     }
47399 }
47400 
47401 /**
47402  * @brief Setup hosts and their identifiers after a scan, from host details.
47403  *
47404  * At the end of a scan this revises the decision about which asset host to use
47405  * for each host that has identifiers.  The rules for this decision are described
47406  * in \ref asset_rules.  (The initial decision is made by \ref host_notice.)
47407  *
47408  * @param[in]  report  Report that the identifiers come from.
47409  */
47410 void
hosts_set_identifiers(report_t report)47411 hosts_set_identifiers (report_t report)
47412 {
47413   if (identifier_hosts)
47414     {
47415       int host_index, index;
47416       gchar *ip;
47417 
47418       array_terminate (identifiers);
47419       array_terminate (identifier_hosts);
47420 
47421       host_index = 0;
47422       while ((ip = (gchar*) g_ptr_array_index (identifier_hosts, host_index)))
47423         {
47424           host_t host, host_new;
47425           gchar *quoted_host_name;
47426           identifier_t *identifier;
47427           GString *select;
47428 
47429           if (report_host_noticeable (report, ip) == 0)
47430             continue;
47431 
47432           quoted_host_name = sql_quote (ip);
47433 
47434           select = g_string_new ("");
47435 
47436           /* Select the most recent host whose identifiers all match the given
47437            * identifiers, even if the host has fewer identifiers than given. */
47438 
47439           g_string_append_printf (select,
47440                                   "SELECT id FROM hosts"
47441                                   " WHERE name = '%s'"
47442                                   " AND owner = (SELECT id FROM users"
47443                                   "              WHERE uuid = '%s')",
47444                                   quoted_host_name,
47445                                   current_credentials.uuid);
47446 
47447           index = 0;
47448           while ((identifier = (identifier_t*) g_ptr_array_index (identifiers, index)))
47449             {
47450               gchar *quoted_identifier_name, *quoted_identifier_value;
47451 
47452               if (strcmp (identifier->ip, ip))
47453                 {
47454                   index++;
47455                   continue;
47456                 }
47457 
47458               quoted_identifier_name = sql_quote (identifier->name);
47459               quoted_identifier_value = sql_quote (identifier->value);
47460 
47461               g_string_append_printf (select,
47462                                       " AND (EXISTS (SELECT * FROM host_identifiers"
47463                                       "              WHERE host = hosts.id"
47464                                       "              AND owner = (SELECT id FROM users"
47465                                       "                           WHERE uuid = '%s')"
47466                                       "              AND name = '%s'"
47467                                       "              AND value = '%s')"
47468                                       "      OR NOT EXISTS (SELECT * FROM host_identifiers"
47469                                       "                     WHERE host = hosts.id"
47470                                       "                     AND owner = (SELECT id FROM users"
47471                                       "                                  WHERE uuid = '%s')"
47472                                       "                     AND name = '%s'))",
47473                                       current_credentials.uuid,
47474                                       quoted_identifier_name,
47475                                       quoted_identifier_value,
47476                                       current_credentials.uuid,
47477                                       quoted_identifier_name);
47478 
47479               g_free (quoted_identifier_name);
47480               g_free (quoted_identifier_value);
47481 
47482               index++;
47483             }
47484 
47485           g_string_append_printf (select,
47486                                   " ORDER BY creation_time DESC LIMIT 1;");
47487 
47488           switch (sql_int64 (&host, select->str))
47489             {
47490               case 0:
47491                 break;
47492               case 1:        /* Too few rows in result of query. */
47493                 host = 0;
47494                 break;
47495               default:       /* Programming error. */
47496                 assert (0);
47497               case -1:
47498                 host = 0;
47499                 break;
47500             }
47501 
47502           g_string_free (select, TRUE);
47503 
47504           if (host == 0)
47505             {
47506               /* Add the host. */
47507 
47508               sql ("INSERT into hosts"
47509                    " (uuid, owner, name, comment, creation_time, modification_time)"
47510                    " VALUES"
47511                    " (make_uuid (), (SELECT id FROM users WHERE uuid = '%s'), '%s', '',"
47512                    "  m_now (), m_now ());",
47513                    current_credentials.uuid,
47514                    quoted_host_name);
47515 
47516               host_new = host = sql_last_insert_id ();
47517 
47518               /* Make sure the Report Host identifiers added when the host was
47519                * first noticed now refer to the new host. */
47520 
47521               sql ("UPDATE host_identifiers SET host = %llu"
47522                    " WHERE source_id = (SELECT uuid FROM reports"
47523                    "                    WHERE id = %llu)"
47524                    " AND name = 'ip'"
47525                    " AND value = '%s';",
47526                    host_new,
47527                    report,
47528                    quoted_host_name);
47529             }
47530           else
47531             {
47532               /* Use the existing host. */
47533 
47534               host_new = 0;
47535             }
47536 
47537           /* Add the host identifiers. */
47538 
47539           index = 0;
47540           while ((identifier = (identifier_t*) g_ptr_array_index (identifiers,
47541                                                                   index)))
47542             {
47543               gchar *quoted_identifier_name, *quoted_identifier_value;
47544               gchar *quoted_source_id, *quoted_source_type, *quoted_source_data;
47545 
47546               if (strcmp (identifier->ip, ip))
47547                 {
47548                   index++;
47549                   continue;
47550                 }
47551 
47552               quoted_identifier_name = sql_quote (identifier->name);
47553               quoted_identifier_value = sql_quote (identifier->value);
47554               quoted_source_id = sql_quote (identifier->source_id);
47555               quoted_source_data = sql_quote (identifier->source_data);
47556               quoted_source_type = sql_quote (identifier->source_type);
47557 
47558               if (strcmp (identifier->name, "OS") == 0)
47559                 {
47560                   resource_t os;
47561 
47562                   switch (sql_int64 (&os,
47563                                      "SELECT id FROM oss"
47564                                      " WHERE name = '%s'"
47565                                      " AND owner = (SELECT id FROM users"
47566                                      "              WHERE uuid = '%s');",
47567                                      quoted_identifier_value,
47568                                      current_credentials.uuid))
47569                     {
47570                       case 0:
47571                         break;
47572                       default:       /* Programming error. */
47573                         assert (0);
47574                       case -1:
47575                       case 1:        /* Too few rows in result of query. */
47576                         sql ("INSERT into oss"
47577                              " (uuid, owner, name, comment, creation_time,"
47578                              "  modification_time)"
47579                              " VALUES"
47580                              " (make_uuid (),"
47581                              "  (SELECT id FROM users WHERE uuid = '%s'),"
47582                              "  '%s', '', m_now (), m_now ());",
47583                              current_credentials.uuid,
47584                              quoted_identifier_value);
47585                         os = sql_last_insert_id ();
47586                         break;
47587                     }
47588 
47589                   sql ("INSERT into host_oss"
47590                        " (uuid, host, owner, name, comment, os, source_type,"
47591                        "  source_id, source_data, creation_time, modification_time)"
47592                        " VALUES"
47593                        " (make_uuid (), %llu,"
47594                        "  (SELECT id FROM users WHERE uuid = '%s'),"
47595                        "  '%s', '', %llu, '%s', '%s', '%s', m_now (), m_now ());",
47596                        host,
47597                        current_credentials.uuid,
47598                        quoted_identifier_name,
47599                        os,
47600                        quoted_source_type,
47601                        quoted_source_id,
47602                        quoted_source_data);
47603 
47604                   if (host_new == 0)
47605                     {
47606                       sql ("UPDATE hosts"
47607                            " SET modification_time = (SELECT modification_time"
47608                            "                          FROM host_oss"
47609                            "                          WHERE id = %llu)"
47610                            " WHERE id = %llu;",
47611                            sql_last_insert_id (),
47612                            host);
47613 
47614                       sql ("UPDATE oss"
47615                            " SET modification_time = (SELECT modification_time"
47616                            "                          FROM host_oss"
47617                            "                          WHERE id = %llu)"
47618                            " WHERE id = %llu;",
47619                            sql_last_insert_id (),
47620                            os);
47621                     }
47622                 }
47623               else
47624                 {
47625                   sql ("INSERT into host_identifiers"
47626                        " (uuid, host, owner, name, comment, value, source_type,"
47627                        "  source_id, source_data, creation_time, modification_time)"
47628                        " VALUES"
47629                        " (make_uuid (), %llu,"
47630                        "  (SELECT id FROM users WHERE uuid = '%s'),"
47631                        "  '%s', '', '%s', '%s', '%s', '%s', m_now (), m_now ());",
47632                        host,
47633                        current_credentials.uuid,
47634                        quoted_identifier_name,
47635                        quoted_identifier_value,
47636                        quoted_source_type,
47637                        quoted_source_id,
47638                        quoted_source_data);
47639 
47640                   if (host_new == 0)
47641                     sql ("UPDATE hosts"
47642                          " SET modification_time = (SELECT modification_time"
47643                          "                          FROM host_identifiers"
47644                          "                          WHERE id = %llu)"
47645                          " WHERE id = %llu;",
47646                          sql_last_insert_id (),
47647                          host);
47648                 }
47649 
47650               g_free (quoted_source_type);
47651               g_free (quoted_source_id);
47652               g_free (quoted_source_data);
47653               g_free (quoted_identifier_name);
47654               g_free (quoted_identifier_value);
47655 
47656               index++;
47657             }
47658 
47659           g_free (quoted_host_name);
47660           host_index++;
47661         }
47662 
47663       index = 0;
47664       while (identifiers && (index < identifiers->len))
47665         identifier_free (g_ptr_array_index (identifiers, index++));
47666       array_free (identifiers);
47667       identifiers = NULL;
47668 
47669       array_free (identifier_hosts);
47670       identifier_hosts = NULL;
47671     }
47672 }
47673 
47674 /**
47675  * @brief Set the maximum severity of each host in a scan.
47676  *
47677  * @param[in]  report         The report associated with the scan.
47678  * @param[in]  overrides_arg  Whether override should be applied.
47679  * @param[in]  min_qod_arg    Min QOD to use.
47680  */
47681 void
hosts_set_max_severity(report_t report,int * overrides_arg,int * min_qod_arg)47682 hosts_set_max_severity (report_t report, int *overrides_arg, int *min_qod_arg)
47683 {
47684   gchar *new_severity_sql;
47685   int dynamic_severity, overrides, min_qod;
47686 
47687   if (overrides_arg)
47688     overrides = *overrides_arg;
47689   else
47690     {
47691       task_t task;
47692       /* Get "Assets Apply Overrides" task preference. */
47693       overrides = 1;
47694       if (report_task (report, &task) == 0)
47695         {
47696           char *value;
47697           value = task_preference_value (task, "assets_apply_overrides");
47698           if (value && (strcmp (value, "yes") == 0))
47699             overrides = 1;
47700           else
47701             overrides = 0;
47702           free (value);
47703         }
47704     }
47705 
47706   if (min_qod_arg)
47707     min_qod = *min_qod_arg;
47708   else
47709     {
47710       task_t task;
47711       /* Get "Assets Min QOD". */
47712       min_qod = MIN_QOD_DEFAULT;
47713       if (report_task (report, &task) == 0)
47714         {
47715           char *value;
47716           value = task_preference_value (task, "assets_min_qod");
47717           if (value)
47718             min_qod = atoi (value);
47719           free (value);
47720         }
47721     }
47722 
47723   dynamic_severity = setting_dynamic_severity_int ();
47724   new_severity_sql = new_severity_clause (overrides, dynamic_severity);
47725 
47726   sql ("INSERT INTO host_max_severities"
47727        " (host, severity, source_type, source_id, creation_time)"
47728        " SELECT asset_host,"
47729        "        coalesce ((SELECT max (%s) FROM results"
47730        "                   WHERE report = %llu"
47731        "                   AND qod >= %i"
47732        "                   AND host = (SELECT name FROM hosts"
47733        "                               WHERE id = asset_host)),"
47734        "                  0.0),"
47735        "        'Report',"
47736        "        (SELECT uuid FROM reports WHERE id = %llu),"
47737        "        m_now ()"
47738        " FROM (SELECT host AS asset_host"
47739        "       FROM host_identifiers"
47740        "       WHERE source_id = (SELECT uuid FROM reports WHERE id = %llu))"
47741        "      AS subquery;",
47742        new_severity_sql,
47743        report,
47744        min_qod,
47745        report,
47746        report);
47747 
47748   g_free (new_severity_sql);
47749 }
47750 
47751 /**
47752  * @brief Store certain host details in the assets after a scan.
47753  *
47754  * @param[in]  report  The report associated with the scan.
47755  */
47756 void
hosts_set_details(report_t report)47757 hosts_set_details (report_t report)
47758 {
47759   sql ("INSERT INTO host_details"
47760        " (detail_source_type, detail_source_name, detail_source_description,"
47761        "  name, value, source_type, source_id, host)"
47762        " SELECT source_type,"
47763        "        source_name,"
47764        "        source_description,"
47765        "        name,"
47766        "        value,"
47767        "        'Report',"
47768        "        (SELECT uuid FROM reports WHERE id = %llu),"
47769        "        (SELECT host"
47770        "         FROM host_identifiers"
47771        "         WHERE source_id = (SELECT uuid FROM reports"
47772        "                            WHERE id = %llu)"
47773        "         AND (SELECT name FROM hosts WHERE id = host)"
47774        "             = (SELECT host FROM report_hosts"
47775        "                WHERE id = report_host_details.report_host)"
47776        "         LIMIT 1)"
47777        " FROM report_host_details"
47778        " WHERE (SELECT report FROM report_hosts"
47779        "        WHERE id = report_host)"
47780        "       = %llu"
47781        /* Only if the task is included in the assets. */
47782        " AND (SELECT value = 'yes' FROM task_preferences"
47783        "      WHERE task = (SELECT task FROM reports WHERE id = %llu)"
47784        "      AND name = 'in_assets')"
47785        /* Ensure that every report host detail has a corresponding host
47786         *  in the assets. */
47787        " AND EXISTS (SELECT *"
47788        "               FROM host_identifiers"
47789        "              WHERE source_id = (SELECT uuid FROM reports"
47790        "                                 WHERE id = %llu)"
47791        "                AND (SELECT name FROM hosts WHERE id = host)"
47792        "                      = (SELECT host FROM report_hosts"
47793        "                         WHERE id = report_host_details.report_host))"
47794        " AND (name IN ('best_os_cpe', 'best_os_txt', 'traceroute'));",
47795        report,
47796        report,
47797        report,
47798        report,
47799        report);
47800 }
47801 
47802 /**
47803  * @brief Get XML of a detailed host route.
47804  *
47805  * @param[in]  host  The host.
47806  *
47807  * @return XML.
47808  */
47809 gchar*
host_routes_xml(host_t host)47810 host_routes_xml (host_t host)
47811 {
47812   iterator_t routes;
47813   GString* buffer;
47814 
47815   gchar *owned_clause, *with_clause;
47816 
47817   owned_clause = acl_where_owned_for_get ("host", NULL, NULL, &with_clause);
47818 
47819   buffer = g_string_new ("<routes>");
47820   init_iterator (&routes,
47821                  "SELECT outer_details.value,"
47822                  "       outer_details.source_type,"
47823                  "       outer_details.source_id,"
47824                  "       outer_identifiers.modification_time"
47825                  "  FROM host_details AS outer_details"
47826                  "  JOIN host_identifiers AS outer_identifiers"
47827                  "    ON outer_identifiers.host = outer_details.host"
47828                  " WHERE outer_details.host = %llu"
47829                  "   AND outer_details.name = 'traceroute'"
47830                  "   AND outer_details.source_id = outer_identifiers.source_id"
47831                  "   AND outer_identifiers.name='ip'"
47832                  "   AND outer_identifiers.modification_time"
47833                  "         = (SELECT max (modification_time)"
47834                  "              FROM host_identifiers"
47835                  "             WHERE host_identifiers.host = %llu"
47836                  "               AND host_identifiers.source_id IN"
47837                  "                   (SELECT source_id FROM host_details"
47838                  "                     WHERE host = %llu"
47839                  "                       AND value = outer_details.value)"
47840                  "              AND host_identifiers.name='ip')"
47841                  " ORDER BY outer_identifiers.modification_time DESC;",
47842                  host, host, host);
47843 
47844   while (next (&routes))
47845     {
47846       const char *traceroute;
47847       const char *source_id;
47848       time_t modified;
47849       gchar **hop_ips, **hop_ip;
47850       int distance;
47851 
47852       g_string_append (buffer, "<route>");
47853 
47854       traceroute = iterator_string (&routes, 0);
47855       source_id = iterator_string (&routes, 2);
47856       modified = iterator_int64 (&routes, 3);
47857 
47858       hop_ips = g_strsplit (traceroute, ",", 0);
47859       hop_ip = hop_ips;
47860 
47861       distance = 0;
47862 
47863       while (*hop_ip != NULL) {
47864         iterator_t best_host_iterator;
47865         const char *best_host_id;
47866         int same_source;
47867 
47868         init_iterator (&best_host_iterator,
47869                        "%s"
47870                        " SELECT hosts.uuid,"
47871                        "       (source_id='%s')"
47872                        "         AS same_source"
47873                        "  FROM hosts, host_identifiers"
47874                        " WHERE hosts.id = host_identifiers.host"
47875                        "   AND host_identifiers.name = 'ip'"
47876                        "   AND host_identifiers.value='%s'"
47877                        "   AND %s"
47878                        " ORDER BY same_source DESC,"
47879                        "          abs(host_identifiers.modification_time"
47880                        "              - %llu) ASC"
47881                        " LIMIT 1;",
47882                        with_clause ? with_clause : "",
47883                        source_id,
47884                        *hop_ip,
47885                        owned_clause,
47886                        modified);
47887 
47888         if (next (&best_host_iterator))
47889           {
47890             best_host_id = iterator_string (&best_host_iterator, 0);
47891             same_source = iterator_int (&best_host_iterator, 1);
47892           }
47893         else
47894           {
47895             best_host_id = NULL;
47896             same_source = 0;
47897           }
47898 
47899         g_string_append_printf (buffer,
47900                                 "<host id=\"%s\""
47901                                 " distance=\"%d\""
47902                                 " same_source=\"%d\">"
47903                                 "<ip>%s</ip>"
47904                                 "</host>",
47905                                 best_host_id ? best_host_id : "",
47906                                 distance,
47907                                 same_source,
47908                                 *hop_ip);
47909 
47910         cleanup_iterator (&best_host_iterator);
47911 
47912         distance++;
47913         hop_ip++;
47914       }
47915 
47916       g_string_append (buffer, "</route>");
47917       g_strfreev(hop_ips);
47918     }
47919 
47920   g_free (with_clause);
47921   g_free (owned_clause);
47922 
47923   cleanup_iterator (&routes);
47924 
47925   g_string_append (buffer, "</routes>");
47926 
47927   return g_string_free (buffer, FALSE);
47928 }
47929 
47930 /**
47931  * @brief Add host details to a report host.
47932  *
47933  * @param[in]  report  UUID of resource.
47934  * @param[in]  ip      Host.
47935  * @param[in]  entity  XML entity containing details.
47936  *
47937  * @return 0 success, -1 failed to parse XML.
47938  */
47939 int
manage_report_host_details(report_t report,const char * ip,entity_t entity)47940 manage_report_host_details (report_t report, const char *ip, entity_t entity)
47941 {
47942   int in_assets;
47943   entities_t details;
47944   entity_t detail;
47945   char *uuid;
47946 
47947   in_assets = sql_int ("SELECT not(value = 'no') FROM task_preferences"
47948                        " WHERE task = (SELECT task FROM reports"
47949                        "                WHERE id = %llu)"
47950                        " AND name = 'in_assets';",
47951                        report);
47952 
47953   details = entity->entities;
47954   if (identifiers == NULL)
47955     identifiers = make_array ();
47956   if (identifier_hosts == NULL)
47957     identifier_hosts = make_array ();
47958   uuid = report_uuid (report);
47959   while ((detail = first_entity (details)))
47960     {
47961       if (strcmp (entity_name (detail), "detail") == 0)
47962         {
47963           entity_t source, source_type, source_name, source_desc, name, value;
47964 
47965           /* Parse host detail and add to report */
47966           source = entity_child (detail, "source");
47967           if (source == NULL)
47968             goto error;
47969           source_type = entity_child (source, "type");
47970           if (source_type == NULL)
47971             goto error;
47972           source_name = entity_child (source, "name");
47973           if (source_name == NULL)
47974             goto error;
47975           source_desc = entity_child (source, "description");
47976           if (source_desc == NULL)
47977             goto error;
47978           name = entity_child (detail, "name");
47979           if (name == NULL)
47980             goto error;
47981           value = entity_child (detail, "value");
47982           if (value == NULL)
47983             goto error;
47984           insert_report_host_detail
47985            (report, ip, entity_text (source_type), entity_text (source_name),
47986             entity_text (source_desc), entity_text (name), entity_text (value));
47987 
47988           /* Only add to assets if "Add to Assets" is set on the task. */
47989           if (in_assets)
47990             {
47991               if (strcmp (entity_text (name), "hostname") == 0)
47992                 {
47993                   identifier_t *identifier;
47994 
47995                   identifier = g_malloc (sizeof (identifier_t));
47996                   identifier->ip = g_strdup (ip);
47997                   identifier->name = g_strdup ("hostname");
47998                   identifier->value = g_strdup (entity_text (value));
47999                   identifier->source_id = g_strdup (uuid);
48000                   identifier->source_type = g_strdup ("Report Host Detail");
48001                   identifier->source_data
48002                     = g_strdup (entity_text (source_name));
48003                   array_add (identifiers, identifier);
48004                   array_add_new_string (identifier_hosts, g_strdup (ip));
48005                 }
48006               if (strcmp (entity_text (name), "MAC") == 0)
48007                 {
48008                   identifier_t *identifier;
48009 
48010                   identifier = g_malloc (sizeof (identifier_t));
48011                   identifier->ip = g_strdup (ip);
48012                   identifier->name = g_strdup ("MAC");
48013                   identifier->value = g_strdup (entity_text (value));
48014                   identifier->source_id = g_strdup (uuid);
48015                   identifier->source_type = g_strdup ("Report Host Detail");
48016                   identifier->source_data
48017                     = g_strdup (entity_text (source_name));
48018                   array_add (identifiers, identifier);
48019                   array_add_new_string (identifier_hosts, g_strdup (ip));
48020                 }
48021               if (strcmp (entity_text (name), "OS") == 0
48022                   && g_str_has_prefix (entity_text (value), "cpe:"))
48023                 {
48024                   identifier_t *identifier;
48025 
48026                   identifier = g_malloc (sizeof (identifier_t));
48027                   identifier->ip = g_strdup (ip);
48028                   identifier->name = g_strdup ("OS");
48029                   identifier->value = g_strdup (entity_text (value));
48030                   identifier->source_id = g_strdup (uuid);
48031                   identifier->source_type = g_strdup ("Report Host Detail");
48032                   identifier->source_data
48033                     = g_strdup (entity_text (source_name));
48034                   array_add (identifiers, identifier);
48035                   array_add_new_string (identifier_hosts, g_strdup (ip));
48036                 }
48037               if (strcmp (entity_text (name), "ssh-key") == 0)
48038                 {
48039                   identifier_t *identifier;
48040 
48041                   identifier = g_malloc (sizeof (identifier_t));
48042                   identifier->ip = g_strdup (ip);
48043                   identifier->name = g_strdup ("ssh-key");
48044                   identifier->value = g_strdup (entity_text (value));
48045                   identifier->source_id = g_strdup (uuid);
48046                   identifier->source_type = g_strdup ("Report Host Detail");
48047                   identifier->source_data
48048                     = g_strdup (entity_text (source_name));
48049                   array_add (identifiers, identifier);
48050                   array_add_new_string (identifier_hosts, g_strdup (ip));
48051                 }
48052             }
48053         }
48054       details = next_entities (details);
48055     }
48056   free (uuid);
48057 
48058   return 0;
48059 
48060  error:
48061   free (uuid);
48062   return -1;
48063 }
48064 
48065 /**
48066  * @brief Add a host detail to a report host.
48067  *
48068  * @param[in]  report  UUID of resource.
48069  * @param[in]  host    Host.
48070  * @param[in]  xml     Report host detail XML.
48071  *
48072  * @return 0 success, -1 failed to parse XML, -2 host was NULL.
48073  */
48074 int
manage_report_host_detail(report_t report,const char * host,const char * xml)48075 manage_report_host_detail (report_t report, const char *host, const char *xml)
48076 {
48077   int ret;
48078   entity_t entity;
48079 
48080   if (host == NULL)
48081     return -2;
48082 
48083   entity = NULL;
48084   if (parse_entity (xml, &entity))
48085     return -1;
48086 
48087   ret = manage_report_host_details (report, host, entity);
48088   free_entity (entity);
48089   return ret;
48090 }
48091 
48092 /**
48093  * @brief Initialise a host identifier iterator.
48094  *
48095  * @param[in]  iterator    Iterator.
48096  * @param[in]  host        Host.
48097  * @param[in]  ascending   Whether to sort ascending or descending.
48098  * @param[in]  sort_field  Field to sort on, or NULL for type then start.
48099  */
48100 void
init_host_identifier_iterator(iterator_t * iterator,host_t host,int ascending,const char * sort_field)48101 init_host_identifier_iterator (iterator_t* iterator, host_t host,
48102                                int ascending, const char* sort_field)
48103 {
48104   assert (current_credentials.uuid);
48105 
48106   if (host)
48107     init_iterator (iterator,
48108                    "SELECT id, uuid, name, comment, iso_time (creation_time),"
48109                    "       iso_time (modification_time), creation_time,"
48110                    "       modification_time, owner, owner, value,"
48111                    "       source_type, source_id, source_data,"
48112                    "       (CASE WHEN source_type LIKE 'Report%%'"
48113                    "        THEN NOT EXISTS (SELECT * FROM reports"
48114                    "                         WHERE uuid = source_id)"
48115                    "        ELSE CAST (0 AS boolean)"
48116                    "        END),"
48117                    "       '', ''"
48118                    " FROM host_identifiers"
48119                    " WHERE host = %llu"
48120                    " UNION"
48121                    " SELECT id, uuid, name, comment, iso_time (creation_time),"
48122                    "        iso_time (modification_time), creation_time,"
48123                    "        modification_time, owner, owner,"
48124                    "        (SELECT name FROM oss WHERE id = os),"
48125                    "        source_type, source_id, source_data,"
48126                    "        (CASE WHEN source_type LIKE 'Report%%'"
48127                    "         THEN NOT EXISTS (SELECT * FROM reports"
48128                    "                          WHERE uuid = source_id)"
48129                    "         ELSE CAST (0 AS boolean)"
48130                    "         END),"
48131                    "        (SELECT uuid FROM oss WHERE id = os),"
48132                    "        cpe_title ((SELECT name FROM oss WHERE id = os))"
48133                    " FROM host_oss"
48134                    " WHERE host = %llu"
48135                    " ORDER BY %s %s;",
48136                    host,
48137                    host,
48138                    sort_field ? sort_field : "creation_time",
48139                    ascending ? "ASC" : "DESC");
48140   else
48141     init_iterator (iterator,
48142                    "SELECT id, uuid, name, comment, iso_time (creation_time),"
48143                    "       iso_time (modification_time), creation_time,"
48144                    "       modification_time, owner, owner, value,"
48145                    "       source_type, source_id, source_data, 0, '', ''"
48146                    " FROM host_identifiers"
48147                    " ORDER BY %s %s;",
48148                    sort_field ? sort_field : "creation_time",
48149                    ascending ? "ASC" : "DESC");
48150 }
48151 
48152 /**
48153  * @brief Get the value from a host identifier iterator.
48154  *
48155  * @param[in]  iterator  Iterator.
48156  *
48157  * @return The value of the host identifier, or NULL if iteration is complete.
48158  *         Freed by cleanup_iterator.
48159  */
48160 DEF_ACCESS (host_identifier_iterator_value, GET_ITERATOR_COLUMN_COUNT);
48161 
48162 /**
48163  * @brief Get the source type from a host identifier iterator.
48164  *
48165  * @param[in]  iterator  Iterator.
48166  *
48167  * @return The source type of the host identifier, or NULL if iteration is
48168  *         complete. Freed by cleanup_iterator.
48169  */
48170 DEF_ACCESS (host_identifier_iterator_source_type,
48171             GET_ITERATOR_COLUMN_COUNT + 1);
48172 
48173 /**
48174  * @brief Get the source from a host identifier iterator.
48175  *
48176  * @param[in]  iterator  Iterator.
48177  *
48178  * @return The source of the host identifier, or NULL if iteration is
48179  *         complete. Freed by cleanup_iterator.
48180  */
48181 DEF_ACCESS (host_identifier_iterator_source_id, GET_ITERATOR_COLUMN_COUNT + 2);
48182 
48183 /**
48184  * @brief Get the source data from a host identifier iterator.
48185  *
48186  * @param[in]  iterator  Iterator.
48187  *
48188  * @return The source data of the host identifier, or NULL if iteration is
48189  *         complete. Freed by cleanup_iterator.
48190  */
48191 DEF_ACCESS (host_identifier_iterator_source_data,
48192             GET_ITERATOR_COLUMN_COUNT + 3);
48193 
48194 /**
48195  * @brief Get the source orphan state from a host identifier iterator.
48196  *
48197  * @param[in]  iterator  Iterator.
48198  *
48199  * @return The source orphan state of the host identifier, or 0 if iteration is
48200  *         complete. Freed by cleanup_iterator.
48201  */
48202 int
host_identifier_iterator_source_orphan(iterator_t * iterator)48203 host_identifier_iterator_source_orphan (iterator_t* iterator)
48204 {
48205   if (iterator->done) return 0;
48206   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
48207 }
48208 
48209 /**
48210  * @brief Get the OS UUID from a host identifier iterator.
48211  *
48212  * @param[in]  iterator  Iterator.
48213  *
48214  * @return The OS UUID of the host identifier, or NULL if iteration is
48215  *         complete. Freed by cleanup_iterator.
48216  */
48217 DEF_ACCESS (host_identifier_iterator_os_id,
48218             GET_ITERATOR_COLUMN_COUNT + 5);
48219 
48220 /**
48221  * @brief Get the OS title from a host identifier iterator.
48222  *
48223  * @param[in]  iterator  Iterator.
48224  *
48225  * @return The OS title of the host identifier, or NULL if iteration is
48226  *         complete. Freed by cleanup_iterator.
48227  */
48228 DEF_ACCESS (host_identifier_iterator_os_title,
48229             GET_ITERATOR_COLUMN_COUNT + 6);
48230 
48231 /**
48232  * @brief Filter columns for host iterator.
48233  */
48234 #define HOST_ITERATOR_FILTER_COLUMNS                                        \
48235  { GET_ITERATOR_FILTER_COLUMNS, "severity", "os", "oss", "hostname", "ip",  \
48236    "severity_level", "updated", NULL }
48237 
48238 /**
48239  * @brief Host iterator columns.
48240  */
48241 #define HOST_ITERATOR_COLUMNS                                         \
48242  {                                                                    \
48243    GET_ITERATOR_COLUMNS (hosts),                                      \
48244    {                                                                  \
48245      "1",                                                             \
48246      "writable",                                                      \
48247      KEYWORD_TYPE_INTEGER                                             \
48248    },                                                                 \
48249    {                                                                  \
48250      "0",                                                             \
48251      "in_use",                                                        \
48252      KEYWORD_TYPE_INTEGER                                             \
48253    },                                                                 \
48254    {                                                                  \
48255      "(SELECT round (CAST (severity AS numeric), 1)"                  \
48256      " FROM host_max_severities"                                      \
48257      " WHERE host = hosts.id"                                         \
48258      " ORDER by creation_time DESC"                                   \
48259      " LIMIT 1)",                                                     \
48260      "severity",                                                      \
48261      KEYWORD_TYPE_DOUBLE                                              \
48262    },                                                                 \
48263    {                                                                  \
48264      "(SELECT CASE"                                                   \
48265      "        WHEN best_os_text LIKE '%[possible conflict]%'"         \
48266      "        THEN best_os_text"                                      \
48267      "        WHEN best_os_cpe IS NULL"                               \
48268      "        THEN '[unknown]'"                                       \
48269      "        ELSE best_os_cpe"                                       \
48270      "        END"                                                    \
48271      " FROM (SELECT (SELECT value"                                    \
48272      "               FROM (SELECT max (id) AS id"                     \
48273      "                     FROM host_details"                         \
48274      "                     WHERE host = hosts.id"                     \
48275      "                     AND name = 'best_os_cpe')"                 \
48276      "                     AS sub,"                                   \
48277      "                    host_details"                               \
48278      "               WHERE sub.id = host_details.id)"                 \
48279      "              AS best_os_cpe,"                                  \
48280      "              (SELECT value"                                    \
48281      "               FROM (SELECT max (id) AS id"                     \
48282      "                     FROM host_details"                         \
48283      "                     WHERE host = hosts.id"                     \
48284      "                     AND name = 'best_os_text')"                \
48285      "                     AS sub,"                                   \
48286      "                    host_details"                               \
48287      "               WHERE sub.id = host_details.id)"                 \
48288      "              AS best_os_text)"                                 \
48289      "      AS vars)",                                                \
48290      "os",                                                            \
48291      KEYWORD_TYPE_STRING                                              \
48292    },                                                                 \
48293    {                                                                  \
48294      "(SELECT group_concat (name, ', ') FROM oss"                     \
48295      "  WHERE id IN (SELECT distinct os FROM host_oss"                \
48296      "               WHERE host = hosts.id))",                        \
48297      "oss",                                                           \
48298      KEYWORD_TYPE_INTEGER                                             \
48299    },                                                                 \
48300    {                                                                  \
48301      "(SELECT value"                                                  \
48302      " FROM host_identifiers"                                         \
48303      " WHERE host = hosts.id"                                         \
48304      " AND name = 'hostname'"                                         \
48305      " ORDER by creation_time DESC"                                   \
48306      " LIMIT 1)",                                                     \
48307      "hostname",                                                      \
48308      KEYWORD_TYPE_STRING                                              \
48309    },                                                                 \
48310    {                                                                  \
48311      "(SELECT value"                                                  \
48312      " FROM host_identifiers"                                         \
48313      " WHERE host = hosts.id"                                         \
48314      " AND name = 'ip'"                                               \
48315      " ORDER by creation_time DESC"                                   \
48316      " LIMIT 1)",                                                     \
48317      "ip",                                                            \
48318      KEYWORD_TYPE_STRING                                              \
48319    },                                                                 \
48320    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                               \
48321  }
48322 
48323 /**
48324  * @brief Host iterator WHERE columns.
48325  */
48326 #define HOST_ITERATOR_WHERE_COLUMNS                                   \
48327  {                                                                    \
48328    {                                                                  \
48329      "(SELECT severity_to_level (CAST (severity AS numeric), 0)"      \
48330      " FROM host_max_severities"                                      \
48331      " WHERE host = hosts.id"                                         \
48332      " ORDER by creation_time DESC"                                   \
48333      " LIMIT 1)",                                                     \
48334      "severity_level",                                                \
48335      KEYWORD_TYPE_STRING                                              \
48336    },                                                                 \
48337    {                                                                  \
48338      "modification_time", "updated", KEYWORD_TYPE_INTEGER             \
48339    },                                                                 \
48340    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                               \
48341  }
48342 
48343 /**
48344  * @brief Initialise a host iterator.
48345  *
48346  * @param[in]  iterator    Iterator.
48347  * @param[in]  get         GET data.
48348  *
48349  * @return 0 success, 1 failed to find host, 2 failed to find filter,
48350  *         -1 error.
48351  */
48352 int
init_asset_host_iterator(iterator_t * iterator,const get_data_t * get)48353 init_asset_host_iterator (iterator_t *iterator, const get_data_t *get)
48354 {
48355   static const char *filter_columns[] = HOST_ITERATOR_FILTER_COLUMNS;
48356   static column_t columns[] = HOST_ITERATOR_COLUMNS;
48357   static column_t where_columns[] = HOST_ITERATOR_WHERE_COLUMNS;
48358 
48359   return init_get_iterator2 (iterator,
48360                              "host",
48361                              get,
48362                              /* Columns. */
48363                              columns,
48364                              /* Columns for trashcan. */
48365                              NULL,
48366                              /* WHERE Columns. */
48367                              where_columns,
48368                              /* WHERE Columns for trashcan. */
48369                              NULL,
48370                              filter_columns,
48371                              0,
48372                              NULL,
48373                              NULL,
48374                              NULL,
48375                              TRUE,
48376                              FALSE,
48377                              NULL);
48378 }
48379 
48380 /**
48381  * @brief Get the writable status from an asset iterator.
48382  *
48383  * @param[in]  iterator  Iterator.
48384  *
48385  * @return 1 if writable, else 0.
48386  */
48387 int
asset_iterator_writable(iterator_t * iterator)48388 asset_iterator_writable (iterator_t* iterator)
48389 {
48390   if (iterator->done) return 0;
48391   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT);
48392 }
48393 
48394 /**
48395  * @brief Get the "in use" status from an asset iterator.
48396  *
48397  * @param[in]  iterator  Iterator.
48398  *
48399  * @return 1 if in use, else 0.
48400  */
48401 int
asset_iterator_in_use(iterator_t * iterator)48402 asset_iterator_in_use (iterator_t* iterator)
48403 {
48404   if (iterator->done) return 0;
48405   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
48406 }
48407 
48408 /**
48409  * @brief Get the max severity from an asset host iterator.
48410  *
48411  * @param[in]  iterator  Iterator.
48412  *
48413  * @return The maximum severity of the host, or NULL if iteration is
48414  *         complete. Freed by cleanup_iterator.
48415  */
48416 DEF_ACCESS (asset_host_iterator_severity, GET_ITERATOR_COLUMN_COUNT + 2);
48417 
48418 /**
48419  * @brief Count number of hosts.
48420  *
48421  * @param[in]  get  GET params.
48422  *
48423  * @return Total number of hosts in filtered set.
48424  */
48425 int
asset_host_count(const get_data_t * get)48426 asset_host_count (const get_data_t *get)
48427 {
48428   static const char *filter_columns[] = HOST_ITERATOR_FILTER_COLUMNS;
48429   static column_t columns[] = HOST_ITERATOR_COLUMNS;
48430   static column_t where_columns[] = HOST_ITERATOR_WHERE_COLUMNS;
48431   return count2 ("host", get, columns, NULL, where_columns, NULL,
48432                  filter_columns, 0, NULL, NULL, NULL, TRUE);
48433 }
48434 
48435 /**
48436  * @brief Filter columns for os iterator.
48437  */
48438 #define OS_ITERATOR_FILTER_COLUMNS                                           \
48439  { GET_ITERATOR_FILTER_COLUMNS, "title", "hosts", "latest_severity",         \
48440    "highest_severity", "average_severity", "average_severity_score",         \
48441    "severity", NULL }
48442 
48443 /**
48444  * @brief OS iterator columns.
48445  */
48446 #define OS_ITERATOR_COLUMNS                                                   \
48447  {                                                                            \
48448    GET_ITERATOR_COLUMNS (oss),                                                \
48449    {                                                                          \
48450      "0",                                                                     \
48451      "writable",                                                              \
48452      KEYWORD_TYPE_INTEGER                                                     \
48453    },                                                                         \
48454    {                                                                          \
48455      "(SELECT count (*) > 0 FROM host_oss WHERE os = oss.id)",                \
48456      "in_use",                                                                \
48457      KEYWORD_TYPE_INTEGER                                                     \
48458    },                                                                         \
48459    {                                                                          \
48460      "(SELECT coalesce (cpe_title (oss.name), ''))",                          \
48461      "title",                                                                 \
48462      KEYWORD_TYPE_STRING                                                      \
48463    },                                                                         \
48464    {                                                                          \
48465      "(SELECT count(*)"                                                       \
48466      " FROM (SELECT inner_cpes[1] AS cpe, host"                               \
48467      "       FROM (SELECT array_agg (host_details.value"                      \
48468      "                               ORDER BY host_details.id DESC)"          \
48469      "                    AS inner_cpes,"                                     \
48470      "                    host"                                               \
48471      "             FROM host_details, hosts"                                  \
48472      "             WHERE host_details.name = 'best_os_cpe'"                   \
48473      "             AND hosts.id = host_details.host"                          \
48474      "             AND (" ACL_USER_MAY_OPTS ("hosts") ")"                     \
48475      "             GROUP BY host)"                                            \
48476      "            AS host_details_subselect)"                                 \
48477      "      AS array_removal_subselect"                                       \
48478      " WHERE cpe = oss.name)",                                                \
48479      "hosts",                                                                 \
48480      KEYWORD_TYPE_INTEGER                                                     \
48481    },                                                                         \
48482    {                                                                          \
48483      "(SELECT round (CAST (severity AS numeric), 1) FROM host_max_severities" \
48484      " WHERE host = (SELECT host FROM host_oss"                               \
48485      "               WHERE os = oss.id"                                       \
48486      "               ORDER BY creation_time DESC LIMIT 1)"                    \
48487      " ORDER BY creation_time DESC LIMIT 1)",                                 \
48488      "latest_severity",                                                       \
48489      KEYWORD_TYPE_DOUBLE                                                      \
48490    },                                                                         \
48491    {                                                                          \
48492      "(SELECT round (max (CAST (severity AS numeric)), 1)"                    \
48493      " FROM host_max_severities"                                              \
48494      " WHERE host IN (SELECT DISTINCT host FROM host_oss"                     \
48495      "                WHERE os = oss.id))",                                   \
48496      "highest_severity",                                                      \
48497      KEYWORD_TYPE_DOUBLE                                                      \
48498    },                                                                         \
48499    {                                                                          \
48500      "(SELECT round (CAST (avg (severity) AS numeric), 2)"                    \
48501      " FROM (SELECT (SELECT severity FROM host_max_severities"                \
48502      "               WHERE host = hosts.host"                                 \
48503      "               ORDER BY creation_time DESC LIMIT 1)"                    \
48504      "              AS severity"                                              \
48505      "       FROM (SELECT distinct host FROM host_oss WHERE os = oss.id)"     \
48506      "       AS hosts)"                                                       \
48507      " AS severities)",                                                       \
48508      "average_severity",                                                      \
48509      KEYWORD_TYPE_DOUBLE                                                      \
48510    },                                                                         \
48511    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
48512  }
48513 
48514 /**
48515  * @brief OS iterator optional filtering columns.
48516  */
48517 #define OS_ITERATOR_WHERE_COLUMNS                                             \
48518  {                                                                            \
48519    {                                                                          \
48520      "(SELECT round (CAST (avg (severity) AS numeric)"                        \
48521      "               * (SELECT count (distinct host)"                         \
48522      "                  FROM host_oss WHERE os = oss.id), 2)"                 \
48523      " FROM (SELECT (SELECT severity FROM host_max_severities"                \
48524      "               WHERE host = hosts.host"                                 \
48525      "               ORDER BY creation_time DESC LIMIT 1)"                    \
48526      "              AS severity"                                              \
48527      "       FROM (SELECT distinct host FROM host_oss WHERE os = oss.id)"     \
48528      "       AS hosts)"                                                       \
48529      " AS severities)",                                                       \
48530      "average_severity_score",                                                \
48531      KEYWORD_TYPE_DOUBLE                                                      \
48532    },                                                                         \
48533    {                                                                          \
48534      "(SELECT round (CAST (avg (severity) AS numeric), 2)"                    \
48535      " FROM (SELECT (SELECT severity FROM host_max_severities"                \
48536      "               WHERE host = hosts.host"                                 \
48537      "               ORDER BY creation_time DESC LIMIT 1)"                    \
48538      "              AS severity"                                              \
48539      "       FROM (SELECT distinct host FROM host_oss WHERE os = oss.id)"     \
48540      "       AS hosts)"                                                       \
48541      " AS severities)",                                                       \
48542      "severity",                                                              \
48543      KEYWORD_TYPE_DOUBLE                                                      \
48544    },                                                                         \
48545    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                       \
48546  }
48547 
48548 /**
48549  * @brief Generate the extra_tables string for an OS iterator.
48550  *
48551  * @return Newly allocated string.
48552  */
48553 static gchar *
asset_os_iterator_opts_table()48554 asset_os_iterator_opts_table ()
48555 {
48556   assert (current_credentials.uuid);
48557 
48558   return g_strdup_printf (", (SELECT"
48559                           "   (SELECT id FROM users"
48560                           "    WHERE users.uuid = '%s')"
48561                           "   AS user_id,"
48562                           "   'host' AS type)"
48563                           "  AS opts",
48564                           current_credentials.uuid);
48565 }
48566 
48567 /**
48568  * @brief Initialise an OS iterator.
48569  *
48570  * @param[in]  iterator    Iterator.
48571  * @param[in]  get         GET data.
48572  *
48573  * @return 0 success, 1 failed to find os, 2 failed to find filter,
48574  *         -1 error.
48575  */
48576 int
init_asset_os_iterator(iterator_t * iterator,const get_data_t * get)48577 init_asset_os_iterator (iterator_t *iterator, const get_data_t *get)
48578 {
48579   int ret;
48580   static const char *filter_columns[] = OS_ITERATOR_FILTER_COLUMNS;
48581   static column_t columns[] = OS_ITERATOR_COLUMNS;
48582   static column_t where_columns[] = OS_ITERATOR_WHERE_COLUMNS;
48583   gchar *extra_tables;
48584 
48585   extra_tables = asset_os_iterator_opts_table ();
48586 
48587   ret = init_get_iterator2_with (iterator,
48588                                  "os",
48589                                  get,
48590                                  /* Columns. */
48591                                  columns,
48592                                  /* Columns for trashcan. */
48593                                  NULL,
48594                                  /* WHERE Columns. */
48595                                  where_columns,
48596                                  /* WHERE Columns for trashcan. */
48597                                  NULL,
48598                                  filter_columns,
48599                                  0,
48600                                  extra_tables,
48601                                  NULL,
48602                                  NULL,
48603                                  TRUE,
48604                                  FALSE,
48605                                  NULL,
48606                                  NULL,
48607                                  0,
48608                                  0);
48609 
48610   g_free (extra_tables);
48611 
48612   return ret;
48613 }
48614 
48615 /**
48616  * @brief Get the title from an OS iterator.
48617  *
48618  * @param[in]  iterator  Iterator.
48619  *
48620  * @return The title of the OS, or NULL if iteration is
48621  *         complete. Freed by cleanup_iterator.
48622  */
48623 DEF_ACCESS (asset_os_iterator_title, GET_ITERATOR_COLUMN_COUNT + 2);
48624 
48625 /**
48626  * @brief Get the number of installs from an asset OS iterator.
48627  *
48628  * @param[in]  iterator  Iterator.
48629  *
48630  * @return Number of hosts that have the OS.
48631  */
48632 int
asset_os_iterator_installs(iterator_t * iterator)48633 asset_os_iterator_installs (iterator_t* iterator)
48634 {
48635   if (iterator->done) return 0;
48636   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
48637 }
48638 
48639 /**
48640  * @brief Get the latest severity from an OS iterator.
48641  *
48642  * @param[in]  iterator  Iterator.
48643  *
48644  * @return The severity of the OS, or NULL if iteration is
48645  *         complete. Freed by cleanup_iterator.
48646  */
48647 DEF_ACCESS (asset_os_iterator_latest_severity, GET_ITERATOR_COLUMN_COUNT + 4);
48648 
48649 /**
48650  * @brief Get the highest severity from an OS iterator.
48651  *
48652  * @param[in]  iterator  Iterator.
48653  *
48654  * @return The severity of the OS, or NULL if iteration is
48655  *         complete. Freed by cleanup_iterator.
48656  */
48657 DEF_ACCESS (asset_os_iterator_highest_severity, GET_ITERATOR_COLUMN_COUNT + 5);
48658 
48659 /**
48660  * @brief Get the average severity from an OS iterator.
48661  *
48662  * @param[in]  iterator  Iterator.
48663  *
48664  * @return The severity of the OS, or NULL if iteration is
48665  *         complete. Freed by cleanup_iterator.
48666  */
48667 DEF_ACCESS (asset_os_iterator_average_severity, GET_ITERATOR_COLUMN_COUNT + 6);
48668 
48669 /**
48670  * @brief Count number of oss.
48671  *
48672  * @param[in]  get  GET params.
48673  *
48674  * @return Total number of oss in filtered set.
48675  */
48676 int
asset_os_count(const get_data_t * get)48677 asset_os_count (const get_data_t *get)
48678 {
48679   static const char *extra_columns[] = OS_ITERATOR_FILTER_COLUMNS;
48680   static column_t columns[] = OS_ITERATOR_COLUMNS;
48681   static column_t where_columns[] = OS_ITERATOR_WHERE_COLUMNS;
48682   int ret;
48683 
48684   ret = count2 ("os", get, columns, NULL, where_columns, NULL,
48685                 extra_columns, 0, 0, 0, NULL, TRUE);
48686 
48687   return ret;
48688 }
48689 
48690 /**
48691  * @brief Initialise an OS host iterator.
48692  *
48693  * @param[in]  iterator    Iterator.
48694  * @param[in]  os          OS.
48695  */
48696 void
init_os_host_iterator(iterator_t * iterator,resource_t os)48697 init_os_host_iterator (iterator_t* iterator, resource_t os)
48698 {
48699   assert (os);
48700   init_iterator (iterator,
48701                  "SELECT id, uuid, name, comment, iso_time (creation_time),"
48702                  "       iso_time (modification_time), creation_time,"
48703                  "       modification_time, owner, owner,"
48704                  "       (SELECT round (CAST (severity AS numeric), 1)"
48705                  "        FROM host_max_severities"
48706                  "        WHERE host = hosts.id"
48707                  "        ORDER by creation_time DESC"
48708                  "        LIMIT 1)"
48709                  " FROM hosts"
48710                  " WHERE id IN (SELECT DISTINCT host FROM host_oss"
48711                  "              WHERE os = %llu)"
48712                  " ORDER BY modification_time DESC;",
48713                  os);
48714 }
48715 
48716 /**
48717  * @brief Get the severity from an OS host detail iterator.
48718  *
48719  * @param[in]  iterator  Iterator.
48720  *
48721  * @return The severity of the OS host, or NULL if iteration is
48722  *         complete.  Freed by cleanup_iterator.
48723  */
48724 DEF_ACCESS (os_host_iterator_severity, GET_ITERATOR_COLUMN_COUNT);
48725 
48726 /**
48727  * @brief Initialise an asset host detail iterator.
48728  *
48729  * @param[in]  iterator    Iterator.
48730  * @param[in]  host        Host.
48731  */
48732 void
init_host_detail_iterator(iterator_t * iterator,resource_t host)48733 init_host_detail_iterator (iterator_t* iterator, resource_t host)
48734 {
48735   assert (host);
48736   init_iterator (iterator,
48737                  "SELECT sub.id, name, value, source_type, source_id"
48738                  " FROM (SELECT max (id) AS id FROM host_details"
48739                  "       WHERE host = %llu"
48740                  "       GROUP BY name)"
48741                  "      AS sub,"
48742                  "      host_details"
48743                  " WHERE sub.id = host_details.id"
48744                  " ORDER BY name ASC;",
48745                  host);
48746 }
48747 
48748 /**
48749  * @brief Get the name from an asset host detail iterator.
48750  *
48751  * @param[in]  iterator  Iterator.
48752  *
48753  * @return The name of the host detail, or NULL if iteration is
48754  *         complete.  Freed by cleanup_iterator.
48755  */
48756 DEF_ACCESS (host_detail_iterator_name, 1);
48757 
48758 /**
48759  * @brief Get the name from an asset host detail iterator.
48760  *
48761  * @param[in]  iterator  Iterator.
48762  *
48763  * @return The name of the host detail, or NULL if iteration is
48764  *         complete.  Freed by cleanup_iterator.
48765  */
48766 DEF_ACCESS (host_detail_iterator_value, 2);
48767 
48768 /**
48769  * @brief Get the source type from an asset host detail iterator.
48770  *
48771  * @param[in]  iterator  Iterator.
48772  *
48773  * @return The source type of the host detail, or NULL if iteration is
48774  *         complete.  Freed by cleanup_iterator.
48775  */
48776 DEF_ACCESS (host_detail_iterator_source_type, 3);
48777 
48778 /**
48779  * @brief Get the source ID from an asset host detail iterator.
48780  *
48781  * @param[in]  iterator  Iterator.
48782  *
48783  * @return The source ID of the host detail, or NULL if iteration is
48784  *         complete.  Freed by cleanup_iterator.
48785  */
48786 DEF_ACCESS (host_detail_iterator_source_id, 4);
48787 
48788 /**
48789  * @brief Find a host for a specific permission, given a UUID.
48790  *
48791  * @param[in]   uuid        UUID of host.
48792  * @param[out]  host      Host return, 0 if successfully failed to find host.
48793  * @param[in]   permission  Permission.
48794  *
48795  * @return FALSE on success (including if failed to find host), TRUE on error.
48796  */
48797 static gboolean
find_host_with_permission(const char * uuid,host_t * host,const char * permission)48798 find_host_with_permission (const char* uuid, host_t* host,
48799                            const char *permission)
48800 {
48801   return find_resource_with_permission ("host", uuid, host, permission, 0);
48802 }
48803 
48804 /**
48805  * @brief Check whether a string is an identifier name.
48806  *
48807  * @param[in]  name  Possible identifier name.
48808  *
48809  * @return 1 if an identifier name, else 0.
48810  */
48811 static int
identifier_name(const char * name)48812 identifier_name (const char *name)
48813 {
48814   return (strcmp ("hostname", name) == 0)
48815          || (strcmp ("MAC", name) == 0)
48816          || (strcmp ("OS", name) == 0)
48817          || (strcmp ("ssh-key", name) == 0);
48818 }
48819 
48820 /**
48821  * @brief Create a host asset.
48822  *
48823  * @param[in]  host_name    Host Name.
48824  * @param[in]  comment      Comment.
48825  * @param[out] host_return  Created asset.
48826  *
48827  * @return 0 success, 1 failed to find report, 2 host not an IP address,
48828  *         99 permission denied, -1 error.
48829  */
48830 int
create_asset_host(const char * host_name,const char * comment,resource_t * host_return)48831 create_asset_host (const char *host_name, const char *comment,
48832                    resource_t* host_return)
48833 {
48834   int host_type;
48835   resource_t host;
48836   gchar *quoted_host_name, *quoted_comment;
48837 
48838   if (host_name == NULL)
48839     return -1;
48840 
48841   sql_begin_immediate ();
48842 
48843   if (acl_user_may ("create_asset") == 0)
48844     {
48845       sql_rollback ();
48846       return 99;
48847     }
48848 
48849   host_type = gvm_get_host_type (host_name);
48850   if (host_type != HOST_TYPE_IPV4 && host_type != HOST_TYPE_IPV6)
48851     {
48852       sql_rollback ();
48853       return 2;
48854     }
48855 
48856   quoted_host_name = sql_quote (host_name);
48857   quoted_comment = sql_quote (comment ? comment : "");
48858   sql ("INSERT into hosts"
48859        " (uuid, owner, name, comment, creation_time, modification_time)"
48860        " VALUES"
48861        " (make_uuid (), (SELECT id FROM users WHERE uuid = '%s'), '%s', '%s',"
48862        "  m_now (), m_now ());",
48863        current_credentials.uuid,
48864        quoted_host_name,
48865        quoted_comment);
48866   g_free (quoted_comment);
48867 
48868   host = sql_last_insert_id ();
48869 
48870   sql ("INSERT into host_identifiers"
48871        " (uuid, host, owner, name, comment, value, source_type, source_id,"
48872        "  source_data, creation_time, modification_time)"
48873        " VALUES"
48874        " (make_uuid (), %llu, (SELECT id FROM users WHERE uuid = '%s'), 'ip',"
48875        "  '', '%s', 'User', '%s', '', m_now (), m_now ());",
48876        host,
48877        current_credentials.uuid,
48878        quoted_host_name,
48879        current_credentials.uuid);
48880 
48881   g_free (quoted_host_name);
48882 
48883   if (host_return)
48884     *host_return = host;
48885 
48886   sql_commit ();
48887 
48888   return 0;
48889 }
48890 
48891 /**
48892  * @brief Create all available assets from a report.
48893  *
48894  * @param[in]  report_id  UUID of report.
48895  * @param[in]  term       Filter term, for min_qod and apply_overrides.
48896  *
48897  * @return 0 success, 1 failed to find report, 99 permission denied, -1 error.
48898  */
48899 int
create_asset_report(const char * report_id,const char * term)48900 create_asset_report (const char *report_id, const char *term)
48901 {
48902   resource_t report;
48903   iterator_t hosts;
48904   gchar *quoted_report_id;
48905   int apply_overrides, min_qod;
48906 
48907   if (report_id == NULL)
48908     return -1;
48909 
48910   sql_begin_immediate ();
48911 
48912   if (acl_user_may ("create_asset") == 0)
48913     {
48914       sql_rollback ();
48915       return 99;
48916     }
48917 
48918   report = 0;
48919   if (find_report_with_permission (report_id, &report, "get_reports"))
48920     {
48921       sql_rollback ();
48922       return -1;
48923     }
48924 
48925   if (report == 0)
48926     {
48927       sql_rollback ();
48928       return 1;
48929     }
48930 
48931   /* These are freed by hosts_set_identifiers. */
48932   if (identifiers == NULL)
48933     identifiers = make_array ();
48934   if (identifier_hosts == NULL)
48935     identifier_hosts = make_array ();
48936 
48937   quoted_report_id = sql_quote (report_id);
48938   sql ("DELETE FROM host_identifiers WHERE source_id = '%s';",
48939        quoted_report_id);
48940   sql ("DELETE FROM host_oss WHERE source_id = '%s';",
48941        quoted_report_id);
48942   sql ("DELETE FROM host_max_severities WHERE source_id = '%s';",
48943        quoted_report_id);
48944   sql ("DELETE FROM host_details WHERE source_id = '%s';",
48945        quoted_report_id);
48946   g_free (quoted_report_id);
48947 
48948   init_report_host_iterator (&hosts, report, NULL, 0);
48949   while (next (&hosts))
48950     {
48951       const char *host;
48952       report_host_t report_host;
48953       iterator_t details;
48954 
48955       host = host_iterator_host (&hosts);
48956       report_host = host_iterator_report_host (&hosts);
48957 
48958       if (report_host_dead (report_host)
48959           || report_host_result_count (report_host) == 0)
48960         continue;
48961 
48962       host_notice (host, "ip", host, "Report Host", report_id, 0, 0);
48963 
48964       init_report_host_details_iterator (&details, report_host);
48965       while (next (&details))
48966         {
48967           const char *name;
48968 
48969           name = report_host_details_iterator_name (&details);
48970 
48971           if (identifier_name (name))
48972             {
48973               const char *value;
48974               identifier_t *identifier;
48975 
48976               value = report_host_details_iterator_value (&details);
48977 
48978               if ((strcmp (name, "OS") == 0)
48979                   && (g_str_has_prefix (value, "cpe:") == 0))
48980                 continue;
48981 
48982               identifier = g_malloc (sizeof (identifier_t));
48983               identifier->ip = g_strdup (host);
48984               identifier->name = g_strdup (name);
48985               identifier->value = g_strdup (value);
48986               identifier->source_id = g_strdup (report_id);
48987               identifier->source_type = g_strdup ("Report Host Detail");
48988               identifier->source_data
48989                = g_strdup (report_host_details_iterator_source_name (&details));
48990 
48991               array_add (identifiers, identifier);
48992               array_add_new_string (identifier_hosts, g_strdup (host));
48993             }
48994         }
48995       cleanup_iterator (&details);
48996     }
48997   cleanup_iterator (&hosts);
48998   hosts_set_identifiers (report);
48999   apply_overrides = filter_term_apply_overrides (term);
49000   min_qod = filter_term_min_qod (term);
49001   hosts_set_max_severity (report, &apply_overrides, &min_qod);
49002   hosts_set_details (report);
49003 
49004   sql_commit ();
49005 
49006   return 0;
49007 }
49008 
49009 /**
49010  * @brief Modify an asset.
49011  *
49012  * @param[in]   asset_id        UUID of asset.
49013  * @param[in]   comment         Comment on asset.
49014  *
49015  * @return 0 success, 1 failed to find asset, 3 asset_id required,
49016  *         99 permission denied, -1 internal error.
49017  */
49018 int
modify_asset(const char * asset_id,const char * comment)49019 modify_asset (const char *asset_id, const char *comment)
49020 {
49021   gchar *quoted_asset_id, *quoted_comment;
49022   resource_t asset;
49023 
49024   if (asset_id == NULL)
49025     return 3;
49026 
49027   sql_begin_immediate ();
49028 
49029   if (acl_user_may ("modify_asset") == 0)
49030     {
49031       sql_rollback ();
49032       return 99;
49033     }
49034 
49035   /* Host. */
49036 
49037   quoted_asset_id = sql_quote (asset_id);
49038   switch (sql_int64 (&asset,
49039                      "SELECT id FROM hosts WHERE uuid = '%s';",
49040                      quoted_asset_id))
49041     {
49042       case 0:
49043         break;
49044       case 1:        /* Too few rows in result of query. */
49045         asset = 0;
49046         break;
49047       default:       /* Programming error. */
49048         assert (0);
49049       case -1:
49050         g_free (quoted_asset_id);
49051         sql_rollback ();
49052         return -1;
49053         break;
49054     }
49055 
49056   g_free (quoted_asset_id);
49057 
49058   if (asset == 0)
49059     {
49060       sql_rollback ();
49061       return 1;
49062     }
49063 
49064   quoted_comment = sql_quote (comment ?: "");
49065 
49066   sql ("UPDATE hosts SET"
49067        " comment = '%s',"
49068        " modification_time = m_now ()"
49069        " WHERE id = %llu;",
49070        quoted_comment, asset);
49071 
49072   g_free (quoted_comment);
49073 
49074   sql_commit ();
49075 
49076   return 0;
49077 }
49078 
49079 /**
49080  * @brief Delete all asset that came from a report.
49081  *
49082  * Assume caller started a transaction.
49083  *
49084  * @param[in]  report_id  UUID of report.
49085  *
49086  * @return 0 success, 2 failed to find report, 4 UUID
49087  *         required, 99 permission denied, -1 error.
49088  */
49089 static int
delete_report_assets(const char * report_id)49090 delete_report_assets (const char *report_id)
49091 {
49092   resource_t report;
49093   gchar *quoted_report_id;
49094 
49095   report = 0;
49096   if (find_report_with_permission (report_id, &report, "delete_report"))
49097     {
49098       sql_rollback ();
49099       return -1;
49100     }
49101 
49102   if (report == 0)
49103     {
49104       sql_rollback ();
49105       return 1;
49106     }
49107 
49108   quoted_report_id = sql_quote (report_id);
49109 
49110   /* Delete the hosts and OSs identified by this report if they were only
49111    * identified by this report. */
49112 
49113   sql ("CREATE TEMPORARY TABLE delete_report_assets_hosts (host INTEGER);");
49114 
49115   /* Collect hosts that were only identified by the given source. */
49116   sql ("INSERT into delete_report_assets_hosts"
49117        " (host)"
49118        " SELECT id FROM hosts"
49119        " WHERE (EXISTS (SELECT * FROM host_identifiers"
49120        "                WHERE host = hosts.id"
49121        "                AND source_id = '%s')"
49122        "        OR EXISTS (SELECT * FROM host_oss"
49123        "                   WHERE host = hosts.id"
49124        "                   AND source_id = '%s'))"
49125        " AND NOT EXISTS (SELECT * FROM host_identifiers"
49126        "                 WHERE host = hosts.id"
49127        "                 AND source_id != '%s')"
49128        " AND NOT EXISTS (SELECT * FROM host_oss"
49129        "                 WHERE host = hosts.id"
49130        "                 AND source_id != '%s');",
49131       quoted_report_id,
49132       quoted_report_id,
49133       quoted_report_id,
49134       quoted_report_id);
49135 
49136   sql ("DELETE FROM host_identifiers WHERE source_id = '%s';",
49137        quoted_report_id);
49138   sql ("DELETE FROM host_oss WHERE source_id = '%s';",
49139        quoted_report_id);
49140   sql ("DELETE FROM host_max_severities WHERE source_id = '%s';",
49141        quoted_report_id);
49142   sql ("DELETE FROM host_details WHERE source_id = '%s';",
49143        quoted_report_id);
49144 
49145   g_free (quoted_report_id);
49146 
49147   /* The host may have details from sources that did not identify the host. */
49148   sql ("DELETE FROM host_details"
49149        " WHERE host in (SELECT host FROM delete_report_assets_hosts);");
49150 
49151   /* The host may have severities from sources that did not identify the
49152    * host. */
49153   sql ("DELETE FROM host_max_severities"
49154        " WHERE host in (SELECT host FROM delete_report_assets_hosts);");
49155 
49156   sql ("DELETE FROM hosts"
49157        " WHERE id in (SELECT host FROM delete_report_assets_hosts);");
49158 
49159   sql ("DROP TABLE delete_report_assets_hosts;");
49160 
49161   sql_commit ();
49162   return 0;
49163 }
49164 
49165 /**
49166  * @brief Delete an asset.
49167  *
49168  * @param[in]  asset_id   UUID of asset.
49169  * @param[in]  report_id  UUID of report from which to delete assets.
49170  *                        Overridden by asset_id.
49171  * @param[in]  dummy      Dummy arg to match other delete functions.
49172  *
49173  * @return 0 success, 1 asset is in use, 2 failed to find asset, 4 UUID
49174  *         required, 99 permission denied, -1 error.
49175  */
49176 int
delete_asset(const char * asset_id,const char * report_id,int dummy)49177 delete_asset (const char *asset_id, const char *report_id, int dummy)
49178 {
49179   resource_t asset, parent;
49180   gchar *quoted_asset_id, *parent_id;
49181 
49182   asset = parent = 0;
49183 
49184   sql_begin_immediate ();
49185 
49186   if (acl_user_may ("delete_asset") == 0)
49187     {
49188       sql_rollback ();
49189       return 99;
49190     }
49191 
49192   if (asset_id == NULL)
49193     {
49194       if (report_id == NULL)
49195         {
49196           sql_rollback ();
49197           return 3;
49198         }
49199       return delete_report_assets (report_id);
49200     }
49201 
49202   /* Host identifier. */
49203 
49204   quoted_asset_id = sql_quote (asset_id);
49205   switch (sql_int64 (&asset,
49206                      "SELECT id FROM host_identifiers WHERE uuid = '%s';",
49207                      quoted_asset_id))
49208     {
49209       case 0:
49210         break;
49211       case 1:        /* Too few rows in result of query. */
49212         asset = 0;
49213         break;
49214       default:       /* Programming error. */
49215         assert (0);
49216       case -1:
49217         g_free (quoted_asset_id);
49218         sql_rollback ();
49219         return -1;
49220         break;
49221     }
49222 
49223   g_free (quoted_asset_id);
49224 
49225   if (asset)
49226     {
49227       parent_id = sql_string ("SELECT uuid FROM hosts"
49228                               " WHERE id = (SELECT host FROM host_identifiers"
49229                               "             WHERE id = %llu);",
49230                               asset);
49231       parent = 0;
49232       if (find_host_with_permission (parent_id, &parent, "delete_asset"))
49233         {
49234           sql_rollback ();
49235           return -1;
49236         }
49237 
49238       if (parent == 0)
49239         {
49240           sql_rollback ();
49241           return 99;
49242         }
49243 
49244       sql ("DELETE FROM host_identifiers WHERE id = %llu;", asset);
49245       sql_commit ();
49246 
49247       return 0;
49248     }
49249 
49250   /* Host OS. */
49251 
49252   quoted_asset_id = sql_quote (asset_id);
49253   switch (sql_int64 (&asset,
49254                      "SELECT id FROM host_oss WHERE uuid = '%s';",
49255                      quoted_asset_id))
49256     {
49257       case 0:
49258         break;
49259       case 1:        /* Too few rows in result of query. */
49260         asset = 0;
49261         break;
49262       default:       /* Programming error. */
49263         assert (0);
49264       case -1:
49265         g_free (quoted_asset_id);
49266         sql_rollback ();
49267         return -1;
49268         break;
49269     }
49270 
49271   g_free (quoted_asset_id);
49272 
49273   if (asset)
49274     {
49275       parent_id = sql_string ("SELECT uuid FROM hosts"
49276                               " WHERE id = (SELECT host FROM host_oss"
49277                               "             WHERE id = %llu);",
49278                               asset);
49279       parent = 0;
49280       if (find_host_with_permission (parent_id, &parent, "delete_asset"))
49281         {
49282           sql_rollback ();
49283           return -1;
49284         }
49285 
49286       if (parent == 0)
49287         {
49288           sql_rollback ();
49289           return 99;
49290         }
49291 
49292       sql ("DELETE FROM host_oss WHERE id = %llu;", asset);
49293       sql_commit ();
49294 
49295       return 0;
49296     }
49297 
49298   /* OS. */
49299 
49300   quoted_asset_id = sql_quote (asset_id);
49301   switch (sql_int64 (&asset,
49302                      "SELECT id FROM oss WHERE uuid = '%s';",
49303                      quoted_asset_id))
49304     {
49305       case 0:
49306         break;
49307       case 1:        /* Too few rows in result of query. */
49308         asset = 0;
49309         break;
49310       default:       /* Programming error. */
49311         assert (0);
49312       case -1:
49313         g_free (quoted_asset_id);
49314         sql_rollback ();
49315         return -1;
49316         break;
49317     }
49318 
49319   g_free (quoted_asset_id);
49320 
49321   if (asset)
49322     {
49323       if (sql_int ("SELECT count (*) FROM host_oss"
49324                    " WHERE os = %llu;",
49325                    asset))
49326         {
49327           sql_rollback ();
49328           return 1;
49329         }
49330 
49331       sql ("DELETE FROM oss WHERE id = %llu;", asset);
49332       permissions_set_orphans ("os", asset, LOCATION_TABLE);
49333       tags_remove_resource ("os", asset, LOCATION_TABLE);
49334       sql_commit ();
49335 
49336       return 0;
49337     }
49338 
49339   /* Host. */
49340 
49341   quoted_asset_id = sql_quote (asset_id);
49342   switch (sql_int64 (&asset,
49343                      "SELECT id FROM hosts WHERE uuid = '%s';",
49344                      quoted_asset_id))
49345     {
49346       case 0:
49347         break;
49348       case 1:        /* Too few rows in result of query. */
49349         asset = 0;
49350         break;
49351       default:       /* Programming error. */
49352         assert (0);
49353       case -1:
49354         g_free (quoted_asset_id);
49355         sql_rollback ();
49356         return -1;
49357         break;
49358     }
49359 
49360   g_free (quoted_asset_id);
49361 
49362   if (asset)
49363     {
49364       sql ("DELETE FROM host_identifiers WHERE host = %llu;", asset);
49365       sql ("DELETE FROM host_oss WHERE host = %llu;", asset);
49366       sql ("DELETE FROM host_max_severities WHERE host = %llu;", asset);
49367       sql ("DELETE FROM host_details WHERE host = %llu;", asset);
49368       sql ("DELETE FROM hosts WHERE id = %llu;", asset);
49369       permissions_set_orphans ("host", asset, LOCATION_TABLE);
49370       tags_remove_resource ("host", asset, LOCATION_TABLE);
49371       sql_commit ();
49372 
49373       return 0;
49374     }
49375 
49376   sql_rollback ();
49377   return 2;
49378 }
49379 
49380 /**
49381  * @brief Generates and adds assets from report host details
49382  *
49383  * @param[in]  report   The report to get host details from.
49384  * @param[in]  host_ip  IP address of the host to get details from.
49385  *
49386  * @return 0 success, -1 error.
49387  */
49388 int
add_assets_from_host_in_report(report_t report,const char * host_ip)49389 add_assets_from_host_in_report (report_t report, const char *host_ip)
49390 {
49391   int ret;
49392   gchar *quoted_host;
49393   char *report_id;
49394   report_host_t report_host = 0;
49395 
49396   /* Get report UUID */
49397   report_id = report_uuid (report);
49398   if (report_id == NULL)
49399     {
49400       g_warning ("%s: report %llu not found.",
49401                  __func__, report);
49402       return -1;
49403     }
49404 
49405   /* Find report_host */
49406   quoted_host = sql_quote (host_ip);
49407   sql_int64 (&report_host,
49408              "SELECT id FROM report_hosts"
49409              " WHERE host = '%s' AND report = %llu",
49410              quoted_host,
49411              report);
49412   g_free (quoted_host);
49413   if (report_host == 0)
49414     {
49415       g_warning ("%s: report_host for host '%s' and report '%s' not found.",
49416                  __func__, host_ip, report_id);
49417       free (report_id);
49418       return -1;
49419     }
49420 
49421   /* Create assets */
49422   if (report_host_noticeable (report, host_ip))
49423     {
49424       host_notice (host_ip, "ip", host_ip, "Report Host", report_id, 1, 1);
49425     }
49426 
49427   ret = add_tls_certificates_from_report_host (report_host,
49428                                                report_id,
49429                                                host_ip);
49430   if (ret)
49431     {
49432       free (report_id);
49433       return ret;
49434     }
49435 
49436   return 0;
49437 }
49438 
49439 
49440 /* Settings. */
49441 
49442 /**
49443  * @brief Filter columns for setting iterator.
49444  */
49445 #define SETTING_ITERATOR_FILTER_COLUMNS \
49446  { "name", "comment", "value", NULL }
49447 
49448 /**
49449  * @brief Setting iterator columns.
49450  */
49451 #define SETTING_ITERATOR_COLUMNS                              \
49452  {                                                            \
49453    { "id" , NULL, KEYWORD_TYPE_INTEGER },                     \
49454    { "uuid", NULL, KEYWORD_TYPE_STRING },                     \
49455    { "name", NULL, KEYWORD_TYPE_STRING },                     \
49456    { "comment", NULL, KEYWORD_TYPE_STRING },                  \
49457    { "value", NULL, KEYWORD_TYPE_STRING },                    \
49458    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                       \
49459  }
49460 
49461 /**
49462  * @brief Count number of settings.
49463  *
49464  * @param[in]  filter           Filter term.
49465  *
49466  * @return Total number of settings in filtered set.
49467  */
49468 int
setting_count(const char * filter)49469 setting_count (const char *filter)
49470 {
49471   static const char *filter_columns[] = SETTING_ITERATOR_FILTER_COLUMNS;
49472   static column_t select_columns[] = SETTING_ITERATOR_COLUMNS;
49473   gchar *clause;
49474   int ret;
49475 
49476   assert (current_credentials.uuid);
49477 
49478   clause = filter_clause ("setting", filter, filter_columns, select_columns,
49479                           NULL, 0, NULL, NULL, NULL, NULL, NULL);
49480 
49481   ret = sql_int ("SELECT count (*)"
49482                  " FROM settings"
49483                  " WHERE"
49484                  " (owner = (SELECT id FROM users WHERE uuid = '%s')"
49485                  "  OR (owner IS NULL"
49486                  "      AND uuid"
49487                  "      NOT IN (SELECT uuid FROM settings"
49488                  "              WHERE owner = (SELECT id FROM users"
49489                  "                             WHERE uuid = '%s'))))"
49490                  "%s%s;",
49491                  current_credentials.uuid,
49492                  current_credentials.uuid,
49493                  clause ? " AND " : "",
49494                  clause ? clause : "");
49495 
49496   g_free (clause);
49497 
49498   return ret;
49499 }
49500 
49501 /**
49502  * @brief Return the uuid of a resource filter from settings.
49503  *
49504  * @param[in]  resource  Resource (eg. Filters, Targets, CPE).
49505  *
49506  * @return resource filter uuid in settings if it exists, "" otherwise.
49507  */
49508 char *
setting_filter(const char * resource)49509 setting_filter (const char *resource)
49510 {
49511   return sql_string ("SELECT value FROM settings WHERE name = '%s Filter'"
49512                      " AND " ACL_GLOBAL_OR_USER_OWNS () ""
49513                      " ORDER BY coalesce (owner, 0) DESC;",
49514                      resource,
49515                      current_credentials.uuid);
49516 }
49517 
49518 /**
49519  * @brief Return the Default Severity user setting as a double.
49520  *
49521  * @return The user's Default Severity.
49522  */
49523 static double
setting_default_severity_dbl()49524 setting_default_severity_dbl ()
49525 {
49526   return current_credentials.default_severity;
49527 }
49528 
49529 /**
49530  * @brief Return the Dynamic Severity user setting as an int.
49531  *
49532  * @return 1 if user's Dynamic Severity is "Yes", 0 if it is "No",
49533  *         or does not exist.
49534  */
49535 static int
setting_dynamic_severity_int()49536 setting_dynamic_severity_int ()
49537 {
49538   return current_credentials.dynamic_severity;
49539 }
49540 
49541 /**
49542  * @brief Return the user's timezone.
49543  *
49544  * @return User Severity Class in settings if it exists, else NULL.
49545  */
49546 static char *
setting_timezone()49547 setting_timezone ()
49548 {
49549   return sql_string ("SELECT timezone FROM users WHERE uuid = '%s'",
49550                      current_credentials.uuid);
49551 }
49552 
49553 /**
49554  * @brief Return the Auto Cache Rebuild user setting as an int.
49555  *
49556  * @return 1 if cache is rebuilt automatically, 0 if not.
49557  */
49558 static int
setting_auto_cache_rebuild_int()49559 setting_auto_cache_rebuild_int ()
49560 {
49561   return sql_int ("SELECT coalesce"
49562                   "        ((SELECT value FROM settings"
49563                   "          WHERE uuid = 'a09285b0-2d47-49b6-a4ef-946ee71f1d5c'"
49564                   "          AND " ACL_USER_OWNS () ""
49565                   "          ORDER BY coalesce (owner, 0) DESC LIMIT 1),"
49566                   "         '1');",
49567                   current_credentials.uuid);
49568 }
49569 
49570 /**
49571  * @brief Initialise a setting iterator, including observed settings.
49572  *
49573  * @param[in]  iterator    Iterator.
49574  * @param[in]  uuid        UUID of setting to limit iteration to.  0 for all.
49575  * @param[in]  filter      Filter term.
49576  * @param[in]  first       First setting.
49577  * @param[in]  max         Maximum number of settings returned.
49578  * @param[in]  ascending   Whether to sort ascending or descending.
49579  * @param[in]  sort_field  Field to sort on, or NULL for "id".
49580  */
49581 void
init_setting_iterator(iterator_t * iterator,const char * uuid,const char * filter,int first,int max,int ascending,const char * sort_field)49582 init_setting_iterator (iterator_t *iterator, const char *uuid,
49583                        const char *filter, int first, int max, int ascending,
49584                        const char *sort_field)
49585 {
49586   static const char *filter_columns[] = SETTING_ITERATOR_FILTER_COLUMNS;
49587   static column_t select_columns[] = SETTING_ITERATOR_COLUMNS;
49588   gchar *clause, *columns, *quoted_uuid;
49589 
49590   assert (current_credentials.uuid);
49591 
49592   if (first < 0)
49593     first = 0;
49594   if (max < 1)
49595     max = -1;
49596 
49597   clause = filter_clause ("setting", filter, filter_columns, select_columns,
49598                           NULL, 0, NULL, NULL, NULL, NULL, NULL);
49599 
49600   quoted_uuid = uuid ? sql_quote (uuid) : NULL;
49601   columns = columns_build_select (select_columns);
49602 
49603   if (quoted_uuid)
49604     init_iterator (iterator,
49605                    "SELECT %s"
49606                    " FROM settings"
49607                    " WHERE uuid = '%s'"
49608                    " AND (owner = (SELECT id FROM users WHERE uuid = '%s')"
49609                    "      OR (owner IS NULL"
49610                    "          AND uuid"
49611                    "          NOT IN (SELECT uuid FROM settings"
49612                    "                  WHERE owner = (SELECT id FROM users"
49613                    "                                 WHERE uuid = '%s'))))",
49614                    columns,
49615                    quoted_uuid,
49616                    current_credentials.uuid,
49617                    current_credentials.uuid);
49618   else
49619     init_iterator (iterator,
49620                    "SELECT %s"
49621                    " FROM settings"
49622                    " WHERE"
49623                    " (owner = (SELECT id FROM users WHERE uuid = '%s')"
49624                    "  OR (owner IS NULL"
49625                    "      AND uuid"
49626                    "      NOT IN (SELECT uuid FROM settings"
49627                    "              WHERE owner = (SELECT id FROM users"
49628                    "                             WHERE uuid = '%s'))))"
49629                    "%s%s"
49630                    " ORDER BY %s %s"
49631                    " LIMIT %s OFFSET %i;",
49632                    columns,
49633                    current_credentials.uuid,
49634                    current_credentials.uuid,
49635                    clause ? " AND " : "",
49636                    clause ? clause : "",
49637                    sort_field ? sort_field : "id",
49638                    ascending ? "ASC" : "DESC",
49639                    sql_select_limit (max),
49640                    first);
49641 
49642   g_free (quoted_uuid);
49643   g_free (columns);
49644   g_free (clause);
49645 }
49646 
49647 /**
49648  * @brief Get the UUID from a setting iterator.
49649  *
49650  * @param[in]  iterator  Iterator.
49651  *
49652  * @return The UUID of the setting, or NULL if iteration is complete.  Freed by
49653  *         cleanup_iterator.
49654  */
49655 DEF_ACCESS (setting_iterator_uuid, 1);
49656 
49657 /**
49658  * @brief Get the name from a setting iterator.
49659  *
49660  * @param[in]  iterator  Iterator.
49661  *
49662  * @return The name of the setting, or NULL if iteration is complete.  Freed by
49663  *         cleanup_iterator.
49664  */
49665 DEF_ACCESS (setting_iterator_name, 2);
49666 
49667 /**
49668  * @brief Get the comment from a setting iterator.
49669  *
49670  * @param[in]  iterator  Iterator.
49671  *
49672  * @return The comment of the setting, or NULL if iteration is complete.  Freed by
49673  *         cleanup_iterator.
49674  */
49675 DEF_ACCESS (setting_iterator_comment, 3);
49676 
49677 /**
49678  * @brief Get the value from a setting iterator.
49679  *
49680  * @param[in]  iterator  Iterator.
49681  *
49682  * @return The value of the setting, or NULL if iteration is complete.  Freed by
49683  *         cleanup_iterator.
49684  */
49685 DEF_ACCESS (setting_iterator_value, 4);
49686 
49687 /**
49688  * @brief Get the value of a setting as a string.
49689  *
49690  * @param[in]   uuid   UUID of setting.
49691  * @param[out]  value  Freshly allocated value.
49692  *
49693  * @return 0 success, -1 error.
49694  */
49695 int
setting_value(const char * uuid,char ** value)49696 setting_value (const char *uuid, char **value)
49697 {
49698   gchar *quoted_uuid;
49699 
49700   if (value == NULL || uuid == NULL)
49701     return -1;
49702 
49703   quoted_uuid = sql_quote (uuid);
49704 
49705   if (sql_int ("SELECT count (*)"
49706                " FROM settings"
49707                " WHERE uuid = '%s'"
49708                " AND " ACL_GLOBAL_OR_USER_OWNS () ";",
49709                quoted_uuid,
49710                current_credentials.uuid)
49711       == 0)
49712     {
49713       *value = NULL;
49714       g_free (quoted_uuid);
49715       return -1;
49716     }
49717 
49718   *value = sql_string
49719              ("SELECT value"
49720               " FROM settings"
49721               " WHERE uuid = '%s'"
49722               " AND " ACL_GLOBAL_OR_USER_OWNS ()
49723               /* Force the user's setting to come before the default. */
49724               " ORDER BY coalesce (owner, 0) DESC;",
49725               quoted_uuid,
49726               current_credentials.uuid);
49727 
49728   g_free (quoted_uuid);
49729 
49730   return 0;
49731 }
49732 
49733 /**
49734  * @brief Get the value of a setting.
49735  *
49736  * @param[in]   uuid   UUID of setting.
49737  * @param[out]  value  Value.
49738  *
49739  * @return 0 success, -1 error.
49740  */
49741 static int
setting_value_int(const char * uuid,int * value)49742 setting_value_int (const char *uuid, int *value)
49743 {
49744   gchar *quoted_uuid;
49745 
49746   if (value == NULL || uuid == NULL)
49747     return -1;
49748 
49749   quoted_uuid = sql_quote (uuid);
49750 
49751   if (sql_int ("SELECT count (*)"
49752                " FROM settings"
49753                " WHERE uuid = '%s'"
49754                " AND " ACL_GLOBAL_OR_USER_OWNS () ";",
49755                quoted_uuid,
49756                current_credentials.uuid)
49757       == 0)
49758     {
49759       *value = -1;
49760       g_free (quoted_uuid);
49761       return -1;
49762     }
49763 
49764   *value = sql_int ("SELECT value"
49765                     " FROM settings"
49766                     " WHERE uuid = '%s'"
49767                     " AND " ACL_GLOBAL_OR_USER_OWNS ()
49768                     /* Force the user's setting to come before the default. */
49769                     " ORDER BY coalesce (owner, 0) DESC;",
49770                     quoted_uuid,
49771                     current_credentials.uuid);
49772 
49773   g_free (quoted_uuid);
49774 
49775   return 0;
49776 }
49777 
49778 /**
49779  * @brief Set the value of a setting.
49780  *
49781  * @param[in]  uuid      UUID of setting.
49782  * @param[in]  name      Setting name.  For Timezone and Password.
49783  * @param[in]  value_64  New setting value, base64 encoded.
49784  * @param[out] r_errdesc If not NULL the address of a variable to receive
49785  *                       a malloced string with the error description.  Will
49786  *                       always be set to NULL on success.
49787  *
49788  * @return 0 success, 1 failed to find setting, 2 syntax error in value,
49789  *         99 permission denied, -1 on error.
49790  */
49791 int
modify_setting(const gchar * uuid,const gchar * name,const gchar * value_64,gchar ** r_errdesc)49792 modify_setting (const gchar *uuid, const gchar *name,
49793                 const gchar *value_64, gchar **r_errdesc)
49794 {
49795   char *setting_name;
49796 
49797   assert (current_credentials.uuid);
49798 
49799   if (acl_user_may ("modify_setting") == 0)
49800     return 99;
49801 
49802   if (r_errdesc)
49803     *r_errdesc = NULL;
49804 
49805   if (name && (strcmp (name, "Timezone") == 0))
49806     {
49807       gsize value_size;
49808       gchar *quoted_timezone, *value;
49809       if (value_64 && strlen (value_64))
49810         {
49811           value = (gchar*) g_base64_decode (value_64, &value_size);
49812           if (g_utf8_validate (value, value_size, NULL) == FALSE)
49813             {
49814               if (r_errdesc)
49815                 *r_errdesc = g_strdup ("Value cannot be decoded to"
49816                                        " valid UTF-8");
49817               g_free (value);
49818               return -1;
49819             }
49820         }
49821       else
49822         {
49823           value = g_strdup ("");
49824           value_size = 0;
49825         }
49826       quoted_timezone = sql_quote (value);
49827       g_free (value);
49828       sql ("UPDATE users SET timezone = '%s', modification_time = m_now ()"
49829            " WHERE uuid = '%s';",
49830            quoted_timezone,
49831            current_credentials.uuid);
49832       g_free (quoted_timezone);
49833       return 0;
49834     }
49835 
49836   if (name && (strcmp (name, "Password") == 0))
49837     {
49838       gsize value_size;
49839       gchar *value;
49840       int ret;
49841 
49842       assert (current_credentials.username);
49843 
49844       if (value_64 && strlen (value_64))
49845         {
49846           value = (gchar*) g_base64_decode (value_64, &value_size);
49847           if (g_utf8_validate (value, value_size, NULL) == FALSE)
49848             {
49849               if (r_errdesc)
49850                 *r_errdesc = g_strdup ("Value cannot be decoded to"
49851                                        " valid UTF-8");
49852               g_free (value);
49853               return -1;
49854             }
49855         }
49856       else
49857         {
49858           value = g_strdup ("");
49859           value_size = 0;
49860         }
49861 
49862       ret = set_password (current_credentials.username,
49863                           current_credentials.uuid,
49864                           value,
49865                           r_errdesc);
49866       g_free (value);
49867       return ret;
49868     }
49869 
49870   if (uuid && (strcmp (uuid, SETTING_UUID_ROWS_PER_PAGE) == 0
49871                || strcmp (uuid, "6765549a-934e-11e3-b358-406186ea4fc5") == 0
49872                || strcmp (uuid, "77ec2444-e7f2-4a80-a59b-f4237782d93f") == 0
49873                || strcmp (uuid, "7eda49c5-096c-4bef-b1ab-d080d87300df") == 0
49874                || strcmp (uuid, "578a1c14-e2dc-45ef-a591-89d31391d007") == 0
49875                || strcmp (uuid, "02e294fa-061b-11e6-ae64-28d24461215b") == 0
49876                || strcmp (uuid, "5a9046cc-0628-11e6-ba53-28d24461215b") == 0
49877                || strcmp (uuid, "a09285b0-2d47-49b6-a4ef-946ee71f1d5c") == 0))
49878     {
49879       gsize value_size;
49880       gchar *value, *quoted_uuid, *quoted_value;
49881 
49882       assert (current_credentials.username);
49883 
49884       quoted_uuid = sql_quote (uuid);
49885 
49886       if (sql_int ("SELECT count(*) FROM settings"
49887                    " WHERE uuid = '%s'"
49888                    " AND " ACL_IS_GLOBAL () ";",
49889                    quoted_uuid,
49890                    current_credentials.uuid)
49891           == 0)
49892         {
49893           g_free (quoted_uuid);
49894           return 1;
49895         }
49896 
49897       if (value_64 && strlen (value_64))
49898         {
49899           value = (gchar*) g_base64_decode (value_64, &value_size);
49900           if (g_utf8_validate (value, value_size, NULL) == FALSE)
49901             {
49902               if (r_errdesc)
49903                 *r_errdesc = g_strdup ("Value cannot be decoded to"
49904                                        " valid UTF-8");
49905               g_free (value);
49906               return -1;
49907             }
49908         }
49909       else
49910         {
49911           value = g_strdup ("");
49912           value_size = 0;
49913         }
49914 
49915       if (strcmp (uuid, SETTING_UUID_ROWS_PER_PAGE) == 0)
49916         {
49917           const gchar *val;
49918           /* Rows Per Page. */
49919           val = value;
49920           while (*val && isdigit (*val)) val++;
49921           if (*val && strcmp (value, "-1"))
49922             {
49923               g_free (quoted_uuid);
49924               return 2;
49925             }
49926         }
49927 
49928       if (strcmp (uuid, "6765549a-934e-11e3-b358-406186ea4fc5") == 0)
49929         {
49930           GRegex *languages_regex;
49931           gboolean match;
49932           /*
49933            * regex: colon-separated lists of language or language and country
49934            *  codes (ISO 639-1, 639-2 and 3166-1 alpha-2)
49935            *  as used in the LANGUAGE env variable by gettext
49936            */
49937           languages_regex
49938             = g_regex_new ("^(Browser Language|"
49939                            "([a-z]{2,3})(_[A-Z]{2})?(@[[:alnum:]_-]+)?"
49940                            "(:([a-z]{2,3})(_[A-Z]{2})?(@[[:alnum:]_-]+)?)*)$",
49941                            0, 0, NULL);
49942           match = g_regex_match (languages_regex, value, 0, NULL);
49943           g_regex_unref (languages_regex);
49944 
49945           /* User Interface Language. */
49946           if (match)
49947             {
49948               // Valid languages string or "Browser Language":
49949               //  keep string as it is
49950             }
49951           /* Legacy full language names */
49952           else if (strcmp (value, "Chinese") == 0)
49953             {
49954               g_free (value);
49955               value = g_strdup ("zh_CN");
49956             }
49957           else if (strcmp (value, "English") == 0)
49958             {
49959               g_free (value);
49960               value = g_strdup ("en");
49961             }
49962           else if (strcmp (value, "German") == 0)
49963             {
49964               g_free (value);
49965               value = g_strdup ("de");
49966             }
49967           /* Invalid value */
49968           else
49969             {
49970               g_free (quoted_uuid);
49971               g_free (value);
49972               return 2;
49973             }
49974         }
49975 
49976       if (strcmp (uuid, "77ec2444-e7f2-4a80-a59b-f4237782d93f") == 0)
49977         {
49978           /* Dynamic Severity */
49979           current_credentials.dynamic_severity = atoi (value);
49980           reports_clear_count_cache (current_credentials.uuid);
49981         }
49982 
49983       if (strcmp (uuid, "7eda49c5-096c-4bef-b1ab-d080d87300df") == 0)
49984         {
49985           double severity_dbl;
49986           /* Default Severity */
49987           if (sscanf (value, "%lf", &severity_dbl) != 1
49988               || severity_dbl < 0.0 || severity_dbl > 10.0)
49989             {
49990               g_free (value);
49991               return 2;
49992             }
49993           else
49994             current_credentials.default_severity = severity_dbl;
49995         }
49996 
49997       if (strcmp (uuid, "a09285b0-2d47-49b6-a4ef-946ee71f1d5c") == 0)
49998         {
49999           int value_int;
50000           /* Auto Cache Rebuild */
50001           if (sscanf (value, "%d", &value_int) != 1
50002               || (strcmp (value, "0") && strcmp (value, "1")))
50003             {
50004               g_free (value);
50005               return 2;
50006             }
50007         }
50008 
50009       quoted_value = sql_quote (value);
50010       g_free (value);
50011 
50012       if (sql_int ("SELECT count(*) FROM settings"
50013                    " WHERE uuid = '%s'"
50014                    " AND owner = (SELECT id FROM users WHERE uuid = '%s');",
50015                    quoted_uuid,
50016                    current_credentials.uuid))
50017         sql ("UPDATE settings SET value = '%s'"
50018              " WHERE uuid = '%s'"
50019              " AND owner = (SELECT id FROM users WHERE uuid = '%s');",
50020              quoted_value,
50021              quoted_uuid,
50022              current_credentials.uuid);
50023       else
50024         sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
50025              " VALUES"
50026              " ('%s',"
50027              "  (SELECT id FROM users WHERE uuid = '%s'),"
50028              "  (SELECT name FROM settings"
50029              "   WHERE uuid = '%s' AND " ACL_IS_GLOBAL ()
50030              "   LIMIT 1),"
50031              "  (SELECT comment FROM settings"
50032              "   WHERE uuid = '%s' AND " ACL_IS_GLOBAL ()
50033              "   LIMIT 1),"
50034              "  '%s');",
50035              quoted_uuid,
50036              current_credentials.uuid,
50037              quoted_uuid,
50038              quoted_uuid,
50039              quoted_value);
50040 
50041       g_free (quoted_uuid);
50042       g_free (quoted_value);
50043 
50044       return 0;
50045     }
50046 
50047   /* Export file name format */
50048   if (uuid
50049       && (strcmp (uuid, "a6ac88c5-729c-41ba-ac0a-deea4a3441f2") == 0
50050           || strcmp (uuid, "0872a6ed-4f85-48c5-ac3f-a5ef5e006745") == 0
50051           || strcmp (uuid, "e1a2ae0b-736e-4484-b029-330c9e15b900") == 0))
50052     {
50053       gsize value_size;
50054       gchar *value, *quoted_value;
50055 
50056       assert (current_credentials.uuid);
50057       if (strcmp (uuid, "a6ac88c5-729c-41ba-ac0a-deea4a3441f2") == 0)
50058         setting_name = "Details Export File Name";
50059       else if (strcmp (uuid, "0872a6ed-4f85-48c5-ac3f-a5ef5e006745") == 0)
50060         setting_name = "List Export File Name";
50061       else if (strcmp (uuid, "e1a2ae0b-736e-4484-b029-330c9e15b900") == 0)
50062         setting_name = "Report Export File Name";
50063       else
50064         return -1;
50065 
50066       if (value_64 && strlen (value_64))
50067         {
50068           value = (gchar*) g_base64_decode (value_64, &value_size);
50069           if (g_utf8_validate (value, value_size, NULL) == FALSE)
50070             {
50071               if (r_errdesc)
50072                 *r_errdesc = g_strdup ("Value cannot be decoded to"
50073                                        " valid UTF-8");
50074               g_free (value);
50075               return -1;
50076             }
50077         }
50078       else
50079         {
50080           value = g_strdup ("");
50081           value_size = 0;
50082         }
50083       quoted_value = sql_quote (value);
50084 
50085       if (strcmp (value, "") == 0)
50086         {
50087           g_free (value);
50088           g_free (quoted_value);
50089           return 2;
50090         }
50091 
50092       if (sql_int ("SELECT count(*) FROM settings"
50093                    " WHERE uuid = '%s'"
50094                    " AND owner = (SELECT id FROM users WHERE uuid = '%s');",
50095                    uuid,
50096                    current_credentials.uuid))
50097         sql ("UPDATE settings SET value = '%s'"
50098              " WHERE uuid = '%s'"
50099              " AND owner = (SELECT id FROM users WHERE uuid = '%s');",
50100              quoted_value,
50101              uuid,
50102              current_credentials.uuid);
50103       else
50104         sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
50105              " VALUES"
50106              " ('%s',"
50107              "  (SELECT id FROM users WHERE uuid = '%s'),"
50108              "  '%s',"
50109              "  (SELECT comment FROM settings"
50110              "   WHERE uuid = '%s' AND " ACL_IS_GLOBAL () "),"
50111              "  '%s');",
50112              uuid,
50113              current_credentials.uuid,
50114              setting_name,
50115              uuid,
50116              quoted_value);
50117 
50118       g_free (value);
50119       g_free (quoted_value);
50120 
50121       return 0;
50122     }
50123 
50124   /* Resources filters, default resource selections and chart preferences. */
50125 
50126   setting_name = NULL;
50127   if (uuid)
50128     {
50129       /* Filters */
50130       if (strcmp (uuid, "b833a6f2-dcdc-4535-bfb0-a5154b5b5092") == 0)
50131         setting_name = g_strdup ("Alerts Filter");
50132       else if (strcmp (uuid, "0f040d06-abf9-43a2-8f94-9de178b0e978") == 0)
50133         setting_name = g_strdup ("Assets Filter");
50134       else if (strcmp (uuid, "1a9fbd91-0182-44cd-bc88-a13a9b3b1bef") == 0)
50135         setting_name = g_strdup ("Configs Filter");
50136       else if (strcmp (uuid, "186a5ac8-fe5a-4fb1-aa22-44031fb339f3") == 0)
50137         setting_name = g_strdup ("Credentials Filter");
50138       else if (strcmp (uuid, "f9691163-976c-47e7-ad9a-38f2d5c81649") == 0)
50139         setting_name = g_strdup ("Filters Filter");
50140       else if (strcmp (uuid, "f722e5a4-88d8-475f-95b9-e4dcafbc075b") == 0)
50141         setting_name = g_strdup ("Groups Filter");
50142       else if (strcmp (uuid, "37562dfe-1f7e-4cae-a7c0-fa95e6f194c5") == 0)
50143         setting_name = g_strdup ("Hosts Filter");
50144       else if (strcmp (uuid, "96abcd5a-9b6d-456c-80b8-c3221bfa499d") == 0)
50145         setting_name = g_strdup ("Notes Filter");
50146       else if (strcmp (uuid, "f608c3ec-ce73-4ff6-8e04-7532749783af") == 0)
50147         setting_name = g_strdup ("Operating Systems Filter");
50148       else if (strcmp (uuid, "eaaaebf1-01ef-4c49-b7bb-955461c78e0a") == 0)
50149         setting_name = g_strdup ("Overrides Filter");
50150       else if (strcmp (uuid, "ffb16b28-538c-11e3-b8f9-406186ea4fc5") == 0)
50151         setting_name = g_strdup ("Permissions Filter");
50152       else if (strcmp (uuid, "7d52d575-baeb-4d98-bb68-e1730dbc6236") == 0)
50153         setting_name = g_strdup ("Port Lists Filter");
50154       else if (strcmp (uuid, "48ae588e-9085-41bc-abcb-3d6389cf7237") == 0)
50155         setting_name = g_strdup ("Reports Filter");
50156       else if (strcmp (uuid, "249c7a55-065c-47fb-b453-78e11a665565") == 0)
50157         setting_name = g_strdup ("Report Formats Filter");
50158       else if (strcmp (uuid, "739ab810-163d-11e3-9af6-406186ea4fc5") == 0)
50159         setting_name = g_strdup ("Results Filter");
50160       else if (strcmp (uuid, "f38e673a-bcd1-11e2-a19a-406186ea4fc5") == 0)
50161         setting_name = g_strdup ("Roles Filter");
50162       else if (strcmp (uuid, "ba00fe91-bdce-483c-b8df-2372e9774ad6") == 0)
50163         setting_name = g_strdup ("Scanners Filter");
50164       else if (strcmp (uuid, "a83e321b-d994-4ae8-beec-bfb5fe3e7336") == 0)
50165         setting_name = g_strdup ("Schedules Filter");
50166       else if (strcmp (uuid, "108eea3b-fc61-483c-9da9-046762f137a8") == 0)
50167         setting_name = g_strdup ("Tags Filter");
50168       else if (strcmp (uuid, "236e2e41-9771-4e7a-8124-c432045985e0") == 0)
50169         setting_name = g_strdup ("Targets Filter");
50170       else if (strcmp (uuid, "1c981851-8244-466c-92c4-865ffe05e721") == 0)
50171         setting_name = g_strdup ("Tasks Filter");
50172       else if (strcmp (uuid, "801544de-f06d-4377-bb77-bbb23369bad4") == 0)
50173         setting_name = g_strdup ("Tickets Filter");
50174       else if (strcmp (uuid, "34a176c1-0278-4c29-b84d-3d72117b2169") == 0)
50175         setting_name = g_strdup ("TLS Certificates Filter");
50176       else if (strcmp (uuid, "a33635be-7263-4549-bd80-c04d2dba89b4") == 0)
50177         setting_name = g_strdup ("Users Filter");
50178       else if (strcmp (uuid, "17c9d269-95e7-4bfa-b1b2-bc106a2175c7") == 0)
50179         setting_name = g_strdup ("Vulnerabilities Filter");
50180       else if (strcmp (uuid, "3414a107-ae46-4dea-872d-5c4479a48e8f") == 0)
50181         setting_name = g_strdup ("CPE Filter");
50182       else if (strcmp (uuid, "def63b5a-41ef-43f4-b9ef-03ef1665db5d") == 0)
50183         setting_name = g_strdup ("CVE Filter");
50184       else if (strcmp (uuid, "bef08b33-075c-4f8c-84f5-51f6137e40a3") == 0)
50185         setting_name = g_strdup ("NVT Filter");
50186       else if (strcmp (uuid, "adb6ffc8-e50e-4aab-9c31-13c741eb8a16") == 0)
50187         setting_name = g_strdup ("OVAL Filter");
50188       else if (strcmp (uuid, "e4cf514a-17e2-4ab9-9c90-336f15e24750") == 0)
50189         setting_name = g_strdup ("CERT-Bund Filter");
50190       else if (strcmp (uuid, "312350ed-bc06-44f3-8b3f-ab9eb828b80b") == 0)
50191         setting_name = g_strdup ("DFN-CERT Filter");
50192       else if (strcmp (uuid, "32b3d606-461b-4770-b3e1-b9ea3cf0f84c") == 0)
50193         setting_name = g_strdup ("Notes Filter");
50194       else if (strcmp (uuid, "956d13bd-3baa-4404-a138-5e7eb8f9630e") == 0)
50195         setting_name = g_strdup ("Overrides Filter");
50196 
50197       /* Content composer defaults */
50198       else if (strcmp (uuid, "b6b449ee-5d90-4ff0-af20-7e838c389d39") == 0)
50199         setting_name = g_strdup ("Report Composer Defaults");
50200 
50201       /* Default resource selections */
50202       else if (strcmp (uuid, "f9f5a546-8018-48d0-bef5-5ad4926ea899") == 0)
50203         setting_name = g_strdup ("Default Alert");
50204 
50205       else if (strcmp (uuid, "fe7ea321-e3e3-4cc6-9952-da836aae83ce") == 0)
50206         setting_name = g_strdup ("Default OpenVAS Scan Config");
50207       else if (strcmp (uuid, "fb19ac4b-614c-424c-b046-0bc32bf1be73") == 0)
50208         setting_name = g_strdup ("Default OSP Scan Config");
50209 
50210       else if (strcmp (uuid, "6fc56b72-c1cf-451c-a4c4-3a9dc784c3bd") == 0)
50211         setting_name = g_strdup ("Default SSH Credential");
50212       else if (strcmp (uuid, "a25c0cfe-f977-417b-b1da-47da370c03e8") == 0)
50213         setting_name = g_strdup ("Default SMB Credential");
50214       else if (strcmp (uuid, "83545bcf-0c49-4b4c-abbf-63baf82cc2a7") == 0)
50215         setting_name = g_strdup ("Default ESXi Credential");
50216       else if (strcmp (uuid, "024550b8-868e-4b3c-98bf-99bb732f6a0d") == 0)
50217         setting_name = g_strdup ("Default SNMP Credential");
50218 
50219       else if (strcmp (uuid, "d74a9ee8-7d35-4879-9485-ab23f1bd45bc") == 0)
50220         setting_name = g_strdup ("Default Port List");
50221 
50222       else if (strcmp (uuid, "f7d0f6ed-6f9e-45dc-8bd9-05cced84e80d") == 0)
50223         setting_name = g_strdup ("Default OpenVAS Scanner");
50224       else if (strcmp (uuid, "b20697c9-be0a-4cd4-8b4d-5fe7841ebb03") == 0)
50225         setting_name = g_strdup ("Default OSP Scanner");
50226 
50227       else if (strcmp (uuid, "353304fc-645e-11e6-ba7a-28d24461215b") == 0)
50228         setting_name = g_strdup ("Default Report Format");
50229 
50230       else if (strcmp (uuid, "778eedad-5550-4de0-abb6-1320d13b5e18") == 0)
50231         setting_name = g_strdup ("Default Schedule");
50232 
50233       else if (strcmp (uuid, "23409203-940a-4b4a-b70c-447475f18323") == 0)
50234         setting_name = g_strdup ("Default Target");
50235 
50236       /*
50237        * Main dashboard
50238        */
50239       else if (strcmp (uuid, "d97eca9f-0386-4e5d-88f2-0ed7f60c0646") == 0)
50240         setting_name = g_strdup ("Main Dashboard Configuration");
50241 
50242       /*
50243        * Scans dashboards
50244        */
50245       else if (strcmp (uuid, "c7584d7c-649f-4f8b-9ded-9e1dc20f24c8") == 0)
50246         setting_name = g_strdup ("Scans Dashboard Configuration");
50247 
50248       /* Tasks dashboard settings */
50249       else if (strcmp (uuid, "3d5db3c7-5208-4b47-8c28-48efc621b1e0") == 0)
50250         setting_name = g_strdup ("Tasks Top Dashboard Configuration");
50251 
50252       /* Reports dashboard settings */
50253       else if (strcmp (uuid, "e599bb6b-b95a-4bb2-a6bb-fe8ac69bc071") == 0)
50254         setting_name = g_strdup ("Reports Top Dashboard Configuration");
50255 
50256       /* Results dashboard settings */
50257       else if (strcmp (uuid, "0b8ae70d-d8fc-4418-8a72-e65ac8d2828e") == 0)
50258         setting_name = g_strdup ("Results Top Dashboard Configuration");
50259 
50260       /* Vulns dashboard settings */
50261       else if (strcmp (uuid, "43690dcb-3174-4d84-aa88-58c1936c7f5c") == 0)
50262         setting_name = g_strdup ("Vulnerabilities Top Dashboard Configuration");
50263 
50264       /* Notes dashboard settings */
50265       else if (strcmp (uuid, "ce7b121-c609-47b0-ab57-fd020a0336f4a") == 0)
50266         setting_name = g_strdup ("Notes Top Dashboard Configuration");
50267 
50268       /* Overrides dashboard settings */
50269       else if (strcmp (uuid, "054862fe-0781-4527-b1aa-2113bcd16ce7") == 0)
50270         setting_name = g_strdup ("Overrides Top Dashboard Configuration");
50271 
50272       /*
50273        * Assets dashboards
50274        */
50275       else if (strcmp (uuid, "0320e0db-bf30-4d4f-9379-b0a022d07cf7") == 0)
50276         setting_name = g_strdup ("Assets Dashboard Configuration");
50277 
50278       /* Hosts dashboard settings */
50279       else if (strcmp (uuid, "d3f5f2de-a85b-43f2-a817-b127457cc8ba") == 0)
50280         setting_name = g_strdup ("Hosts Top Dashboard Configuration");
50281 
50282       /* TLS Certificate dashboard settings */
50283       else if (strcmp (uuid, "9b62bf16-bf90-11e9-ad97-28d24461215b") == 0)
50284         setting_name = g_strdup ("TLS Certificates Top Dashboard Configuration");
50285 
50286       /* Operating Systems dashboard settings */
50287       else if (strcmp (uuid, "e93b51ed-5881-40e0-bc4f-7d3268a36177") == 0)
50288         setting_name = g_strdup ("OSs Top Dashboard Configuration");
50289 
50290       /*
50291        * SecInfo dashboards
50292        */
50293       else if (strcmp (uuid, "84ab32da-fe69-44d8-8a8f-70034cf28d4e") == 0)
50294         setting_name = g_strdup ("SecInfo Dashboard Configuration");
50295 
50296       /* NVTs dashboard settings */
50297       else if (strcmp (uuid, "f68d9369-1945-477b-968f-121c6029971b") == 0)
50298         setting_name = g_strdup ("NVTs Top Dashboard Configuration");
50299 
50300       /* CVEs dashboard settings */
50301       else if (strcmp (uuid, "815ddd2e-8654-46c7-a05b-d73224102240") == 0)
50302         setting_name = g_strdup ("CVEs Top Dashboard Configuration");
50303 
50304       /* CPEs dashboard settings */
50305       else if (strcmp (uuid, "9cff9b4d-b164-43ce-8687-f2360afc7500") == 0)
50306         setting_name = g_strdup ("CPEs Top Dashboard Configuration");
50307 
50308       /* OVAL Definitions dashboard settings */
50309       else if (strcmp (uuid, "9563efc0-9f4e-4d1f-8f8d-0205e32b90a4") == 0)
50310         setting_name = g_strdup ("OVAL Definitions Top Dashboard Configuration");
50311 
50312       /* CERT-Bund Advisories dashboard settings */
50313       else if (strcmp (uuid, "a6946f44-480f-4f37-8a73-28a4cd5310c4") == 0)
50314         setting_name = g_strdup ("CERT-Bund Advisories Top Dashboard"
50315                                  " Configuration");
50316 
50317       /* DFN-CERT Advisories */
50318       else if (strcmp (uuid, "9812ea49-682d-4f99-b3cc-eca051d1ce59") == 0)
50319         setting_name = g_strdup ("DFN-CERT Advisories Top Dashboard"
50320                                  " Configuration");
50321 
50322       /* All SecInfo */
50323       else if (strcmp (uuid, "4c7b1ea7-b7e6-4d12-9791-eb9f72b6f864") == 0)
50324         setting_name = g_strdup ("All SecInfo Top Dashboard Configuration");
50325 
50326       /*
50327        * Resilience / Remediation dashboards
50328        */
50329 
50330       /* Tickets */
50331       else if (strcmp (uuid, "70b0626f-a835-478e-8194-e09f97887a15") == 0)
50332         setting_name = g_strdup ("Tickets Top Dashboard Configuration");
50333 
50334       /* Business Process Model (BPM) */
50335       else if (strcmp (uuid, "3232d608-e5bb-415e-99aa-019f16eede8d") == 0)
50336         setting_name = g_strdup ("BPM Dashboard Configuration");
50337 
50338       /*
50339        * Client data for Business Process Modeling (BPM)
50340        */
50341       else if (strcmp (uuid, "3ce2d136-bb52-448a-93f0-20069566f877") == 0)
50342         setting_name = g_strdup ("BPM Data");
50343     }
50344 
50345   if (setting_name)
50346     {
50347       gchar *quoted_value, *value;
50348       gsize value_size;
50349 
50350       assert (current_credentials.username);
50351 
50352       if (value_64 && strlen (value_64))
50353         {
50354           value = (gchar*) g_base64_decode (value_64, &value_size);
50355           if (g_utf8_validate (value, value_size, NULL) == FALSE)
50356             {
50357               if (r_errdesc)
50358                 *r_errdesc = g_strdup ("Value cannot be decoded to"
50359                                        " valid UTF-8");
50360               g_free (value);
50361               return -1;
50362             }
50363         }
50364       else
50365         {
50366           value = g_strdup ("");
50367           value_size = 0;
50368         }
50369 
50370       quoted_value = sql_quote (value);
50371 
50372       if (sql_int ("SELECT count(*) FROM settings"
50373                    " WHERE uuid = '%s'"
50374                    " AND owner = (SELECT id FROM users WHERE uuid = '%s');",
50375                    uuid,
50376                    current_credentials.uuid))
50377         sql ("UPDATE settings SET value = '%s'"
50378              " WHERE uuid = '%s'"
50379              " AND owner = (SELECT id FROM users WHERE uuid = '%s');",
50380              quoted_value,
50381              uuid,
50382              current_credentials.uuid);
50383       else
50384         sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
50385              " VALUES"
50386              " ('%s',"
50387              "  (SELECT id FROM users WHERE uuid = '%s'),"
50388              "  '%s',"
50389              "  (SELECT comment FROM settings"
50390              "   WHERE uuid = '%s' AND " ACL_IS_GLOBAL () "),"
50391              "  '%s');",
50392              uuid,
50393              current_credentials.uuid,
50394              setting_name,
50395              uuid,
50396              quoted_value);
50397 
50398       g_free (value);
50399       g_free (quoted_value);
50400 
50401       return 0;
50402     }
50403 
50404   return 1;
50405 }
50406 
50407 /**
50408  * @brief Return max, adjusted according to maximum allowed rows.
50409  *
50410  * @param[in]  max  Max.
50411  *
50412  * @return Adjusted max.
50413  */
50414 int
manage_max_rows(int max)50415 manage_max_rows (int max)
50416 {
50417   int max_rows;
50418 
50419   if (current_credentials.uuid == NULL)
50420     return max;
50421 
50422   if (ignore_max_rows_per_page
50423       || setting_value_int (SETTING_UUID_MAX_ROWS_PER_PAGE, &max_rows))
50424     return max;
50425 
50426   if (max_rows && (max < 0 || max > max_rows))
50427     return max_rows;
50428   return max;
50429 }
50430 
50431 /**
50432  * @brief Get the name of a setting.
50433  *
50434  * @param[in]  uuid  UUID of setting.
50435  *
50436  * @return Setting name.
50437  */
50438 static const gchar *
setting_name(const gchar * uuid)50439 setting_name (const gchar *uuid)
50440 {
50441   if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
50442     return "Default CA Cert";
50443   if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
50444     return "Max Rows Per Page";
50445   if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
50446     return "Debian LSC Package Maintainer";
50447   if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
50448     return "Feed Import Owner";
50449   if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
50450     return "Feed Import Roles";
50451   return NULL;
50452 }
50453 
50454 /**
50455  * @brief Check whether a setting is the Default CA Cert setting.
50456  *
50457  * @param[in]  uuid  UUID of setting.
50458  *
50459  * @return 1 if Default CA Cert, else 0.
50460  */
50461 int
setting_is_default_ca_cert(const gchar * uuid)50462 setting_is_default_ca_cert (const gchar *uuid)
50463 {
50464   return strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0;
50465 }
50466 
50467 /**
50468  * @brief Get the description of a setting.
50469  *
50470  * @param[in]  uuid  UUID of setting.
50471  *
50472  * @return Setting description.
50473  */
50474 static const gchar *
setting_description(const gchar * uuid)50475 setting_description (const gchar *uuid)
50476 {
50477   if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
50478     return "Default CA Certificate for Scanners";
50479   if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
50480     return "The default maximum number of rows displayed in any listing.";
50481   if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
50482     return "Maintainer email address used in generated Debian LSC packages.";
50483   if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
50484     return "User who is given ownership of new resources from feed.";
50485   if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
50486     return "Roles given access to new resources from feed.";
50487   return NULL;
50488 }
50489 
50490 /**
50491  * @brief Get the name of a setting.
50492  *
50493  * @param[in]  uuid   UUID of setting.
50494  * @param[in]  value  Value of setting, to verify.
50495  * @param[in]  user   User setting is to apply to, or NULL.
50496  *
50497  * @return 0 if valid, else 1.
50498  */
50499 static int
setting_verify(const gchar * uuid,const gchar * value,const gchar * user)50500 setting_verify (const gchar *uuid, const gchar *value, const gchar *user)
50501 {
50502   if (value == NULL)
50503     return 0;
50504 
50505   if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
50506     return 0;
50507 
50508   if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
50509     {
50510       int max_rows;
50511       max_rows = atoi (value);
50512       if (user)
50513         {
50514           if (max_rows < -1)
50515             return 1;
50516         }
50517       else if (max_rows < 0)
50518         return 1;
50519     }
50520 
50521   if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
50522     {
50523       if (g_regex_match_simple
50524             ("^([[:alnum:]-_]*@[[:alnum:]-_][[:alnum:]-_.]*)?$",
50525             value, 0, 0) == FALSE)
50526         return 1;
50527     }
50528 
50529   if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0
50530       && strlen (value))
50531     {
50532       user_t value_user;
50533       gchar *quoted_uuid;
50534 
50535       quoted_uuid = sql_quote (value);
50536       switch (sql_int64 (&value_user,
50537                          "SELECT id FROM users WHERE uuid = '%s';",
50538                          quoted_uuid))
50539         {
50540           case 0:
50541             break;
50542           case 1:        /* Too few rows in result of query. */
50543             g_free (quoted_uuid);
50544             return 1;
50545           default:       /* Programming error. */
50546             assert (0);
50547           case -1:
50548             g_free (quoted_uuid);
50549             return 1;
50550         }
50551       g_free (quoted_uuid);
50552     }
50553 
50554   if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
50555     {
50556       gchar **split, **point;
50557 
50558       point = split = g_strsplit (value, ",", 0);
50559       while (*point)
50560         {
50561           if (g_regex_match_simple ("^[-0123456789abcdefABCDEF]{36}$",
50562                                     g_strstrip (*point), 0, 0)
50563               == FALSE)
50564             {
50565               g_strfreev (split);
50566               return 1;
50567             }
50568           point++;
50569         }
50570       g_strfreev (split);
50571     }
50572 
50573   return 0;
50574 }
50575 
50576 /**
50577  * @brief Normalise the value of a setting.
50578  *
50579  * @param[in]  uuid   UUID of setting.
50580  * @param[in]  value  Value of setting, to verify.
50581  *
50582  * @return Normalised value.
50583  */
50584 static gchar *
setting_normalise(const gchar * uuid,const gchar * value)50585 setting_normalise (const gchar *uuid, const gchar *value)
50586 {
50587   if (value == NULL)
50588     return NULL;
50589 
50590   if (strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE) == 0)
50591     {
50592       int max_rows;
50593       max_rows = atoi (value);
50594       if (max_rows < 0)
50595         return NULL;
50596       return g_strdup_printf ("%i", max_rows);
50597     }
50598 
50599   if (strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER) == 0)
50600     {
50601       return g_strstrip (g_strdup (value));
50602     }
50603 
50604   if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0)
50605     {
50606       GString *normalised;
50607       gchar **split, **point;
50608 
50609       normalised = g_string_new ("");
50610       point = split = g_strsplit (value, ",", 0);
50611 
50612       while (*point)
50613         {
50614           g_string_append_printf (normalised,
50615                                   "%s%s",
50616                                   point == split ? "" : ",",
50617                                   g_strstrip (*point));
50618           point++;
50619         }
50620 
50621       g_strfreev (split);
50622 
50623       g_string_ascii_down (normalised);
50624 
50625       return g_string_free (normalised, FALSE);
50626     }
50627 
50628   return g_strdup (value);
50629 }
50630 
50631 /**
50632  * @brief Change value of a setting.
50633  *
50634  * @param[in]  log_config      Log configuration.
50635  * @param[in]  database        Location of manage database.
50636  * @param[in]  name            Name of user.
50637  * @param[in]  uuid            UUID of setting.
50638  * @param[in]  value           New value.
50639  *
50640  * @return 0 success, 1 failed to find user, 2 value out of range, 3 error in
50641  *         setting uuid, 4 modifying setting for a single user forbidden,
50642  *         5 syntax error in setting value, -1 error.
50643  */
50644 int
manage_modify_setting(GSList * log_config,const db_conn_info_t * database,const gchar * name,const gchar * uuid,const char * value)50645 manage_modify_setting (GSList *log_config, const db_conn_info_t *database,
50646                        const gchar *name, const gchar *uuid, const char *value)
50647 {
50648   int ret;
50649   gchar *quoted_name, *quoted_description, *quoted_value, *normalised;
50650 
50651   g_info ("   Modifying setting.");
50652 
50653   if (strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT)
50654       && strcmp (uuid, SETTING_UUID_MAX_ROWS_PER_PAGE)
50655       && strcmp (uuid, SETTING_UUID_LSC_DEB_MAINTAINER)
50656       && strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER)
50657       && strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES))
50658     {
50659       fprintf (stderr, "Error in setting UUID.\n");
50660       return 3;
50661     }
50662 
50663   ret = manage_option_setup (log_config, database);
50664   if (ret)
50665     return ret;
50666 
50667   sql_begin_immediate ();
50668 
50669   if (setting_verify (uuid, value, name))
50670     {
50671       sql_rollback ();
50672       fprintf (stderr, "Syntax error in setting value.\n");
50673       manage_option_cleanup ();
50674       return 5;
50675     }
50676 
50677   if (name)
50678     {
50679       user_t user;
50680 
50681       if ((strcmp (uuid, SETTING_UUID_DEFAULT_CA_CERT) == 0)
50682           || (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
50683           || (strcmp (uuid, SETTING_UUID_FEED_IMPORT_ROLES) == 0))
50684         {
50685           sql_rollback ();
50686           fprintf (stderr,
50687                    "Modifying this setting for a single user is forbidden.\n");
50688           manage_option_cleanup ();
50689           return 4;
50690         }
50691 
50692       if (find_user_by_name (name, &user))
50693         {
50694           sql_rollback ();
50695           fprintf (stderr, "Internal error.\n");
50696           manage_option_cleanup ();
50697           return -1;
50698         }
50699 
50700       if (user == 0)
50701         {
50702           sql_rollback ();
50703           fprintf (stderr, "Failed to find user.\n");
50704           manage_option_cleanup ();
50705           return 1;
50706         }
50707 
50708       sql ("DELETE FROM settings"
50709            " WHERE uuid = '%s'"
50710            " AND owner = %llu;",
50711            uuid,
50712            user);
50713 
50714       normalised = setting_normalise (uuid, value);
50715       if (normalised)
50716         {
50717           quoted_value = sql_quote (normalised);
50718           g_free (normalised);
50719           quoted_name = sql_quote (setting_name (uuid));
50720           quoted_description = sql_quote (setting_description (uuid));
50721           sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
50722                " VALUES ('%s', %llu, '%s', '%s', '%s');",
50723                uuid,
50724                user,
50725                quoted_name,
50726                quoted_description,
50727                quoted_value);
50728           g_free (quoted_value);
50729           g_free (quoted_name);
50730           g_free (quoted_description);
50731         }
50732     }
50733   else
50734     {
50735       sql ("DELETE FROM settings"
50736            " WHERE uuid = '%s'"
50737            " AND owner IS NULL;",
50738            uuid);
50739 
50740       normalised = setting_normalise (uuid, value);
50741       if (normalised)
50742         {
50743           quoted_value = sql_quote (normalised);
50744           g_free (normalised);
50745           quoted_name = sql_quote (setting_name (uuid));
50746           quoted_description = sql_quote (setting_description (uuid));
50747           sql ("INSERT INTO settings (uuid, owner, name, comment, value)"
50748                " VALUES ('%s', NULL, '%s', '%s', '%s');",
50749                uuid,
50750                quoted_name,
50751                quoted_description,
50752                quoted_value);
50753           g_free (quoted_value);
50754           g_free (quoted_name);
50755           g_free (quoted_description);
50756 
50757           if (strcmp (uuid, SETTING_UUID_FEED_IMPORT_OWNER) == 0)
50758             {
50759               migrate_predefined_configs ();
50760               migrate_predefined_port_lists ();
50761               if (migrate_predefined_report_formats ())
50762                 {
50763                   sql_rollback ();
50764                   manage_option_cleanup ();
50765                   return -1;
50766                 }
50767             }
50768         }
50769     }
50770 
50771   sql_commit ();
50772   manage_option_cleanup ();
50773   return 0;
50774 }
50775 
50776 /**
50777  * @brief Get the default CA cert.
50778  *
50779  * @return Freshly allocated value of Default CA Cert setting.
50780  */
50781 char *
manage_default_ca_cert()50782 manage_default_ca_cert ()
50783 {
50784   return sql_string ("SELECT value FROM settings"
50785                      " WHERE uuid = '" SETTING_UUID_DEFAULT_CA_CERT "';");
50786 }
50787 
50788 
50789 /* Users. */
50790 
50791 /**
50792  * @brief Create the given user.
50793  *
50794  * @param[in]  log_config  Log configuration.
50795  * @param[in]  database    Location of manage database.
50796  * @param[in]  name        Name of user.
50797  * @param[in]  password    Password for user.  Autogenerated if NULL.
50798  * @param[in]  role_name   Role of user.  Admin if NULL.
50799  *
50800  * @return 0 success, -1 error,
50801  *         -2 database is wrong version, -3 database needs to be initialised
50802  *         from server.
50803  */
50804 int
manage_create_user(GSList * log_config,const db_conn_info_t * database,const gchar * name,const gchar * password,const gchar * role_name)50805 manage_create_user (GSList *log_config, const db_conn_info_t *database,
50806                     const gchar *name, const gchar *password,
50807                     const gchar *role_name)
50808 {
50809   char *uuid;
50810   array_t *roles;
50811   int ret;
50812   gchar *rejection_msg;
50813 
50814   g_info ("   Creating user.");
50815 
50816   ret = manage_option_setup (log_config, database);
50817   if (ret)
50818     return ret;
50819 
50820   roles = make_array ();
50821   if (role_name)
50822     {
50823       role_t role;
50824       if (find_role_by_name (role_name, &role))
50825         {
50826           array_free (roles);
50827           fprintf (stderr, "Internal Error.\n");
50828           manage_option_cleanup ();
50829           return -1;
50830         }
50831       if (role == 0)
50832         {
50833           array_free (roles);
50834           fprintf (stderr, "Failed to find role.\n");
50835           manage_option_cleanup ();
50836           return -1;
50837         }
50838       array_add (roles, role_uuid (role));
50839     }
50840   else
50841     array_add (roles, g_strdup (ROLE_UUID_ADMIN));
50842 
50843   if (password)
50844     uuid = NULL;
50845   else
50846     uuid = gvm_uuid_make ();
50847 
50848   /* Setup a dummy user, so that create_user will work. */
50849   current_credentials.uuid = "";
50850 
50851   ret = create_user (name, password ? password : uuid, "", NULL, 0, NULL, 0,
50852                      NULL, NULL, NULL, roles, NULL, &rejection_msg, NULL, 0);
50853 
50854   switch (ret)
50855     {
50856       case 0:
50857         if (password)
50858           printf ("User created.\n");
50859         else
50860           printf ("User created with password '%s'.\n", uuid);
50861         break;
50862       case -2:
50863         fprintf (stderr, "User exists already.\n");
50864         break;
50865       default:
50866         if (rejection_msg)
50867           {
50868             fprintf (stderr, "Failed to create user: %s\n", rejection_msg);
50869           }
50870         else
50871           fprintf (stderr, "Failed to create user.\n");
50872         break;
50873     }
50874 
50875   current_credentials.uuid = NULL;
50876   g_free (rejection_msg);
50877 
50878   array_free (roles);
50879   free (uuid);
50880 
50881   manage_option_cleanup ();
50882 
50883   return ret;
50884 }
50885 
50886 /**
50887  * @brief Delete the given user.
50888  *
50889  * @param[in]  log_config  Log configuration.
50890  * @param[in]  database    Location of manage database.
50891  * @param[in]  name        Name of user.
50892  * @param[in]  inheritor_name  Name of user that inherits user's resources.
50893  *
50894  * @return 0 success, 2 failed to find user, 4 user has active tasks, -1 error.
50895  *         -2 database is wrong version, -3 database needs to be initialised
50896  *         from server.
50897  */
50898 int
manage_delete_user(GSList * log_config,const db_conn_info_t * database,const gchar * name,const gchar * inheritor_name)50899 manage_delete_user (GSList *log_config, const db_conn_info_t *database,
50900                     const gchar *name, const gchar *inheritor_name)
50901 {
50902   int ret;
50903 
50904   g_info ("   Deleting user.");
50905 
50906   ret = manage_option_setup (log_config, database);
50907   if (ret)
50908     return ret;
50909 
50910   /* Setup a dummy user, so that delete_user will work. */
50911   current_credentials.uuid = "";
50912 
50913   switch ((ret = delete_user (NULL, name, 1, 0, NULL, inheritor_name)))
50914     {
50915       case 0:
50916         printf ("User deleted.\n");
50917         break;
50918       case 2:
50919         fprintf (stderr, "Failed to find user.\n");
50920         break;
50921       case 4:
50922         fprintf (stderr, "User has active tasks.\n");
50923         break;
50924       case 6:
50925         fprintf (stderr, "Inheritor not found.\n");
50926         break;
50927       case 7:
50928         fprintf (stderr, "Inheritor same as deleted user.\n");
50929         break;
50930       case 8:
50931         fprintf (stderr, "Invalid inheritor.\n");
50932         break;
50933       case 9:
50934         fprintf (stderr,
50935                  "Resources owned by the user are still in use by others.\n");
50936         break;
50937       case 10:
50938         fprintf (stderr, "User is Feed Import Owner.\n");
50939         break;
50940       default:
50941         fprintf (stderr, "Internal Error.\n");
50942         break;
50943     }
50944 
50945   current_credentials.uuid = NULL;
50946 
50947   manage_option_cleanup ();
50948 
50949   return ret;
50950 }
50951 
50952 /**
50953  * @brief List users.
50954  *
50955  * @param[in]  log_config  Log configuration.
50956  * @param[in]  database    Location of manage database.
50957  * @param[in]  role_name   Role name.
50958  * @param[in]  verbose     Whether to print UUID.
50959  *
50960  * @return 0 success, -1 error.
50961  */
50962 int
manage_get_users(GSList * log_config,const db_conn_info_t * database,const gchar * role_name,int verbose)50963 manage_get_users (GSList *log_config, const db_conn_info_t *database,
50964                   const gchar* role_name, int verbose)
50965 {
50966   iterator_t users;
50967   int ret;
50968 
50969   g_info ("   Getting users.");
50970 
50971   ret = manage_option_setup (log_config, database);
50972   if (ret)
50973     return ret;
50974 
50975   if (role_name)
50976     {
50977       role_t role;
50978       if (find_role_by_name (role_name, &role))
50979         {
50980           fprintf (stderr, "Internal Error.\n");
50981           manage_option_cleanup ();
50982           return -1;
50983         }
50984       if (role == 0)
50985         {
50986           fprintf (stderr, "Failed to find role.\n");
50987           manage_option_cleanup ();
50988           return -1;
50989         }
50990       init_iterator (&users,
50991                      "SELECT name, uuid FROM users"
50992                      " WHERE id IN (SELECT \"user\" FROM role_users"
50993                      "              WHERE role = %llu);",
50994                      role);
50995     }
50996   else
50997     init_iterator (&users, "SELECT name, uuid FROM users;");
50998   while (next (&users))
50999     if (verbose)
51000       printf ("%s %s\n", iterator_string (&users, 0), iterator_string (&users, 1));
51001     else
51002       printf ("%s\n", iterator_string (&users, 0));
51003 
51004   cleanup_iterator (&users);
51005 
51006   manage_option_cleanup ();
51007 
51008   return 0;
51009 }
51010 
51011 /**
51012  * @brief Set the password of a user.
51013  *
51014  * @param[in]  name      Name of user.
51015  * @param[in]  uuid      User UUID.
51016  * @param[in]  password  New password.
51017  * @param[out] r_errdesc Address to receive a malloced string with the error
51018  *                       description, or NULL.
51019  *
51020  * @return 0 success, -1 error.
51021  */
51022 static int
set_password(const gchar * name,const gchar * uuid,const gchar * password,gchar ** r_errdesc)51023 set_password (const gchar *name, const gchar *uuid, const gchar *password,
51024               gchar **r_errdesc)
51025 {
51026   gchar *errstr, *hash;
51027 
51028   assert (name && uuid);
51029 
51030   if ((errstr = gvm_validate_password (password, name)))
51031     {
51032       g_warning ("new password for '%s' rejected: %s", name, errstr);
51033       if (r_errdesc)
51034         *r_errdesc = errstr;
51035       else
51036         g_free (errstr);
51037       return -1;
51038     }
51039   hash = manage_authentication_hash (password);
51040   sql ("UPDATE users SET password = '%s', modification_time = m_now ()"
51041        " WHERE uuid = '%s';",
51042        hash,
51043        uuid);
51044   g_free (hash);
51045   return 0;
51046 }
51047 
51048 /**
51049  * @brief Set the password of a user.
51050  *
51051  * @param[in]  log_config      Log configuration.
51052  * @param[in]  database  Location of manage database.
51053  * @param[in]  name      Name of user.
51054  * @param[in]  password  New password.
51055  *
51056  * @return 0 success, -1 error.
51057  */
51058 int
manage_set_password(GSList * log_config,const db_conn_info_t * database,const gchar * name,const gchar * password)51059 manage_set_password (GSList *log_config, const db_conn_info_t *database,
51060                      const gchar *name, const gchar *password)
51061 {
51062   user_t user;
51063   char *uuid;
51064   int ret;
51065   gchar *rejection_msg;
51066 
51067   g_info ("   Modifying user password.");
51068 
51069   if (name == NULL)
51070     {
51071       fprintf (stderr, "--user required.\n");
51072       return -1;
51073     }
51074 
51075   ret = manage_option_setup (log_config, database);
51076   if (ret)
51077     return ret;
51078 
51079   sql_begin_immediate ();
51080 
51081   if (find_user_by_name (name, &user))
51082     {
51083       fprintf (stderr, "Internal error.\n");
51084       goto fail;
51085     }
51086 
51087   if (user == 0)
51088     {
51089       fprintf (stderr, "Failed to find user.\n");
51090       goto fail;
51091     }
51092 
51093   uuid = user_uuid (user);
51094   if (uuid == NULL)
51095     {
51096       fprintf (stderr, "Failed to allocate UUID.\n");
51097       goto fail;
51098     }
51099 
51100   rejection_msg = NULL;
51101   if (set_password (name, uuid, password, &rejection_msg))
51102     {
51103       if (rejection_msg)
51104         {
51105           fprintf (stderr, "New password rejected: %s\n", rejection_msg);
51106           g_free (rejection_msg);
51107         }
51108       else
51109         fprintf (stderr, "New password rejected.\n");
51110       free (uuid);
51111       goto fail;
51112     }
51113 
51114   sql_commit ();
51115   free (uuid);
51116   manage_option_cleanup ();
51117   return ret;
51118 
51119  fail:
51120   sql_rollback ();
51121   manage_option_cleanup ();
51122   return -1;
51123 }
51124 
51125 /**
51126  * @brief Find a user for a specific permission, given a UUID.
51127  *
51128  * @param[in]   uuid        UUID of user.
51129  * @param[out]  user        User return, 0 if successfully failed to find user.
51130  * @param[in]   permission  Permission.
51131  *
51132  * @return FALSE on success (including if failed to find user), TRUE on error.
51133  */
51134 static gboolean
find_user_with_permission(const char * uuid,user_t * user,const char * permission)51135 find_user_with_permission (const char* uuid, user_t* user,
51136                            const char *permission)
51137 {
51138   return find_resource_with_permission ("user", uuid, user, permission, 0);
51139 }
51140 
51141 /**
51142  * @brief Find a user given a name.
51143  *
51144  * @param[in]   name  A user name.
51145  * @param[out]  user  User return, 0 if successfully failed to find user.
51146  * @param[in]   permission  Permission.
51147  *
51148  * @return FALSE on success (including if failed to find user), TRUE on error.
51149  */
51150 gboolean
find_user_by_name_with_permission(const char * name,user_t * user,const char * permission)51151 find_user_by_name_with_permission (const char* name, user_t *user,
51152                                    const char *permission)
51153 {
51154   return find_resource_by_name_with_permission ("user", name, user, permission);
51155 }
51156 
51157 /**
51158  * @brief Find a user given a name.
51159  *
51160  * @param[in]   name  A user name.
51161  * @param[out]  user  User return, 0 if successfully failed to find user.
51162  *
51163  * @return FALSE on success (including if failed to find user), TRUE on error.
51164  */
51165 static gboolean
find_user_by_name(const char * name,user_t * user)51166 find_user_by_name (const char* name, user_t *user)
51167 {
51168   return find_resource_by_name ("user", name, user);
51169 }
51170 
51171 /**
51172  * @brief Adds a new user to the GVM installation.
51173  *
51174  * @todo Adding users authenticating with certificates is not yet implemented.
51175  *
51176  * @param[in]  name         The name of the new user.
51177  * @param[in]  password     The password of the new user.
51178  * @param[in]  comment      Comment for the new user or NULL.
51179  * @param[in]  hosts        The host the user is allowed/forbidden to scan.
51180  * @param[in]  hosts_allow  Whether hosts is allow or forbid.
51181  * @param[in]  ifaces       Interfaces the user is allowed/forbidden to scan.
51182  * @param[in]  ifaces_allow     Whether ifaces is allow or forbid.
51183  * @param[in]  allowed_methods  Allowed login methods.
51184  * @param[in]  groups       Groups.
51185  * @param[out] group_id_return  ID of group on "failed to find" error.
51186  * @param[in]  roles        Roles.
51187  * @param[out] role_id_return  ID of role on "failed to find" error.
51188  * @param[out] r_errdesc    If not NULL the address of a variable to receive
51189  *                          a malloced string with the error description.  Will
51190  *                          always be set to NULL on success.
51191  * @param[out] new_user     Created user.
51192  * @param[in]  forbid_super_admin  Whether to forbid creation of Super Admin.
51193  *
51194  * @return 0 if the user has been added successfully, 1 failed to find group,
51195  *         2 failed to find role, 3 syntax error in hosts, 99 permission denied,
51196  *         -1 on error, -2 if user exists already.
51197  */
51198 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)51199 create_user (const gchar * name, const gchar * password, const gchar *comment,
51200              const gchar * hosts, int hosts_allow,
51201              const gchar *ifaces, int ifaces_allow,
51202              const array_t * allowed_methods, array_t *groups,
51203              gchar **group_id_return, array_t *roles, gchar **role_id_return,
51204              gchar **r_errdesc, user_t *new_user, int forbid_super_admin)
51205 {
51206   char *errstr, *uuid;
51207   gchar *quoted_hosts, *quoted_ifaces, *quoted_method, *quoted_name, *hash;
51208   gchar *quoted_comment, *clean, *generated;
51209   int index, max, ret;
51210   user_t user;
51211   GArray *cache_users;
51212 
51213   assert (name);
51214   assert (password);
51215 
51216   if (r_errdesc)
51217     *r_errdesc = NULL;
51218 
51219   /* allowed_methods is a NULL terminated array. */
51220   if (allowed_methods && (allowed_methods->len > 2))
51221     return -3;
51222 
51223   if (allowed_methods && (allowed_methods->len == 0))
51224     allowed_methods = NULL;
51225 
51226   // TODO validate methods  single source, one of ldap, ...
51227 
51228   if (validate_username (name) != 0)
51229     {
51230       g_warning ("Invalid characters in user name!");
51231       if (r_errdesc)
51232         *r_errdesc = g_strdup ("Invalid characters in user name");
51233       return -1;
51234     }
51235 
51236   if (allowed_methods &&
51237       (!strcmp (g_ptr_array_index (allowed_methods, 0), "ldap_connect")
51238        || !strcmp (g_ptr_array_index (allowed_methods, 0), "radius_connect")))
51239     password = generated = gvm_uuid_make ();
51240   else
51241     generated = NULL;
51242 
51243   if ((errstr = gvm_validate_password (password, name)))
51244     {
51245       g_warning ("new password for '%s' rejected: %s", name, errstr);
51246       if (r_errdesc)
51247         *r_errdesc = errstr;
51248       else
51249         g_free (errstr);
51250       g_free (generated);
51251       return -1;
51252     }
51253 
51254   sql_begin_immediate ();
51255 
51256   if (acl_user_may ("create_user") == 0)
51257     {
51258       sql_rollback ();
51259       g_free (generated);
51260       return 99;
51261     }
51262 
51263   /* Check if user exists already. */
51264 
51265   if (resource_with_name_exists_global (name, "user", 0))
51266     {
51267       sql_rollback ();
51268       g_free (generated);
51269       return -2;
51270     }
51271   quoted_name = sql_quote (name);
51272 
51273   /* Check hosts. */
51274 
51275   max = manage_max_hosts ();
51276   manage_set_max_hosts (MANAGE_USER_MAX_HOSTS);
51277   if (hosts && (manage_count_hosts (hosts, NULL) < 0))
51278     {
51279       manage_set_max_hosts (max);
51280       sql_rollback ();
51281       g_free (generated);
51282       return 3;
51283     }
51284   manage_set_max_hosts (max);
51285 
51286   /* Get the password hashes. */
51287 
51288   hash = manage_authentication_hash (password);
51289 
51290   /* Get the quoted comment */
51291 
51292   if (comment)
51293     quoted_comment = sql_quote (comment);
51294   else
51295     quoted_comment = g_strdup ("");
51296 
51297   /* Add the user to the database. */
51298 
51299   clean = clean_hosts (hosts ? hosts : "", &max);
51300   quoted_hosts = sql_quote (clean);
51301   quoted_ifaces = sql_quote (ifaces ? ifaces : "");
51302   g_free (clean);
51303   quoted_method = sql_quote (allowed_methods
51304                               ? g_ptr_array_index (allowed_methods, 0)
51305                               : "file");
51306 
51307   ret
51308     = sql_error ("INSERT INTO users"
51309                  " (uuid, owner, name, password, comment, hosts, hosts_allow,"
51310                  "  ifaces, ifaces_allow, method, creation_time,"
51311                  "  modification_time)"
51312                  " VALUES"
51313                  " (make_uuid (),"
51314                  "  (SELECT id FROM users WHERE uuid = '%s'),"
51315                  "  '%s', '%s', '%s', '%s', %i,"
51316                  "  '%s', %i, '%s', m_now (),"
51317                  "  m_now ());",
51318                  current_credentials.uuid,
51319                  quoted_name,
51320                  hash,
51321                  quoted_comment,
51322                  quoted_hosts,
51323                  hosts_allow,
51324                  quoted_ifaces,
51325                  ifaces_allow,
51326                  quoted_method);
51327   g_free (generated);
51328   g_free (hash);
51329   g_free (quoted_comment);
51330   g_free (quoted_hosts);
51331   g_free (quoted_ifaces);
51332   g_free (quoted_method);
51333   g_free (quoted_name);
51334 
51335   if (ret == 3)
51336     {
51337       sql_rollback ();
51338       return -2;
51339     }
51340   else if (ret)
51341     {
51342       sql_rollback ();
51343       return -1;
51344     }
51345 
51346   user = sql_last_insert_id ();
51347 
51348   /* Add the user to any given groups. */
51349 
51350   index = 0;
51351   while (groups && (index < groups->len))
51352     {
51353       gchar *group_id;
51354       group_t group;
51355 
51356       group_id = (gchar*) g_ptr_array_index (groups, index);
51357       if (strcmp (group_id, "0") == 0)
51358         {
51359           index++;
51360           continue;
51361         }
51362 
51363       if (find_group_with_permission (group_id, &group, "modify_group"))
51364         {
51365           sql_rollback ();
51366           return -1;
51367         }
51368 
51369       if (group == 0)
51370         {
51371           sql_rollback ();
51372           if (group_id_return) *group_id_return = group_id;
51373           return 1;
51374         }
51375 
51376       sql ("INSERT INTO group_users (\"group\", \"user\") VALUES (%llu, %llu);",
51377            group,
51378            user);
51379 
51380       index++;
51381     }
51382 
51383   /* Add the user to any given roles. */
51384 
51385   index = 0;
51386   while (roles && (index < roles->len))
51387     {
51388       gchar *role_id;
51389       role_t role;
51390 
51391       role_id = (gchar*) g_ptr_array_index (roles, index);
51392       if (strcmp (role_id, "0") == 0)
51393         {
51394           index++;
51395           continue;
51396         }
51397 
51398       if (forbid_super_admin && acl_role_can_super_everyone (role_id))
51399         {
51400           sql_rollback ();
51401           return 99;
51402         }
51403 
51404       if (find_role_with_permission (role_id, &role, "get_roles"))
51405         {
51406           sql_rollback ();
51407           return -1;
51408         }
51409 
51410       if (role == 0)
51411         {
51412           sql_rollback ();
51413           if (role_id_return) *role_id_return = role_id;
51414           return 2;
51415         }
51416 
51417       sql ("INSERT INTO role_users (role, \"user\") VALUES (%llu, %llu);",
51418            role,
51419            user);
51420 
51421       index++;
51422     }
51423 
51424   if (new_user)
51425     *new_user = user;
51426 
51427   /* Ensure the user can see themself. */
51428 
51429   uuid = user_uuid (user);
51430   if (uuid == NULL)
51431     {
51432       g_warning ("%s: Failed to allocate UUID", __func__);
51433       sql_rollback ();
51434       return -1;
51435     }
51436 
51437   create_permission_internal (1,
51438                               "GET_USERS",
51439                               "Automatically created when adding user",
51440                               NULL,
51441                               uuid,
51442                               "user",
51443                               uuid,
51444                               NULL);
51445 
51446   free (uuid);
51447 
51448   /* Cache permissions. */
51449 
51450   cache_users = g_array_new (TRUE, TRUE, sizeof (user_t));
51451   g_array_append_val (cache_users, user);
51452   cache_all_permissions_for_users (cache_users);
51453   g_free (g_array_free (cache_users, TRUE));
51454 
51455   sql_commit ();
51456   return 0;
51457 }
51458 
51459 /**
51460  * @brief Create a user from an existing user.
51461  *
51462  * @param[in]  name      Name of new user.  NULL to copy from existing.
51463  * @param[in]  comment   Comment on new user.  NULL to copy from existing.
51464  * @param[in]  user_id   UUID of existing user.
51465  * @param[out] new_user  New user.
51466  *
51467  * @return 0 success, 1 user exists already, 2 failed to find existing
51468  *         user, 99 permission denied, -1 error.
51469  */
51470 int
copy_user(const char * name,const char * comment,const char * user_id,user_t * new_user)51471 copy_user (const char* name, const char* comment, const char *user_id,
51472            user_t* new_user)
51473 {
51474   user_t user;
51475   int ret;
51476   gchar *quoted_uuid;
51477   GArray *cache_users;
51478   char *uuid;
51479 
51480   if (acl_user_can_super_everyone (user_id))
51481     return 99;
51482 
51483   sql_begin_immediate ();
51484 
51485   ret = copy_resource_lock ("user", name, comment, user_id,
51486                             "password, timezone, hosts, hosts_allow,"
51487                             " ifaces, ifaces_allow, method",
51488                             1, &user, NULL);
51489   if (ret)
51490     {
51491       sql_rollback ();
51492       return ret;
51493     }
51494 
51495   sql ("UPDATE users SET password = NULL WHERE id = %llu;", user);
51496 
51497   quoted_uuid = sql_quote (user_id);
51498 
51499   sql ("INSERT INTO group_users (\"user\", \"group\")"
51500        " SELECT %llu, \"group\" FROM group_users"
51501        " WHERE \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
51502        user,
51503        quoted_uuid);
51504 
51505   sql ("INSERT INTO role_users (\"user\", role)"
51506        " SELECT %llu, role FROM role_users"
51507        " WHERE \"user\" = (SELECT id FROM users WHERE uuid = '%s');",
51508        user,
51509        quoted_uuid);
51510 
51511   g_free (quoted_uuid);
51512 
51513   /* Ensure the user can see themself. */
51514 
51515   uuid = user_uuid (user);
51516   if (uuid == NULL)
51517     {
51518       g_warning ("%s: Failed to allocate UUID", __func__);
51519       sql_rollback ();
51520       return -1;
51521     }
51522 
51523   create_permission_internal (1,
51524                               "GET_USERS",
51525                               "Automatically created when adding user",
51526                               NULL,
51527                               uuid,
51528                               "user",
51529                               uuid,
51530                               NULL);
51531 
51532   free (uuid);
51533 
51534   /* Cache permissions. */
51535 
51536   cache_users = g_array_new (TRUE, TRUE, sizeof (user_t));
51537   g_array_append_val (cache_users, user);
51538   cache_all_permissions_for_users (cache_users);
51539   g_free (g_array_free (cache_users, TRUE));
51540 
51541   sql_commit ();
51542 
51543   if (new_user)
51544     *new_user = user;
51545 
51546   return ret;
51547 }
51548 
51549 /**
51550  * @brief Delete a user.
51551  *
51552  * @param[in]  user_id_arg  UUID of user.
51553  * @param[in]  name_arg     Name of user.  Overridden by user_id.
51554  * @param[in]  ultimate     Whether to remove entirely, or to trashcan.
51555  * @param[in]  forbid_super_admin  Whether to forbid removal of Super Admin.
51556  * @param[in]  inheritor_id   UUID of user who will inherit owned objects.
51557  * @param[in]  inheritor_name Name of user who will inherit owned objects.
51558  *
51559  * @return 0 success, 2 failed to find user, 4 user has active tasks,
51560  *         5 attempted suicide, 6 inheritor not found, 7 inheritor same as
51561  *         deleted user, 8 invalid inheritor, 9 resources still in use,
51562  *         10 user is 'Feed Import Owner' 99 permission denied, -1 error.
51563  */
51564 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)51565 delete_user (const char *user_id_arg, const char *name_arg, int ultimate,
51566              int forbid_super_admin,
51567              const char* inheritor_id, const char *inheritor_name)
51568 {
51569   iterator_t tasks;
51570   user_t user, inheritor;
51571   get_data_t get;
51572   char *current_uuid, *feed_owner_id;
51573   gboolean has_rows;
51574   iterator_t rows;
51575   gchar *deleted_user_id;
51576 
51577   assert (user_id_arg || name_arg);
51578 
51579   if (current_credentials.username && current_credentials.uuid)
51580     {
51581       if (user_id_arg)
51582         {
51583           if (strcmp (user_id_arg, current_credentials.uuid) == 0)
51584             return 5;
51585         }
51586       else if (name_arg
51587                && (strcmp (name_arg, current_credentials.username) == 0))
51588         return 5;
51589     }
51590 
51591   sql_begin_immediate ();
51592 
51593   if (acl_user_may ("delete_user") == 0)
51594     {
51595       sql_rollback ();
51596       return 99;
51597     }
51598 
51599   user = 0;
51600   if (user_id_arg)
51601     {
51602       if (forbid_super_admin
51603           && (strcmp (user_id_arg, ROLE_UUID_SUPER_ADMIN) == 0))
51604         {
51605           sql_rollback ();
51606           return 99;
51607         }
51608 
51609       if (find_user_with_permission (user_id_arg, &user, "delete_user"))
51610         {
51611           sql_rollback ();
51612           return -1;
51613         }
51614     }
51615   else if (find_user_by_name_with_permission (name_arg, &user, "delete_user"))
51616     {
51617       sql_rollback ();
51618       return -1;
51619     }
51620 
51621   if (user == 0)
51622     return 2;
51623 
51624   setting_value (SETTING_UUID_FEED_IMPORT_OWNER, &feed_owner_id);
51625   if (feed_owner_id)
51626     {
51627       char *uuid;
51628 
51629       uuid = user_uuid (user);
51630       if (strcmp (uuid, feed_owner_id) == 0)
51631         {
51632           free (uuid);
51633           free (feed_owner_id);
51634           sql_rollback ();
51635           return 10;
51636         }
51637       free (feed_owner_id);
51638       free (uuid);
51639     }
51640 
51641   if (forbid_super_admin)
51642     {
51643       char *uuid;
51644 
51645       uuid = user_uuid (user);
51646       if (acl_user_is_super_admin (uuid))
51647         {
51648           free (uuid);
51649           sql_rollback ();
51650           return 99;
51651         }
51652       free (uuid);
51653     }
51654 
51655   /* Fail if there are any active tasks. */
51656 
51657   memset (&get, '\0', sizeof (get));
51658   current_uuid = current_credentials.uuid;
51659   current_credentials.uuid = sql_string ("SELECT uuid FROM users"
51660                                          " WHERE id = %llu;",
51661                                          user);
51662   init_user_task_iterator (&tasks, 0, 1);
51663   while (next (&tasks))
51664     switch (task_iterator_run_status (&tasks))
51665       {
51666         case TASK_STATUS_DELETE_REQUESTED:
51667         case TASK_STATUS_DELETE_ULTIMATE_REQUESTED:
51668         case TASK_STATUS_DELETE_ULTIMATE_WAITING:
51669         case TASK_STATUS_DELETE_WAITING:
51670         case TASK_STATUS_REQUESTED:
51671         case TASK_STATUS_RUNNING:
51672         case TASK_STATUS_QUEUED:
51673         case TASK_STATUS_STOP_REQUESTED:
51674         case TASK_STATUS_STOP_WAITING:
51675           {
51676             cleanup_iterator (&tasks);
51677             free (current_credentials.uuid);
51678             current_credentials.uuid = current_uuid;
51679             sql_rollback ();
51680             return 4;
51681           }
51682         default:
51683           break;
51684       }
51685   cleanup_iterator (&tasks);
51686   free (current_credentials.uuid);
51687   current_credentials.uuid = current_uuid;
51688 
51689   /* Check if there's an inheritor. */
51690 
51691   if (inheritor_id && strcmp (inheritor_id, ""))
51692     {
51693       if (strcmp (inheritor_id, "self") == 0)
51694         {
51695           sql_int64 (&inheritor, "SELECT id FROM users WHERE uuid = '%s'",
51696                      current_credentials.uuid);
51697 
51698           if (inheritor == 0)
51699             {
51700               sql_rollback ();
51701               return -1;
51702             }
51703         }
51704       else
51705         {
51706           if (find_user_with_permission (inheritor_id, &inheritor, "get_users"))
51707             {
51708               sql_rollback ();
51709               return -1;
51710             }
51711 
51712           if (inheritor == 0)
51713             {
51714               sql_rollback ();
51715               return 6;
51716             }
51717         }
51718     }
51719   else if (inheritor_name && strcmp (inheritor_name, ""))
51720     {
51721       if (find_user_by_name_with_permission (inheritor_name, &inheritor,
51722                                              "get_users"))
51723         {
51724           sql_rollback ();
51725           return -1;
51726         }
51727 
51728       if (inheritor == 0)
51729         {
51730           sql_rollback ();
51731           return 6;
51732         }
51733     }
51734   else
51735     inheritor = 0;
51736 
51737   if (inheritor)
51738     {
51739       gchar *deleted_user_name;
51740       gchar *real_inheritor_id, *real_inheritor_name;
51741 
51742       /* Transfer ownership of objects to the inheritor. */
51743 
51744       if (inheritor == user)
51745         {
51746           sql_rollback ();
51747           return 7;
51748         }
51749 
51750       real_inheritor_id = user_uuid (inheritor);
51751 
51752       /* Only the current user, owned users or global users may inherit. */
51753       if (strcmp (real_inheritor_id, current_credentials.uuid)
51754           && sql_int ("SELECT NOT (" ACL_IS_GLOBAL () ")"
51755                       " FROM users WHERE id = %llu",
51756                       inheritor)
51757           && ! acl_user_owns ("user", inheritor, 0)
51758           && sql_int ("SELECT owner != 0 FROM users WHERE id = %llu",
51759                       inheritor))
51760         {
51761           g_free (real_inheritor_id);
51762           sql_rollback ();
51763           return 8;
51764         }
51765 
51766       deleted_user_id = user_uuid (user);
51767       deleted_user_name = user_name (deleted_user_id);
51768       real_inheritor_name = user_name (real_inheritor_id);
51769 
51770       g_log ("event user", G_LOG_LEVEL_MESSAGE,
51771              "User %s (%s) is inheriting from %s (%s)",
51772              real_inheritor_name, real_inheritor_id,
51773              deleted_user_name, deleted_user_id);
51774 
51775       g_free (deleted_user_name);
51776       g_free (real_inheritor_id);
51777       g_free (real_inheritor_name);
51778 
51779       /* Transfer owned resources. */
51780 
51781       sql ("UPDATE alerts SET owner = %llu WHERE owner = %llu;",
51782            inheritor, user);
51783       sql ("UPDATE alerts_trash SET owner = %llu WHERE owner = %llu;",
51784            inheritor, user);
51785       sql ("UPDATE configs SET owner = %llu WHERE owner = %llu;",
51786            inheritor, user);
51787       sql ("UPDATE configs_trash SET owner = %llu WHERE owner = %llu;",
51788            inheritor, user);
51789       sql ("UPDATE credentials SET owner = %llu WHERE owner = %llu;",
51790            inheritor, user);
51791       sql ("UPDATE credentials_trash SET owner = %llu WHERE owner = %llu;",
51792            inheritor, user);
51793       sql ("UPDATE host_identifiers SET owner = %llu WHERE owner = %llu;",
51794            inheritor, user);
51795       sql ("UPDATE host_oss SET owner = %llu WHERE owner = %llu;",
51796            inheritor, user);
51797       sql ("UPDATE hosts SET owner = %llu WHERE owner = %llu;",
51798            inheritor, user);
51799       sql ("UPDATE filters SET owner = %llu WHERE owner = %llu;",
51800            inheritor, user);
51801       sql ("UPDATE filters_trash SET owner = %llu WHERE owner = %llu;",
51802            inheritor, user);
51803       sql ("UPDATE notes SET owner = %llu WHERE owner = %llu;",
51804            inheritor, user);
51805       sql ("UPDATE notes_trash SET owner = %llu WHERE owner = %llu;",
51806            inheritor, user);
51807       sql ("UPDATE oss SET owner = %llu WHERE owner = %llu;",
51808            inheritor, user);
51809       sql ("UPDATE permissions SET owner = %llu WHERE owner = %llu",
51810            inheritor, user);
51811 
51812       inherit_port_lists (user, inheritor);
51813 
51814       sql ("UPDATE reports SET owner = %llu WHERE owner = %llu;",
51815            inheritor, user);
51816       sql ("UPDATE report_counts SET \"user\" = %llu WHERE \"user\" = %llu",
51817            inheritor, user);
51818       sql ("UPDATE reports SET owner = %llu WHERE owner = %llu;",
51819            inheritor, user);
51820       sql ("UPDATE results SET owner = %llu WHERE owner = %llu;",
51821            inheritor, user);
51822       sql ("UPDATE results_trash SET owner = %llu WHERE owner = %llu;",
51823            inheritor, user);
51824 
51825       sql ("UPDATE overrides SET owner = %llu WHERE owner = %llu;",
51826            inheritor, user);
51827       sql ("UPDATE overrides_trash SET owner = %llu WHERE owner = %llu;",
51828            inheritor, user);
51829       sql ("UPDATE permissions SET owner = %llu WHERE owner = %llu;",
51830            inheritor, user);
51831       sql ("UPDATE permissions_trash SET owner = %llu WHERE owner = %llu;",
51832            inheritor, user);
51833       sql ("UPDATE scanners SET owner = %llu WHERE owner = %llu;",
51834            inheritor, user);
51835       sql ("UPDATE scanners_trash SET owner = %llu WHERE owner = %llu;",
51836            inheritor, user);
51837       sql ("UPDATE schedules SET owner = %llu WHERE owner = %llu;",
51838            inheritor, user);
51839       sql ("UPDATE schedules_trash SET owner = %llu WHERE owner = %llu;",
51840            inheritor, user);
51841       sql ("DELETE FROM tag_resources"
51842            " WHERE resource_type = 'user' AND resource = %llu;",
51843            user);
51844       sql ("UPDATE tags SET owner = %llu WHERE owner = %llu;",
51845            inheritor, user);
51846       sql ("DELETE FROM tag_resources_trash"
51847            " WHERE resource_type = 'user' AND resource = %llu;",
51848            user);
51849       sql ("UPDATE tags_trash SET owner = %llu WHERE owner = %llu;",
51850            inheritor, user);
51851       sql ("UPDATE targets SET owner = %llu WHERE owner = %llu;",
51852            inheritor, user);
51853       sql ("UPDATE targets_trash SET owner = %llu WHERE owner = %llu;",
51854            inheritor, user);
51855 
51856       sql ("UPDATE tasks SET owner = %llu WHERE owner = %llu;",
51857            inheritor, user);
51858 
51859       inherit_tickets (user, inheritor);
51860       inherit_tls_certificates (user, inheritor);
51861 
51862       sql ("UPDATE groups SET owner = %llu WHERE owner = %llu;",
51863            inheritor, user);
51864       sql ("UPDATE roles SET owner = %llu WHERE owner = %llu;",
51865            inheritor, user);
51866       sql ("UPDATE users SET owner = %llu WHERE owner = %llu;",
51867            inheritor, user);
51868       sql ("UPDATE groups_trash SET owner = %llu WHERE owner = %llu;",
51869            inheritor, user);
51870       sql ("UPDATE roles_trash SET owner = %llu WHERE owner = %llu;",
51871            inheritor, user);
51872 
51873       /* Report Formats. */
51874 
51875       has_rows = inherit_report_formats (user, inheritor, &rows);
51876 
51877       /* Delete user. */
51878 
51879       sql ("DELETE FROM group_users WHERE \"user\" = %llu;", user);
51880       sql ("DELETE FROM group_users_trash WHERE \"user\" = %llu;", user);
51881       sql ("DELETE FROM role_users WHERE \"user\" = %llu;", user);
51882       sql ("DELETE FROM role_users_trash WHERE \"user\" = %llu;", user);
51883 
51884       delete_permissions_cache_for_user (user);
51885 
51886       sql ("DELETE FROM settings WHERE owner = %llu;", user);
51887       sql ("DELETE FROM users WHERE id = %llu;", user);
51888 
51889       /* Very last: report formats dirs. */
51890 
51891       if (deleted_user_id == NULL)
51892         g_warning ("%s: deleted_user_id NULL, skipping dirs", __func__);
51893       else if (has_rows)
51894         do
51895         {
51896           inherit_report_format_dir (iterator_string (&rows, 0),
51897                                      deleted_user_id,
51898                                      inheritor);
51899         } while (next (&rows));
51900 
51901       g_free (deleted_user_id);
51902       cleanup_iterator (&rows);
51903 
51904       sql_commit ();
51905 
51906       return 0;
51907     }
51908 
51909   /* Delete settings and miscellaneous resources not referenced directly. */
51910 
51911   /* Settings. */
51912   sql ("DELETE FROM settings WHERE owner = %llu;", user);
51913 
51914   /* Delete data modifiers (not directly referenced) */
51915 
51916   /* Notes. */
51917   sql ("DELETE FROM notes WHERE owner = %llu;", user);
51918   sql ("DELETE FROM notes_trash WHERE owner = %llu;", user);
51919 
51920   /* Overrides. */
51921   sql ("DELETE FROM overrides WHERE owner = %llu;", user);
51922   sql ("DELETE FROM overrides_trash WHERE owner = %llu;", user);
51923 
51924   /* Tags. */
51925   sql ("DELETE FROM tag_resources"
51926        " WHERE resource_type = 'user' AND resource = %llu;",
51927        user);
51928   sql ("DELETE FROM tag_resources"
51929        " WHERE tag IN (SELECT id FROM tags WHERE owner = %llu);",
51930        user);
51931   sql ("DELETE FROM tags WHERE owner = %llu;", user);
51932   sql ("DELETE FROM tag_resources_trash"
51933        " WHERE resource_type = 'user' AND resource = %llu;",
51934        user);
51935   sql ("DELETE FROM tag_resources_trash"
51936        " WHERE tag IN (SELECT id FROM tags_trash WHERE owner = %llu);",
51937        user);
51938   sql ("DELETE FROM tags_trash WHERE owner = %llu;", user);
51939 
51940   delete_tickets_user (user);
51941 
51942   delete_tls_certificates_user (user);
51943 
51944   /* Delete assets (not directly referenced). */
51945 
51946   /* Hosts. */
51947   sql ("DELETE FROM host_details WHERE host IN"
51948        " (SELECT id FROM hosts WHERE owner = %llu);", user);
51949   sql ("DELETE FROM host_max_severities WHERE host IN"
51950        " (SELECT id FROM hosts WHERE owner = %llu);", user);
51951   sql ("DELETE FROM host_identifiers WHERE owner = %llu;", user);
51952   sql ("DELETE FROM host_oss WHERE owner = %llu;", user);
51953   sql ("DELETE FROM hosts WHERE owner = %llu;", user);
51954 
51955   /* OSs. */
51956   sql ("DELETE FROM oss WHERE owner = %llu;", user);
51957 
51958   /* Delete report data and tasks (not directly referenced). */
51959 
51960   /* Counts. */
51961   sql ("DELETE FROM report_counts WHERE \"user\" = %llu", user);
51962   sql ("DELETE FROM report_counts"
51963        " WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
51964        user);
51965 
51966   /* Hosts. */
51967   sql ("DELETE FROM report_host_details"
51968        " WHERE report_host IN (SELECT id FROM report_hosts"
51969        "                       WHERE report IN (SELECT id FROM reports"
51970        "                                        WHERE owner = %llu));",
51971        user);
51972   sql ("DELETE FROM report_hosts"
51973        " WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
51974        user);
51975 
51976   /* Results. */
51977   sql ("DELETE FROM results"
51978        " WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
51979        user);
51980   sql ("DELETE FROM results_trash"
51981        " WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
51982        user);
51983 
51984   /* Reports. */
51985   sql ("DELETE FROM result_nvt_reports"
51986        " WHERE report IN (SELECT id FROM reports WHERE owner = %llu);",
51987        user);
51988   sql ("DELETE FROM reports WHERE owner = %llu;", user);
51989 
51990   /* Delete tasks (not directly referenced). */
51991 
51992   if (user_resources_in_use (user,
51993                              "tasks", target_in_use,
51994                              NULL, NULL))
51995     {
51996       sql_rollback ();
51997       return 9;
51998     }
51999   tickets_remove_tasks_user (user);
52000   sql ("DELETE FROM task_alerts"
52001        " WHERE task IN (SELECT id FROM tasks WHERE owner = %llu);",
52002        user);
52003   sql ("DELETE FROM task_files"
52004        " WHERE task IN (SELECT id FROM tasks WHERE owner = %llu);",
52005        user);
52006   sql ("DELETE FROM task_preferences"
52007        " WHERE task IN (SELECT id FROM tasks WHERE owner = %llu);",
52008        user);
52009   sql ("DELETE FROM tasks WHERE owner = %llu;", user);
52010 
52011   /* Delete resources directly used by tasks. */
52012 
52013   /* Alerts. */
52014   if (user_resources_in_use (user,
52015                              "alerts", alert_in_use,
52016                              "alerts_trash", trash_alert_in_use))
52017     {
52018       sql_rollback ();
52019       return 9;
52020     }
52021   sql ("DELETE FROM alert_condition_data"
52022        " WHERE alert IN (SELECT id FROM alerts WHERE owner = %llu);",
52023        user);
52024   sql ("DELETE FROM alert_condition_data_trash"
52025        " WHERE alert IN (SELECT id FROM alerts_trash WHERE owner = %llu);",
52026        user);
52027   sql ("DELETE FROM alert_event_data"
52028        " WHERE alert IN (SELECT id FROM alerts WHERE owner = %llu);",
52029        user);
52030   sql ("DELETE FROM alert_event_data_trash"
52031        " WHERE alert IN (SELECT id FROM alerts_trash WHERE owner = %llu);",
52032        user);
52033   sql ("DELETE FROM alert_method_data"
52034        " WHERE alert IN (SELECT id FROM alerts WHERE owner = %llu);",
52035        user);
52036   sql ("DELETE FROM alert_method_data_trash"
52037        " WHERE alert IN (SELECT id FROM alerts_trash WHERE owner = %llu);",
52038        user);
52039   sql ("DELETE FROM alerts WHERE owner = %llu;", user);
52040   sql ("DELETE FROM alerts_trash WHERE owner = %llu;", user);
52041 
52042   /* Configs. */
52043   if (user_resources_in_use (user,
52044                              "configs", config_in_use,
52045                              "configs_trash", trash_config_in_use))
52046     {
52047       sql_rollback ();
52048       return 9;
52049     }
52050   sql ("DELETE FROM nvt_selectors"
52051        " WHERE name IN (SELECT nvt_selector FROM configs WHERE owner = %llu)"
52052        " AND name != '" MANAGE_NVT_SELECTOR_UUID_ALL "';",
52053        user);
52054   sql ("DELETE FROM config_preferences"
52055        " WHERE config IN (SELECT id FROM configs WHERE owner = %llu);",
52056        user);
52057   sql ("DELETE FROM config_preferences_trash"
52058        " WHERE config IN (SELECT id FROM configs_trash WHERE owner = %llu);",
52059        user);
52060   sql ("DELETE FROM configs WHERE owner = %llu;", user);
52061   sql ("DELETE FROM configs_trash WHERE owner = %llu;", user);
52062 
52063   /* Scanners. */
52064   if (user_resources_in_use (user,
52065                              "scanners", scanner_in_use,
52066                              "scanners_trash", trash_scanner_in_use))
52067     {
52068       sql_rollback ();
52069       return 9;
52070     }
52071   sql ("DELETE FROM scanners WHERE owner = %llu;", user);
52072   sql ("DELETE FROM scanners_trash WHERE owner = %llu;", user);
52073 
52074   /* Schedules. */
52075   if (user_resources_in_use (user,
52076                              "schedules", schedule_in_use,
52077                              "schedules_trash", trash_schedule_in_use))
52078     {
52079       sql_rollback ();
52080       return 9;
52081     }
52082   sql ("DELETE FROM schedules WHERE owner = %llu;", user);
52083   sql ("DELETE FROM schedules_trash WHERE owner = %llu;", user);
52084 
52085   /* Targets. */
52086   if (user_resources_in_use (user,
52087                              "targets", target_in_use,
52088                              "targets_trash", trash_target_in_use))
52089     {
52090       sql_rollback ();
52091       return 9;
52092     }
52093   sql ("DELETE FROM targets_login_data WHERE target IN"
52094        " (SELECT id FROM targets WHERE owner = %llu);", user);
52095   sql ("DELETE FROM targets_trash_login_data WHERE target IN"
52096        " (SELECT id FROM targets_trash WHERE owner = %llu);", user);
52097   sql ("DELETE FROM targets WHERE owner = %llu;", user);
52098   sql ("DELETE FROM targets_trash WHERE owner = %llu;", user);
52099 
52100   /* Delete resources used indirectly by tasks */
52101 
52102   /* Filters (used by alerts and settings). */
52103   if (user_resources_in_use (user,
52104                              "filters", filter_in_use,
52105                              "filters_trash", trash_filter_in_use))
52106     {
52107       sql_rollback ();
52108       return 9;
52109     }
52110   sql ("DELETE FROM filters WHERE owner = %llu;", user);
52111   sql ("DELETE FROM filters_trash WHERE owner = %llu;", user);
52112 
52113   /* Port lists (used by targets). */
52114   if (user_resources_in_use (user,
52115                              "port_lists", port_list_in_use,
52116                              "port_lists_trash", trash_port_list_in_use))
52117     {
52118       sql_rollback ();
52119       return 9;
52120     }
52121   delete_port_lists_user (user);
52122 
52123   /* Check credentials before deleting report formats, because we can't
52124    * rollback the deletion of the report format dirs. */
52125   if (user_resources_in_use (user,
52126                              "credentials", credential_in_use,
52127                              "credentials_trash", trash_credential_in_use))
52128     {
52129       sql_rollback ();
52130       return 9;
52131     }
52132 
52133   /* Check report formats (used by alerts). */
52134   if (user_resources_in_use (user,
52135                              "report_formats",
52136                              report_format_in_use,
52137                              "report_formats_trash",
52138                              trash_report_format_in_use))
52139     {
52140       sql_rollback ();
52141       return 9;
52142     }
52143 
52144   /* Delete credentials last because they can be used in various places */
52145 
52146   sql ("DELETE FROM credentials_data WHERE credential IN"
52147        " (SELECT id FROM credentials WHERE owner = %llu);",
52148        user);
52149   sql ("DELETE FROM credentials_trash_data WHERE credential IN"
52150        " (SELECT id FROM credentials_trash WHERE owner = %llu);",
52151        user);
52152 
52153   sql ("DELETE FROM credentials WHERE owner = %llu;", user);
52154   sql ("DELETE FROM credentials_trash WHERE owner = %llu;", user);
52155 
52156   /* Make permissions global if they are owned by the user and are related
52157    * to users/groups/roles that are owned by the user. */
52158 
52159   sql ("UPDATE permissions SET owner = NULL"
52160        " WHERE owner = %llu"
52161        " AND ((subject_type = 'user' AND subject IN (SELECT id FROM users WHERE owner = %llu))"
52162        "      OR (subject_type = 'group' AND subject IN (SELECT id FROM groups WHERE owner = %llu))"
52163        "      OR (subject_type = 'role' AND subject IN (SELECT id FROM roles WHERE owner = %llu))"
52164        "      OR (resource_type = 'user' AND resource IN (SELECT id FROM users WHERE owner = %llu))"
52165        "      OR (resource_type = 'group' AND resource IN (SELECT id FROM groups WHERE owner = %llu))"
52166        "      OR (resource_type = 'role' AND resource IN (SELECT id FROM roles WHERE owner = %llu)));",
52167        user,
52168        user,
52169        user,
52170        user,
52171        user,
52172        user,
52173        user);
52174 
52175   /* Make users, roles and groups global if they are owned by the user. */
52176 
52177   sql ("UPDATE users SET owner = NULL WHERE owner = %llu;", user);
52178   sql ("UPDATE roles SET owner = NULL WHERE owner = %llu;", user);
52179   sql ("UPDATE groups SET owner = NULL WHERE owner = %llu;", user);
52180   sql ("UPDATE roles_trash SET owner = NULL WHERE owner = %llu;", user);
52181   sql ("UPDATE groups_trash SET owner = NULL WHERE owner = %llu;", user);
52182 
52183   /* Remove all other permissions owned by the user or given on the user. */
52184 
52185   sql ("DELETE FROM permissions"
52186        " WHERE owner = %llu"
52187        " OR subject_type = 'user' AND subject = %llu"
52188        " OR (resource_type = 'user' AND resource = %llu);",  /* For Super. */
52189        user,
52190        user,
52191        user);
52192   sql ("DELETE FROM permissions_get_tasks WHERE \"user\" = %llu;", user);
52193 
52194   /* Delete permissions granted by the user. */
52195 
52196   sql ("DELETE FROM permissions WHERE owner = %llu;", user);
52197   sql ("DELETE FROM permissions_trash WHERE owner = %llu;", user);
52198 
52199   /* Remove user from groups and roles. */
52200 
52201   sql ("DELETE FROM group_users WHERE \"user\" = %llu;", user);
52202   sql ("DELETE FROM group_users_trash WHERE \"user\" = %llu;", user);
52203   sql ("DELETE FROM role_users WHERE \"user\" = %llu;", user);
52204   sql ("DELETE FROM role_users_trash WHERE \"user\" = %llu;", user);
52205 
52206   /* Delete report formats. */
52207 
52208   has_rows = delete_report_formats_user (user, &rows);
52209 
52210   /* Delete user. */
52211 
52212   deleted_user_id = user_uuid (user);
52213 
52214   sql ("DELETE FROM users WHERE id = %llu;", user);
52215 
52216   /* Delete report format dirs. */
52217 
52218   if (deleted_user_id)
52219     delete_report_format_dirs_user (deleted_user_id, has_rows ? &rows : NULL);
52220   else
52221     g_warning ("%s: deleted_user_id NULL, skipping removal of report formats dir",
52222                __func__);
52223 
52224   sql_commit ();
52225   return 0;
52226 }
52227 
52228 /**
52229  * @brief Modify a user.
52230  *
52231  * @param[in]  user_id      The UUID of the user.  Overrides name.
52232  * @param[in]  name         The name of the user.  If NULL then set to name
52233  *                          when return is 3 or 4.
52234  * @param[in]  new_name     New name for the user.  NULL to leave as is.
52235  * @param[in]  password     The password of the user.  NULL to leave as is.
52236  * @param[in]  comment      The comment for the user.  NULL to leave as is.
52237  * @param[in]  hosts        The host the user is allowed/forbidden to scan.
52238  *                          NULL to leave as is.
52239  * @param[in]  hosts_allow  Whether hosts is allow or forbid.
52240  * @param[in]  ifaces       Interfaces the user is allowed/forbidden to scan.
52241  * @param[in]  ifaces_allow     Whether ifaces is allow or forbid.
52242  * @param[in]  allowed_methods  Allowed login methods.
52243  * @param[in]  groups           Groups.
52244  * @param[out] group_id_return  ID of group on "failed to find" error.
52245  * @param[in]  roles            Roles.
52246  * @param[out] role_id_return   ID of role on "failed to find" error.
52247  * @param[out] r_errdesc    If not NULL the address of a variable to receive
52248  *                          a malloced string with the error description.  Will
52249  *                          always be set to NULL on success.
52250  *
52251  * @return 0 if the user has been added successfully, 1 failed to find group,
52252  *         2 failed to find user, 3 success and user gained admin, 4 success
52253  *         and user lost admin, 5 failed to find role, 6 syntax error in hosts,
52254  *         7 syntax error in new name, 99 permission denied, -1 on error,
52255  *         -2 for an unknown role, -3 if wrong number of methods.
52256  */
52257 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)52258 modify_user (const gchar * user_id, gchar **name, const gchar *new_name,
52259              const gchar * password, const gchar * comment,
52260              const gchar * hosts, int hosts_allow,
52261              const gchar *ifaces, int ifaces_allow,
52262              const array_t * allowed_methods, array_t *groups,
52263              gchar **group_id_return, array_t *roles, gchar **role_id_return,
52264              gchar **r_errdesc)
52265 {
52266   char *errstr;
52267   gchar *hash, *quoted_hosts, *quoted_ifaces, *quoted_method, *clean, *uuid;
52268   gchar *quoted_new_name, *quoted_comment;
52269   user_t user;
52270   int max, was_admin, is_admin;
52271   GArray *cache_users;
52272 
52273   if (r_errdesc)
52274     *r_errdesc = NULL;
52275 
52276   /* allowed_methods is a NULL terminated array. */
52277   if (allowed_methods && (allowed_methods->len > 2))
52278     return -3;
52279 
52280   if (allowed_methods && (allowed_methods->len == 0))
52281     allowed_methods = NULL;
52282 
52283   if (allowed_methods
52284       && ((g_ptr_array_index (allowed_methods, 0) == NULL)
52285           || (strlen (g_ptr_array_index (allowed_methods, 0)) == 0)))
52286     allowed_methods = NULL;
52287 
52288   // TODO Validate methods: single source, one of "", "ldap", ...
52289 
52290   sql_begin_immediate ();
52291 
52292   if (acl_user_may ("modify_user") == 0)
52293     {
52294       sql_rollback ();
52295       return 99;
52296     }
52297 
52298   user = 0;
52299   if (user_id)
52300     {
52301       if (find_user_with_permission (user_id, &user, "modify_user"))
52302         {
52303           sql_rollback ();
52304           return -1;
52305         }
52306     }
52307   else if (find_user_by_name_with_permission (*name, &user, "modify_user"))
52308     {
52309       sql_rollback ();
52310       return -1;
52311     }
52312   if (user == 0)
52313     {
52314       sql_rollback ();
52315       return 2;
52316     }
52317 
52318   uuid = sql_string ("SELECT uuid FROM users WHERE id = %llu",
52319                      user);
52320 
52321   /* The only user that can edit a Super Admin is the Super Admin themself. */
52322   if (acl_user_can_super_everyone (uuid) && strcmp (uuid, current_credentials.uuid))
52323     {
52324       g_free (uuid);
52325       sql_rollback ();
52326       return 99;
52327     }
52328 
52329   was_admin = acl_user_is_admin (uuid);
52330 
52331   if (password)
52332     {
52333       char *user_name;
52334 
52335       user_name = sql_string ("SELECT name FROM users WHERE id = %llu",
52336                               user);
52337       errstr = gvm_validate_password (password, user_name);
52338       if (errstr)
52339         {
52340           g_warning ("new password for '%s' rejected: %s", user_name, errstr);
52341           if (r_errdesc)
52342             *r_errdesc = errstr;
52343           else
52344             g_free (errstr);
52345           sql_rollback ();
52346           g_free (user_name);
52347           return -1;
52348         }
52349       g_free (user_name);
52350     }
52351 
52352   /* Check hosts. */
52353 
52354   max = manage_max_hosts ();
52355   manage_set_max_hosts (MANAGE_USER_MAX_HOSTS);
52356   if (hosts && (manage_count_hosts (hosts, NULL) < 0))
52357     {
52358       manage_set_max_hosts (max);
52359       sql_rollback ();
52360       return 6;
52361     }
52362   manage_set_max_hosts (max);
52363 
52364   /* Check new name. */
52365 
52366   if (new_name)
52367     {
52368       if (validate_username (new_name) != 0)
52369         {
52370           sql_rollback ();
52371           return 7;
52372         }
52373 
52374       if (strcmp (uuid, current_credentials.uuid) == 0)
52375         {
52376           sql_rollback ();
52377           return 99;
52378         }
52379 
52380       if (resource_with_name_exists_global (new_name, "user", user))
52381         {
52382           sql_rollback ();
52383           return 8;
52384         }
52385       quoted_new_name = sql_quote (new_name);
52386     }
52387   else
52388     quoted_new_name = NULL;
52389 
52390   /* Get the password hashes. */
52391 
52392   if (password)
52393     hash = manage_authentication_hash (password);
52394   else
52395     hash = NULL;
52396 
52397   if (comment)
52398     {
52399       quoted_comment = sql_quote (comment);
52400     }
52401   else
52402     {
52403       quoted_comment = NULL;
52404     }
52405 
52406 
52407   /* Update the user in the database. */
52408 
52409   clean = clean_hosts (hosts ? hosts : "", &max);
52410   if ((hosts_allow == 0) && (max == 0))
52411     /* Convert "Deny none" to "Allow All". */
52412     hosts_allow = 2;
52413   quoted_ifaces = sql_quote (ifaces ? ifaces : "");
52414   quoted_hosts = sql_quote (clean);
52415   g_free (clean);
52416   quoted_method = sql_quote (allowed_methods
52417                               ? g_ptr_array_index (allowed_methods, 0)
52418                               : "");
52419   sql ("UPDATE users"
52420        " SET name = %s%s%s,"
52421        "     comment = %s%s%s,"
52422        "     hosts = '%s',"
52423        "     hosts_allow = '%i',"
52424        "     ifaces = '%s',"
52425        "     ifaces_allow = %i,"
52426        "     method = %s%s%s,"
52427        "     modification_time = m_now ()"
52428        " WHERE id = %llu;",
52429        quoted_new_name ? "'" : "",
52430        quoted_new_name ? quoted_new_name : "name",
52431        quoted_new_name ? "'" : "",
52432        quoted_comment ? "'" : "",
52433        quoted_comment ? quoted_comment : "comment",
52434        quoted_comment ? "'" : "",
52435        quoted_hosts,
52436        hosts_allow,
52437        quoted_ifaces,
52438        ifaces_allow,
52439        allowed_methods ? "'" : "",
52440        allowed_methods ? quoted_method : "method",
52441        allowed_methods ? "'" : "",
52442        user);
52443   g_free (quoted_new_name);
52444   g_free (quoted_hosts);
52445   g_free (quoted_ifaces);
52446   g_free (quoted_method);
52447   if (hash)
52448     sql ("UPDATE users"
52449          " SET password = '%s'"
52450          " WHERE id = %llu;",
52451          hash,
52452          user);
52453   g_free (hash);
52454 
52455   /* Update the user groups. */
52456 
52457   if (groups)
52458     {
52459       int index;
52460 
52461       sql ("DELETE FROM group_users WHERE \"user\" = %llu;", user);
52462       index = 0;
52463       while (groups && (index < groups->len))
52464         {
52465           gchar *group_id;
52466           group_t group;
52467 
52468           group_id = (gchar*) g_ptr_array_index (groups, index);
52469           if (strcmp (group_id, "0") == 0)
52470             {
52471               index++;
52472               continue;
52473             }
52474 
52475           if (find_group_with_permission (group_id, &group, "modify_group"))
52476             {
52477               sql_rollback ();
52478               return -1;
52479             }
52480 
52481           if (group == 0)
52482             {
52483               sql_rollback ();
52484               if (group_id_return) *group_id_return = group_id;
52485               return 1;
52486             }
52487 
52488           sql ("INSERT INTO group_users (\"group\", \"user\")"
52489                " VALUES (%llu, %llu);",
52490                group,
52491                user);
52492 
52493           index++;
52494         }
52495     }
52496 
52497   /* Update the user roles. */
52498 
52499   if (roles)
52500     {
52501       int index;
52502 
52503       sql ("DELETE FROM role_users"
52504            " WHERE \"user\" = %llu"
52505            " AND role != (SELECT id from roles"
52506            "              WHERE uuid = '" ROLE_UUID_SUPER_ADMIN "');",
52507            user);
52508       index = 0;
52509       while (roles && (index < roles->len))
52510         {
52511           gchar *role_id;
52512           role_t role;
52513 
52514           role_id = (gchar*) g_ptr_array_index (roles, index);
52515           if (strcmp (role_id, "0") == 0)
52516             {
52517               index++;
52518               continue;
52519             }
52520 
52521           if (find_role_with_permission (role_id, &role, "get_roles"))
52522             {
52523               sql_rollback ();
52524               return -1;
52525             }
52526 
52527           if (role == 0)
52528             {
52529               sql_rollback ();
52530               if (role_id_return) *role_id_return = role_id;
52531               return 1;
52532             }
52533 
52534           if (acl_role_can_super_everyone (role_id))
52535             {
52536               sql_rollback ();
52537               return 99;
52538             }
52539 
52540           sql ("INSERT INTO role_users (role, \"user\") VALUES (%llu, %llu);",
52541                role,
52542                user);
52543 
52544           index++;
52545         }
52546     }
52547 
52548   cache_users = g_array_new (TRUE, TRUE, sizeof (user_t));
52549   g_array_append_val (cache_users, user);
52550   cache_all_permissions_for_users (cache_users);
52551   g_free (g_array_free (cache_users, TRUE));
52552 
52553   sql_commit ();
52554 
52555   if (was_admin)
52556     {
52557       is_admin = acl_user_is_admin (uuid);
52558       g_free (uuid);
52559       if (is_admin)
52560         return 0;
52561       if (*name == NULL)
52562         *name = sql_string ("SELECT name FROM users WHERE id = %llu",
52563                             user);
52564       return 4;
52565     }
52566 
52567   is_admin = acl_user_is_admin (uuid);
52568   g_free (uuid);
52569   if (is_admin)
52570     {
52571       if (*name == NULL)
52572         *name = sql_string ("SELECT name FROM users WHERE id = %llu",
52573                             user);
52574       return 3;
52575     }
52576 
52577   return 0;
52578 }
52579 
52580 /**
52581  * @brief Return the name of a user.
52582  *
52583  * @param[in]  uuid  UUID of user.
52584  *
52585  * @return Newly allocated name if available, else NULL.
52586  */
52587 gchar*
user_name(const char * uuid)52588 user_name (const char *uuid)
52589 {
52590   gchar *name, *quoted_uuid;
52591 
52592   quoted_uuid = sql_quote (uuid);
52593   name = sql_string ("SELECT name FROM users WHERE uuid = '%s';",
52594                      quoted_uuid);
52595   g_free (quoted_uuid);
52596   return name;
52597 }
52598 
52599 /**
52600  * @brief Return the UUID of a user.
52601  *
52602  * Warning: this is only safe for users that are known to be in the db.
52603  *
52604  * @param[in]  user  User.
52605  *
52606  * @return Newly allocated UUID if available, else NULL.
52607  */
52608 char*
user_uuid(user_t user)52609 user_uuid (user_t user)
52610 {
52611   return sql_string ("SELECT uuid FROM users WHERE id = %llu;",
52612                      user);
52613 }
52614 
52615 /**
52616  * @brief Check whether a user is in use.
52617  *
52618  * @param[in]  user  User.
52619  *
52620  * @return 1 yes, 0 no.
52621  */
52622 int
user_in_use(user_t user)52623 user_in_use (user_t user)
52624 {
52625   return 0;
52626 }
52627 
52628 /**
52629  * @brief Check whether a trashcan user is in use.
52630  *
52631  * @param[in]  user  User.
52632  *
52633  * @return 1 yes, 0 no.
52634  */
52635 int
trash_user_in_use(user_t user)52636 trash_user_in_use (user_t user)
52637 {
52638   return 0;
52639 }
52640 
52641 /**
52642  * @brief Check whether a user is writable.
52643  *
52644  * @param[in]  user  User.
52645  *
52646  * @return 1 yes, 0 no.
52647  */
52648 int
user_writable(user_t user)52649 user_writable (user_t user)
52650 {
52651   return 1;
52652 }
52653 
52654 /**
52655  * @brief Check whether a trashcan user is writable.
52656  *
52657  * @param[in]  user  User.
52658  *
52659  * @return 1 yes, 0 no.
52660  */
52661 int
trash_user_writable(user_t user)52662 trash_user_writable (user_t user)
52663 {
52664   return 1;
52665 }
52666 
52667 /**
52668  * @brief Return the ifaces of a user.
52669  *
52670  * @param[in]  uuid  UUID of user.
52671  *
52672  * @return Newly allocated ifaces value if available, else NULL.
52673  */
52674 gchar*
user_ifaces(const char * uuid)52675 user_ifaces (const char *uuid)
52676 {
52677   gchar *name, *quoted_uuid;
52678 
52679   quoted_uuid = sql_quote (uuid);
52680   name = sql_string ("SELECT ifaces FROM users WHERE uuid = '%s';",
52681                      quoted_uuid);
52682   g_free (quoted_uuid);
52683   return name;
52684 }
52685 
52686 /**
52687  * @brief Return whether ifaces value of a user denotes allowed.
52688  *
52689  * @param[in]  uuid  UUID of user.
52690  *
52691  * @return 1 if allow, else 0.
52692  */
52693 int
user_ifaces_allow(const char * uuid)52694 user_ifaces_allow (const char *uuid)
52695 {
52696   gchar *quoted_uuid;
52697   int allow;
52698 
52699   quoted_uuid = sql_quote (uuid);
52700   allow = sql_int ("SELECT ifaces_allow FROM users WHERE uuid = '%s';",
52701                    quoted_uuid);
52702   g_free (quoted_uuid);
52703   return allow;
52704 }
52705 
52706 /**
52707  * @brief Return the hosts of a user.
52708  *
52709  * @param[in]  uuid  UUID of user.
52710  *
52711  * @return Newly allocated hosts value if available, else NULL.
52712  */
52713 gchar*
user_hosts(const char * uuid)52714 user_hosts (const char *uuid)
52715 {
52716   gchar *name, *quoted_uuid;
52717 
52718   quoted_uuid = sql_quote (uuid);
52719   name = sql_string ("SELECT hosts FROM users WHERE uuid = '%s';",
52720                      quoted_uuid);
52721   g_free (quoted_uuid);
52722   return name;
52723 }
52724 
52725 /**
52726  * @brief Return whether hosts value of a user denotes allowed.
52727  *
52728  * @param[in]  uuid  UUID of user.
52729  *
52730  * @return 1 if allow, else 0.
52731  */
52732 int
user_hosts_allow(const char * uuid)52733 user_hosts_allow (const char *uuid)
52734 {
52735   gchar *quoted_uuid;
52736   int allow;
52737 
52738   quoted_uuid = sql_quote (uuid);
52739   allow = sql_int ("SELECT hosts_allow FROM users WHERE uuid = '%s';",
52740                    quoted_uuid);
52741   g_free (quoted_uuid);
52742   return allow;
52743 }
52744 
52745 /**
52746  * @brief User columns for user iterator.
52747  */
52748 #define USER_ITERATOR_FILTER_COLUMNS                                  \
52749  { GET_ITERATOR_FILTER_COLUMNS, "method", "roles", "groups", "hosts", \
52750    "ifaces", NULL }
52751 
52752 /**
52753  * @brief User iterator columns.
52754  */
52755 #define USER_ITERATOR_COLUMNS                                              \
52756  {                                                                         \
52757    GET_ITERATOR_COLUMNS (users),                                           \
52758    { "method", NULL, KEYWORD_TYPE_STRING },                                \
52759    { "hosts", NULL, KEYWORD_TYPE_STRING },                                 \
52760    { "hosts_allow", NULL, KEYWORD_TYPE_INTEGER },                          \
52761    {                                                                       \
52762      "coalesce ((SELECT group_concat (name, ', ')"                         \
52763      "           FROM (SELECT DISTINCT name, order_role (name)"            \
52764      "                 FROM roles, role_users"                             \
52765      "                 WHERE role_users.role = roles.id"                   \
52766      "                 AND \"user\" = users.id"                            \
52767      "                 ORDER BY order_role (roles.name) ASC)"              \
52768      "                 AS user_iterator_sub),"                             \
52769      "           '')",                                                     \
52770      "roles",                                                              \
52771      KEYWORD_TYPE_STRING                                                   \
52772    },                                                                      \
52773    {                                                                       \
52774      "coalesce ((SELECT group_concat (name, ', ')"                         \
52775      "           FROM (SELECT DISTINCT name FROM groups, group_users"      \
52776      "                 WHERE group_users.\"group\" = groups.id"            \
52777      "                 AND \"user\" = users.id"                            \
52778      "                 ORDER BY groups.name ASC)"                          \
52779      "                AS user_iterator_sub),"                              \
52780      "           '')",                                                     \
52781      "groups",                                                             \
52782      KEYWORD_TYPE_STRING                                                   \
52783    },                                                                      \
52784    { "ifaces", NULL, KEYWORD_TYPE_STRING },                                \
52785    { "ifaces_allow", NULL, KEYWORD_TYPE_INTEGER },                         \
52786    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                    \
52787  }
52788 
52789 /**
52790  * @brief User iterator columns for trash case.
52791  */
52792 #define USER_ITERATOR_TRASH_COLUMNS                                        \
52793  {                                                                         \
52794    GET_ITERATOR_COLUMNS (users_trash),                                     \
52795    { "method", NULL, KEYWORD_TYPE_STRING },                                \
52796    { "hosts", NULL, KEYWORD_TYPE_STRING },                                 \
52797    { "hosts_allow", NULL, KEYWORD_TYPE_INTEGER },                          \
52798    { "ifaces", NULL, KEYWORD_TYPE_STRING },                                \
52799    { "ifaces_allow", NULL, KEYWORD_TYPE_INTEGER },                         \
52800    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                    \
52801  }
52802 
52803 /**
52804  * @brief Count number of users.
52805  *
52806  * @param[in]  get  GET params.
52807  *
52808  * @return Total number of users in usered set.
52809  */
52810 int
user_count(const get_data_t * get)52811 user_count (const get_data_t *get)
52812 {
52813   static const char *filter_columns[] = USER_ITERATOR_FILTER_COLUMNS;
52814   static column_t columns[] = USER_ITERATOR_COLUMNS;
52815   return count ("user", get, columns, NULL, filter_columns,
52816                   0, 0, 0, TRUE);
52817 }
52818 
52819 /**
52820  * @brief Initialise a user iterator, including observed users.
52821  *
52822  * @param[in]  iterator    Iterator.
52823  * @param[in]  get         GET data.
52824  *
52825  * @return 0 success, 1 failed to find user, 2 failed to find user (filt_id),
52826  *         -1 error.
52827  */
52828 int
init_user_iterator(iterator_t * iterator,const get_data_t * get)52829 init_user_iterator (iterator_t* iterator, const get_data_t *get)
52830 {
52831   static const char *filter_columns[] = USER_ITERATOR_FILTER_COLUMNS;
52832   static column_t columns[] = USER_ITERATOR_COLUMNS;
52833   static column_t trash_columns[] = USER_ITERATOR_TRASH_COLUMNS;
52834 
52835   return init_get_iterator (iterator,
52836                             "user",
52837                             get,
52838                             columns,
52839                             trash_columns,
52840                             filter_columns,
52841                             0,
52842                             NULL,
52843                             NULL,
52844                             TRUE);
52845 }
52846 
52847 /**
52848  * @brief Get the method of the user from a user iterator.
52849  *
52850  * @param[in]  iterator  Iterator.
52851  *
52852  * @return Method of the user or NULL if iteration is complete.
52853  */
52854 DEF_ACCESS (user_iterator_method, GET_ITERATOR_COLUMN_COUNT);
52855 
52856 /**
52857  * @brief Get the hosts from a user iterator.
52858  *
52859  * @param[in]  iterator  Iterator.
52860  *
52861  * @return Hosts or NULL if iteration is complete.
52862  */
52863 DEF_ACCESS (user_iterator_hosts, GET_ITERATOR_COLUMN_COUNT + 1);
52864 
52865 /**
52866  * @brief Get the hosts allow value from a user iterator.
52867  *
52868  * @param[in]  iterator  Iterator.
52869  *
52870  * @return Hosts allow.
52871  */
52872 int
user_iterator_hosts_allow(iterator_t * iterator)52873 user_iterator_hosts_allow (iterator_t* iterator)
52874 {
52875   if (iterator->done) return -1;
52876   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
52877 }
52878 
52879 /**
52880  * @brief Get the ifaces from a user iterator.
52881  *
52882  * @param[in]  iterator  Iterator.
52883  *
52884  * @return Interfaces or NULL if iteration is complete.
52885  */
52886 DEF_ACCESS (user_iterator_ifaces, GET_ITERATOR_COLUMN_COUNT + 5);
52887 
52888 /**
52889  * @brief Get the ifaces allow value from a user iterator.
52890  *
52891  * @param[in]  iterator  Iterator.
52892  *
52893  * @return Interfaces allow.
52894  */
52895 int
user_iterator_ifaces_allow(iterator_t * iterator)52896 user_iterator_ifaces_allow (iterator_t* iterator)
52897 {
52898   if (iterator->done) return -1;
52899   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
52900 }
52901 
52902 /**
52903  * @brief Initialise an info iterator.
52904  *
52905  * @param[in]  iterator        Iterator.
52906  * @param[in]  user            User.
52907  */
52908 void
init_user_group_iterator(iterator_t * iterator,user_t user)52909 init_user_group_iterator (iterator_t *iterator, user_t user)
52910 {
52911   gchar *available, *with_clause;
52912   get_data_t get;
52913   array_t *permissions;
52914 
52915   assert (user);
52916 
52917   get.trash = 0;
52918   permissions = make_array ();
52919   array_add (permissions, g_strdup ("get_groups"));
52920   available = acl_where_owned ("group", &get, 1, "any", 0, permissions, 0,
52921                                &with_clause);
52922   array_free (permissions);
52923 
52924   init_iterator (iterator,
52925                  "%s"
52926                  " SELECT DISTINCT id, uuid, name, %s FROM groups"
52927                  " WHERE id IN (SELECT \"group\" FROM group_users"
52928                  "              WHERE \"user\" = %llu)"
52929                  " ORDER by name;",
52930                  with_clause ? with_clause : "",
52931                  available,
52932                  user);
52933 
52934   g_free (with_clause);
52935   g_free (available);
52936 }
52937 
52938 /**
52939  * @brief Get the UUID from a user group iterator.
52940  *
52941  * @param[in]  iterator  Iterator.
52942  *
52943  * @return UUID or NULL if iteration is complete.  Freed by cleanup_iterator.
52944  */
52945 DEF_ACCESS (user_group_iterator_uuid, 1);
52946 
52947 /**
52948  * @brief Get the NAME from a user group iterator.
52949  *
52950  * @param[in]  iterator  Iterator.
52951  *
52952  * @return NAME or NULL if iteration is complete.  Freed by cleanup_iterator.
52953  */
52954 DEF_ACCESS (user_group_iterator_name, 2);
52955 
52956 /**
52957  * @brief Get the read permission status from a GET iterator.
52958  *
52959  * @param[in]  iterator  Iterator.
52960  *
52961  * @return 1 if may read, else 0.
52962  */
52963 int
user_group_iterator_readable(iterator_t * iterator)52964 user_group_iterator_readable (iterator_t* iterator)
52965 {
52966   if (iterator->done) return 0;
52967   return iterator_int (iterator, 3);
52968 }
52969 
52970 /**
52971  * @brief Initialise an info iterator.
52972  *
52973  * @param[in]  iterator        Iterator.
52974  * @param[in]  user            User.
52975  */
52976 void
init_user_role_iterator(iterator_t * iterator,user_t user)52977 init_user_role_iterator (iterator_t *iterator, user_t user)
52978 {
52979   gchar *available, *with_clause;
52980   get_data_t get;
52981   array_t *permissions;
52982 
52983   assert (user);
52984 
52985   get.trash = 0;
52986   permissions = make_array ();
52987   array_add (permissions, g_strdup ("get_roles"));
52988   available = acl_where_owned ("role", &get, 1, "any", 0, permissions, 0,
52989                                &with_clause);
52990   array_free (permissions);
52991 
52992   init_iterator (iterator,
52993                  "%s"
52994                  " SELECT DISTINCT id, uuid, name, order_role (name), %s"
52995                  " FROM roles"
52996                  " WHERE id IN (SELECT role FROM role_users"
52997                  "              WHERE \"user\" = %llu)"
52998                  " ORDER by order_role (name);",
52999                  with_clause ? with_clause : "",
53000                  available,
53001                  user);
53002 
53003   g_free (with_clause);
53004   g_free (available);
53005 }
53006 
53007 /**
53008  * @brief Get the UUID from a user role iterator.
53009  *
53010  * @param[in]  iterator  Iterator.
53011  *
53012  * @return UUID or NULL if iteration is complete.  Freed by cleanup_iterator.
53013  */
53014 DEF_ACCESS (user_role_iterator_uuid, 1);
53015 
53016 /**
53017  * @brief Get the NAME from a user role iterator.
53018  *
53019  * @param[in]  iterator  Iterator.
53020  *
53021  * @return NAME or NULL if iteration is complete.  Freed by cleanup_iterator.
53022  */
53023 DEF_ACCESS (user_role_iterator_name, 2);
53024 
53025 /**
53026  * @brief Get the read permission status from a GET iterator.
53027  *
53028  * @param[in]  iterator  Iterator.
53029  *
53030  * @return 1 if may read, else 0.
53031  */
53032 int
user_role_iterator_readable(iterator_t * iterator)53033 user_role_iterator_readable (iterator_t* iterator)
53034 {
53035   if (iterator->done) return 0;
53036   return iterator_int (iterator, 4);
53037 }
53038 
53039 /**
53040  * @brief Check if a user still has resources that are in use.
53041  *
53042  * @param[in] user          The user to check.
53043  * @param[in] table         The table to check for resources in use.
53044  * @param[in] in_use        Function to check if a resource is in use.
53045  * @param[in] trash_table   The trash table to check for resources in use.
53046  * @param[in] trash_in_use  Function to check if a trash resource is in use.
53047  *
53048  * @return 0 no resources in use, 1 found resources used by user.
53049  */
53050 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))53051 user_resources_in_use (user_t user,
53052                        const char *table, int(*in_use)(resource_t),
53053                        const char *trash_table, int(*trash_in_use)(resource_t))
53054 {
53055   iterator_t iter;
53056   int has_resource_in_use = 0;
53057 
53058   init_iterator (&iter, "SELECT id FROM %s WHERE owner = %llu",
53059                  table, user);
53060   while (next (&iter) && has_resource_in_use == 0)
53061     {
53062       resource_t resource = iterator_int64 (&iter, 0);
53063       has_resource_in_use = in_use (resource);
53064     }
53065   cleanup_iterator (&iter);
53066   if (has_resource_in_use)
53067     return 1;
53068 
53069   if (trash_table == NULL || trash_in_use == NULL)
53070     return 0;
53071 
53072   init_iterator (&iter, "SELECT id FROM %s WHERE owner = %llu",
53073                  trash_table, user);
53074   while (next (&iter) && has_resource_in_use == 0)
53075     {
53076       resource_t resource = iterator_int64 (&iter, 0);
53077       has_resource_in_use = trash_in_use (resource);
53078     }
53079   cleanup_iterator (&iter);
53080   if (has_resource_in_use)
53081     return 2;
53082 
53083   return 0;
53084 }
53085 
53086 /**
53087  * @brief Filter columns for vuln iterator.
53088  */
53089 #define VULN_ITERATOR_FILTER_COLUMNS                                         \
53090  {                                                                           \
53091    GET_ITERATOR_FILTER_COLUMNS, "results", "hosts", "severity",              \
53092    "qod", "oldest", "newest", "type", NULL                                   \
53093  }
53094 
53095 /**
53096  * @brief Results SQL for VULN_ITERATOR_COLUMNS.
53097  */
53098 #define VULN_RESULTS_WHERE                                                   \
53099      "  WHERE nvt = vulns.uuid"                                              \
53100      "    AND (opts.report IS NULL OR results.report = opts.report)"         \
53101      "    AND (opts.task IS NULL OR results.task = opts.task)"               \
53102      "    AND (opts.host IS NULL OR results.host = opts.host)"               \
53103      "    AND (results.severity != " G_STRINGIFY (SEVERITY_ERROR) ")"        \
53104      "    AND (SELECT has_permission FROM permissions_get_tasks"             \
53105      "         WHERE \"user\" = gvmd_user ()"                                \
53106      "           AND task = results.task)"
53107 
53108 /**
53109  * @brief Vuln iterator columns.
53110  */
53111 #define VULN_ITERATOR_COLUMNS                                                \
53112  {                                                                           \
53113    /* The following must match GET_ITERATOR_COLUMNS */                       \
53114    { "id", "id", KEYWORD_TYPE_INTEGER },                                     \
53115    { "uuid", "uuid", KEYWORD_TYPE_STRING },                                  \
53116    { "name", "name", KEYWORD_TYPE_STRING },                                  \
53117    { "''", "comment", KEYWORD_TYPE_STRING },                                 \
53118    { "iso_time (creation_time)", NULL, KEYWORD_TYPE_STRING },                \
53119    { "iso_time (modification_time)", NULL, KEYWORD_TYPE_STRING },            \
53120    { "creation_time", "created", KEYWORD_TYPE_INTEGER },                     \
53121    { "modification_time", "modified", KEYWORD_TYPE_INTEGER },                \
53122    { "cast (null AS text)", "_owner", KEYWORD_TYPE_INTEGER },                \
53123    { "''", "owner", KEYWORD_TYPE_STRING },                                   \
53124    /* Type specific columns */                                               \
53125    {                                                                         \
53126      "vuln_results (uuid, opts.task, opts.report, opts.host)",               \
53127      "results",                                                              \
53128      KEYWORD_TYPE_INTEGER                                                    \
53129    },                                                                        \
53130    {                                                                         \
53131      "(SELECT count(*) FROM"                                                 \
53132      "  (SELECT results.host FROM results"                                   \
53133      VULN_RESULTS_WHERE                                                      \
53134      "      GROUP BY results.host) AS hosts_subquery)",                      \
53135      "hosts",                                                                \
53136      KEYWORD_TYPE_INTEGER                                                    \
53137    },                                                                        \
53138    {                                                                         \
53139      "severity", NULL, KEYWORD_TYPE_DOUBLE                                   \
53140    },                                                                        \
53141    {                                                                         \
53142      "qod", NULL, KEYWORD_TYPE_INTEGER                                       \
53143    },                                                                        \
53144    {                                                                         \
53145      "type", NULL, KEYWORD_TYPE_INTEGER                                      \
53146    },                                                                        \
53147    {                                                                         \
53148      "(SELECT min (date) FROM results"                                       \
53149      VULN_RESULTS_WHERE ")",                                                 \
53150      "oldest",                                                               \
53151      KEYWORD_TYPE_INTEGER                                                    \
53152    },                                                                        \
53153    {                                                                         \
53154      "(SELECT max (date) FROM results"                                       \
53155      VULN_RESULTS_WHERE ")",                                                 \
53156      "newest",                                                               \
53157      KEYWORD_TYPE_INTEGER                                                    \
53158    },                                                                        \
53159    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
53160  }
53161 
53162 /**
53163  * @brief Generate the extra_tables string for a vuln iterator.
53164  *
53165  * @param[in]  task_id    UUID of the task to limit vulns to.
53166  * @param[in]  report_id  UUID of the report to limit vulns to.
53167  * @param[in]  host       IP address of the task to limit vulns to.
53168  * @param[in]  min_qod    Minimum QoD.
53169  *
53170  * @return Newly allocated string with the extra_tables clause.
53171  */
53172 static gchar*
vuln_iterator_opts_table(const gchar * task_id,const gchar * report_id,const gchar * host,int min_qod)53173 vuln_iterator_opts_table (const gchar *task_id, const gchar *report_id,
53174                           const gchar *host, int min_qod)
53175 {
53176   GString *ret;
53177 
53178   ret = g_string_new (", (SELECT");
53179 
53180   if (task_id && strcmp (task_id, ""))
53181     {
53182       task_t task = 0;
53183       find_task_with_permission (task_id, &task, "get_tasks");
53184       g_string_append_printf (ret, " %llu AS task,", task);
53185     }
53186   else
53187     {
53188       g_string_append (ret, " cast (null AS integer) AS task,");
53189     }
53190 
53191   if (report_id && strcmp (report_id, ""))
53192     {
53193       report_t report = 0;
53194       find_report_with_permission (report_id, &report, "get_reports");
53195       g_string_append_printf (ret, " %llu AS report,", report);
53196     }
53197   else
53198     {
53199       g_string_append (ret, " cast (null AS integer) AS report,");
53200     }
53201 
53202   if (host && strcmp (host, ""))
53203     {
53204       gchar *quoted_host = sql_quote (host);
53205       g_string_append_printf (ret, " '%s' AS host,", quoted_host);
53206       g_free (quoted_host);
53207     }
53208   else
53209     {
53210       g_string_append (ret, " cast (null AS text) AS host,");
53211     }
53212 
53213   g_string_append_printf (ret, " %d AS min_qod) AS opts", min_qod);
53214 
53215   return g_string_free (ret, FALSE);
53216 }
53217 
53218 /**
53219  * @brief Generate the extra_tables string for a vuln iterator using a filter.
53220  *
53221  * @param[in]  filter     The filter term to use.
53222  *
53223  * @return Newly allocated string with the extra_tables clause.
53224  */
53225 static gchar*
vuln_iterator_opts_from_filter(const gchar * filter)53226 vuln_iterator_opts_from_filter (const gchar *filter)
53227 {
53228   gchar *task_id, *report_id, *host;
53229   int min_qod;
53230   gchar *ret;
53231 
53232   if (filter)
53233     {
53234       task_id = filter_term_value (filter, "task_id");
53235       report_id = filter_term_value (filter, "report_id");
53236       host = filter_term_value (filter, "host");
53237     }
53238   else
53239     task_id = report_id = host = NULL;
53240 
53241   min_qod = filter_term_min_qod (filter);
53242 
53243   ret = vuln_iterator_opts_table (task_id, report_id, host, min_qod);
53244 
53245   g_free (task_id);
53246   g_free (report_id);
53247   g_free (host);
53248 
53249   return ret;
53250 }
53251 
53252 /**
53253  * @brief Initialise a vulnerability iterator, including observed vulns.
53254  *
53255  * @param[in]  iterator    Iterator.
53256  * @param[in]  get         GET data.
53257  *
53258  * @return 0 success, 1 failed to find vuln, 2 failed to find filter (filt_id),
53259  *         -1 error.
53260  */
53261 int
init_vuln_iterator(iterator_t * iterator,const get_data_t * get)53262 init_vuln_iterator (iterator_t* iterator, const get_data_t *get)
53263 {
53264   int ret;
53265   gchar *filter;
53266   gchar *extra_tables, *extra_where;
53267   static const char *filter_columns[] = VULN_ITERATOR_FILTER_COLUMNS;
53268   static column_t select_columns[] = VULN_ITERATOR_COLUMNS;
53269 
53270   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
53271     {
53272       if (get->filter_replacement)
53273         /* Replace the filter term with one given by the caller.  This is
53274          * used by GET_REPORTS to use the default filter with any task (when
53275          * given the special value of -3 in filt_id). */
53276         filter = g_strdup (get->filter_replacement);
53277       else
53278         filter = filter_term (get->filt_id);
53279       if (filter == NULL)
53280         return 2;
53281     }
53282   else
53283     filter = NULL;
53284 
53285   extra_tables
53286     = vuln_iterator_opts_from_filter (filter ? filter : get->filter);
53287 
53288   extra_where
53289     = vulns_extra_where ();
53290 
53291   ret = init_get_iterator2 (iterator,
53292                             "vuln",
53293                             get,
53294                             select_columns,
53295                             NULL, /* trash_select_columns */
53296                             NULL, /* where_columns */
53297                             NULL, /* trash_where_columns */
53298                             filter_columns,
53299                             0     /* distinct */,
53300                             extra_tables, /* extra_tables */
53301                             extra_where,  /* extra_where */
53302                             NULL, /* extra_where_single */
53303                             0,    /* owned */
53304                             0,    /* ignore_id */
53305                             NULL);/* extra_order */
53306 
53307   g_free (extra_tables);
53308   g_free (extra_where);
53309 
53310   return ret;
53311 }
53312 
53313 /**
53314  * @brief Get the number of results from a vuln iterator.
53315  *
53316  * @param[in]  iterator  Iterator.
53317  *
53318  * @return The number of results.
53319  */
53320 int
vuln_iterator_results(iterator_t * iterator)53321 vuln_iterator_results (iterator_t* iterator)
53322 {
53323   if (iterator->done) return 0;
53324   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 0);
53325 }
53326 
53327 /**
53328  * @brief Get the number of hosts from a vuln iterator.
53329  *
53330  * @param[in]  iterator  Iterator.
53331  *
53332  * @return The number of hosts.
53333  */
53334 int
vuln_iterator_hosts(iterator_t * iterator)53335 vuln_iterator_hosts (iterator_t* iterator)
53336 {
53337   if (iterator->done) return 0;
53338   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
53339 }
53340 
53341 /**
53342  * @brief Get the severity from a vuln iterator.
53343  *
53344  * @param[in]  iterator  Iterator.
53345  *
53346  * @return The severity.
53347  */
53348 double
vuln_iterator_severity(iterator_t * iterator)53349 vuln_iterator_severity (iterator_t* iterator)
53350 {
53351   if (iterator->done) return SEVERITY_MISSING;
53352   return iterator_double (iterator, GET_ITERATOR_COLUMN_COUNT + 2);
53353 }
53354 
53355 /**
53356  * @brief Get the QoD from a vuln iterator.
53357  *
53358  * @param[in]  iterator  Iterator.
53359  *
53360  * @return The QoD.
53361  */
53362 int
vuln_iterator_qod(iterator_t * iterator)53363 vuln_iterator_qod (iterator_t* iterator)
53364 {
53365   if (iterator->done) return -1;
53366   return iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
53367 }
53368 
53369 /**
53370  * @brief Get the QoD from a vuln iterator.
53371  *
53372  * @param[in]  iterator  Iterator.
53373  *
53374  * @return The QoD.
53375  */
53376 const char*
vuln_iterator_type(iterator_t * iterator)53377 vuln_iterator_type (iterator_t* iterator)
53378 {
53379   if (iterator->done) return NULL;
53380   return iterator_string (iterator, GET_ITERATOR_COLUMN_COUNT + 4);
53381 }
53382 
53383 /**
53384  * @brief Get the date of the oldest result from a vuln iterator.
53385  *
53386  * @param[in]  iterator  Iterator.
53387  *
53388  * @return The oldest result date.
53389  */
53390 time_t
vuln_iterator_oldest(iterator_t * iterator)53391 vuln_iterator_oldest (iterator_t* iterator)
53392 {
53393   if (iterator->done) return 0;
53394   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 5);
53395 }
53396 
53397 /**
53398  * @brief Get the date of the oldest result from a vuln iterator.
53399  *
53400  * @param[in]  iterator  Iterator.
53401  *
53402  * @return The oldest result date.
53403  */
53404 time_t
vuln_iterator_newest(iterator_t * iterator)53405 vuln_iterator_newest (iterator_t* iterator)
53406 {
53407   if (iterator->done) return 0;
53408   return iterator_int64 (iterator, GET_ITERATOR_COLUMN_COUNT + 6);
53409 }
53410 
53411 /**
53412  * @brief Count number of vulns.
53413  *
53414  * @param[in]  get  GET params.
53415  *
53416  * @return Total number of vulns in filtered set.
53417  */
53418 int
vuln_count(const get_data_t * get)53419 vuln_count (const get_data_t *get)
53420 {
53421   gchar *filter;
53422   static const char *filter_columns[] = VULN_ITERATOR_FILTER_COLUMNS;
53423   static column_t columns[] = VULN_ITERATOR_COLUMNS;
53424   gchar *extra_tables, *extra_where;
53425   int ret;
53426 
53427   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
53428     {
53429       if (get->filter_replacement)
53430         /* Replace the filter term with one given by the caller.  This is
53431          * used by GET_REPORTS to use the default filter with any task (when
53432          * given the special value of -3 in filt_id). */
53433         filter = g_strdup (get->filter_replacement);
53434       else
53435         filter = filter_term (get->filt_id);
53436       if (filter == NULL)
53437         return 2;
53438     }
53439   else
53440     filter = NULL;
53441 
53442   extra_tables
53443     = vuln_iterator_opts_from_filter (filter ? filter : get->filter);
53444 
53445   extra_where
53446     = vulns_extra_where ();
53447 
53448   ret = count ("vuln", get, columns, NULL /*trash_columns*/, filter_columns, 0,
53449                extra_tables, extra_where, FALSE);
53450 
53451   g_free (filter);
53452   g_free (extra_tables);
53453   g_free (extra_where);
53454 
53455   return ret;
53456 }
53457 
53458 /**
53459  * @brief Extra WHERE clause for vulns.
53460  *
53461  * @return WHERE clause.
53462  */
53463 static gchar*
vulns_extra_where()53464 vulns_extra_where ()
53465 {
53466   return g_strdup (" AND vuln_results_exist (uuid, opts.task, opts.report,"
53467                    "                         opts.host)"
53468                    " AND (qod >= opts.min_qod)");
53469 }
53470 
53471 /**
53472  * @brief Get LDAP info.
53473  *
53474  * @param[out]  enabled    Whether LDAP is enabled.
53475  * @param[out]  host       Freshly allocated host.
53476  * @param[out]  authdn     Freshly allocated Auth DN.
53477  * @param[out]  plaintext  Whether plaintext auth is allowed.
53478  * @param[out]  cacert     CA cert if there's one, else NULL.
53479  */
53480 void
manage_get_ldap_info(int * enabled,gchar ** host,gchar ** authdn,int * plaintext,gchar ** cacert)53481 manage_get_ldap_info (int *enabled, gchar **host, gchar **authdn,
53482                       int *plaintext, gchar **cacert)
53483 {
53484   if (enabled)
53485     *enabled = ldap_auth_enabled ();
53486 
53487   *host = sql_string ("SELECT value FROM meta"
53488                       " WHERE name = 'ldap_host';");
53489   if (*host == NULL)
53490     *host = g_strdup ("127.0.0.1");
53491 
53492   *authdn = sql_string ("SELECT value FROM meta"
53493                         " WHERE name = 'ldap_authdn';");
53494   if (*authdn == NULL)
53495     *authdn = g_strdup ("userid=%s,dc=example,dc=org");
53496 
53497   *plaintext = sql_int ("SELECT coalesce ((SELECT CAST (value AS integer)"
53498                         "                  FROM meta"
53499                         "                  WHERE name"
53500                         "                        = 'ldap_allow_plaintext'),"
53501                         "                 0);");
53502 
53503   *cacert = sql_string ("SELECT value FROM meta"
53504                         " WHERE name = 'ldap_cacert';");
53505 }
53506 
53507 /**
53508  * @brief Set LDAP info.
53509  *
53510  * @param[in]  enabled    Whether LDAP is enabled.  -1 to keep current value.
53511  * @param[in]  host       LDAP host.  NULL to keep current value.
53512  * @param[in]  authdn     Auth DN.  NULL to keep current value.
53513  * @param[in]  allow_plaintext  Whether plaintext auth is allowed.  -1 to
53514  *                              keep current value.
53515  * @param[in]  cacert     CA certificate.  NULL to keep current value.
53516  */
53517 void
manage_set_ldap_info(int enabled,gchar * host,gchar * authdn,int allow_plaintext,gchar * cacert)53518 manage_set_ldap_info (int enabled, gchar *host, gchar *authdn,
53519                       int allow_plaintext, gchar *cacert)
53520 {
53521   gchar *quoted;
53522 
53523   sql_begin_immediate ();
53524 
53525   if (enabled >= 0)
53526     {
53527       sql ("DELETE FROM meta WHERE name LIKE 'ldap_enable';");
53528       sql ("INSERT INTO meta (name, value) VALUES ('ldap_enable', %i);", enabled);
53529     }
53530 
53531   if (host)
53532     {
53533       sql ("DELETE FROM meta WHERE name LIKE 'ldap_host';");
53534       quoted = sql_quote (host);
53535       sql ("INSERT INTO meta (name, value) VALUES ('ldap_host', '%s');",
53536            quoted);
53537       g_free (quoted);
53538     }
53539 
53540   if (authdn)
53541     {
53542       sql ("DELETE FROM meta WHERE name LIKE 'ldap_authdn';");
53543       quoted = sql_quote (authdn);
53544       sql ("INSERT INTO meta (name, value) VALUES ('ldap_authdn', '%s');",
53545            quoted);
53546       g_free (quoted);
53547     }
53548 
53549   if (allow_plaintext >= 0)
53550     {
53551       sql ("DELETE FROM meta WHERE name LIKE 'ldap_allow_plaintext';");
53552       sql ("INSERT INTO meta (name, value) VALUES ('ldap_allow_plaintext', %i);",
53553            allow_plaintext);
53554     }
53555 
53556   if (cacert)
53557     {
53558       sql ("DELETE FROM meta WHERE name LIKE 'ldap_cacert';");
53559       quoted = sql_quote (cacert);
53560       sql ("INSERT INTO meta (name, value) VALUES ('ldap_cacert', '%s');",
53561            quoted);
53562       g_free (quoted);
53563     }
53564 
53565   sql_commit ();
53566 }
53567 
53568 /**
53569  * @brief Get RADIUS info.
53570  *
53571  * @param[out]  enabled     Whether RADIUS is enabled.
53572  * @param[out]  host        Freshly allocated RADIUS host.
53573  * @param[out]  key         Freshly allocated RADIUS secret key.
53574  */
53575 void
manage_get_radius_info(int * enabled,char ** host,char ** key)53576 manage_get_radius_info (int *enabled, char **host, char **key)
53577 {
53578   if (enabled)
53579     *enabled = radius_auth_enabled ();
53580 
53581   *host = sql_string ("SELECT value FROM meta WHERE name = 'radius_host';");
53582   if (!*host)
53583     *host = g_strdup ("127.0.0.1");
53584 
53585   *key = sql_string ("SELECT value FROM meta WHERE name = 'radius_key';");
53586   if (!*key)
53587     *key = g_strdup ("testing123");
53588 }
53589 
53590 /**
53591  * @brief Set RADIUS info.
53592  *
53593  * @param[out]  enabled    Whether RADIUS is enabled. -1 to keep current value.
53594  * @param[out]  host       RADIUS host. NULL to keep current value.
53595  * @param[out]  key        Secret key.  NULL to keep current value.
53596  */
53597 void
manage_set_radius_info(int enabled,gchar * host,gchar * key)53598 manage_set_radius_info (int enabled, gchar *host, gchar *key)
53599 {
53600   char *quoted;
53601 
53602   sql_begin_immediate ();
53603 
53604   if (enabled >= 0)
53605     {
53606       sql ("DELETE FROM meta WHERE name LIKE 'radius_enable';");
53607       sql ("INSERT INTO meta (name, value) VALUES ('radius_enable', %i);",
53608            enabled);
53609     }
53610 
53611   if (host)
53612     {
53613       sql ("DELETE FROM meta WHERE name LIKE 'radius_host';");
53614       quoted = sql_quote (host);
53615       sql ("INSERT INTO meta (name, value) VALUES ('radius_host', '%s');",
53616            quoted);
53617       g_free (quoted);
53618     }
53619 
53620   if (key)
53621     {
53622       sql ("DELETE FROM meta WHERE name LIKE 'radius_key';");
53623       quoted = sql_quote (key);
53624       sql ("INSERT INTO meta (name, value) VALUES ('radius_key', '%s');",
53625            quoted);
53626       g_free (quoted);
53627     }
53628 
53629   sql_commit ();
53630 }
53631 
53632 
53633 /* Tags */
53634 
53635 /**
53636  * @brief Add a resource to a tag.
53637  *
53638  * @param[in]  tag         Tag to attach to the resource.
53639  * @param[in]  type        The resource Type.
53640  * @param[in]  uuid        The resource UUID.
53641  * @param[in]  resource    The resource row id.
53642  * @param[in]  location    Whether the resource is in the trashcan.
53643  *
53644  * @return  0 success, -1 error
53645  */
53646 static int
tag_add_resource(tag_t tag,const char * type,const char * uuid,resource_t resource,int location)53647 tag_add_resource (tag_t tag, const char *type, const char *uuid,
53648                   resource_t resource, int location)
53649 {
53650   int already_added, ret;
53651   gchar *quoted_resource_uuid;
53652 
53653   ret = 0;
53654 
53655   quoted_resource_uuid = uuid ? sql_insert (uuid) : g_strdup ("''");
53656 
53657   if (type_is_info_subtype (type))
53658     already_added = sql_int ("SELECT count(*) FROM tag_resources"
53659                              " WHERE resource_type = '%s'"
53660                              " AND resource_uuid = %s"
53661                              " AND tag = %llu",
53662                              type, quoted_resource_uuid, tag);
53663   else
53664     already_added = sql_int ("SELECT count(*) FROM tag_resources"
53665                              " WHERE resource_type = '%s'"
53666                              " AND resource = %llu"
53667                              " AND resource_location = %d"
53668                              " AND tag = %llu",
53669                              type, resource, location, tag);
53670 
53671   if (already_added == 0)
53672     {
53673       g_debug ("%s - adding %s %s", __func__, type, uuid);
53674       sql ("INSERT INTO tag_resources"
53675            " (tag, resource_type, resource, resource_uuid, resource_location)"
53676            " VALUES (%llu, '%s', %llu, %s, %d)",
53677            tag, type, resource, quoted_resource_uuid, location);
53678     }
53679   else
53680     {
53681       g_debug ("%s - skipping %s %s", __func__, type, uuid);
53682     }
53683 
53684   g_free (quoted_resource_uuid);
53685 
53686   return ret;
53687 }
53688 
53689 /**
53690  * @brief Find a resource by UUID and add it as a tag resource.
53691  *
53692  * @param[in]  tag         Tag to attach to the resource.
53693  * @param[in]  type        The resource Type.
53694  * @param[in]  uuid        The resource UUID.
53695  * @param[in]  permission  The permission required to get the resource.
53696  *
53697  * @return 0 success, -1 error, 1 resource not found.
53698  */
53699 static int
tag_add_resource_uuid(tag_t tag,const char * type,const char * uuid,const char * permission)53700 tag_add_resource_uuid (tag_t tag, const char *type, const char *uuid,
53701                        const char *permission)
53702 {
53703   int resource_location = LOCATION_TABLE;
53704   resource_t resource;
53705 
53706   if (find_resource_with_permission (type, uuid,
53707                                      &resource, permission, 0))
53708     {
53709       g_warning ("%s: Failed to find %s %s",
53710                  __func__, type, uuid);
53711       return -1;
53712     }
53713   else if (resource == 0
53714            && type_has_trash (type))
53715     {
53716       if (find_resource_with_permission (type, uuid,
53717                                          &resource, permission,
53718                                          1))
53719         {
53720           g_warning ("%s: Failed to find trash %s %s",
53721                      __func__, type, uuid);
53722           return -1;
53723         }
53724       else if (resource != 0)
53725         resource_location = LOCATION_TRASH;
53726     }
53727 
53728   if (resource == 0)
53729     return 1;
53730 
53731   return tag_add_resource (tag, type, uuid, resource, resource_location);
53732 }
53733 
53734 /**
53735  * @brief Find resources from an array by UUID and insert as a tag resource.
53736  *
53737  * @param[in]  tag         Tag to attach to the resource.
53738  * @param[in]  type        The resource type.
53739  * @param[in]  uuids       The array of resource UUIDs.
53740  * @param[out] error_extra Extra error output. Contains UUID if not found.
53741  *
53742  * @return 0 success, -1 error, 1 resource not found.
53743  */
53744 static int
tag_add_resources_list(tag_t tag,const char * type,array_t * uuids,gchar ** error_extra)53745 tag_add_resources_list (tag_t tag, const char *type, array_t *uuids,
53746                         gchar **error_extra)
53747 {
53748   gchar *resource_permission, *current_uuid;
53749   int index;
53750 
53751   if (type_is_info_subtype (type))
53752     resource_permission = g_strdup ("get_info");
53753   else if (type_is_asset_subtype (type))
53754     resource_permission = g_strdup ("get_assets");
53755   else
53756     resource_permission = g_strdup_printf ("get_%ss", type);
53757 
53758   index = 0;
53759   while ((current_uuid = g_ptr_array_index (uuids, index++)))
53760     {
53761       int ret;
53762 
53763       ret = tag_add_resource_uuid (tag, type, current_uuid,
53764                                    resource_permission);
53765       if (ret)
53766         {
53767           g_free (resource_permission);
53768           if (error_extra)
53769             *error_extra = g_strdup (current_uuid);
53770           return ret;
53771         }
53772     }
53773 
53774   return 0;
53775 }
53776 
53777 /**
53778  * @brief Find resources using a filter and insert as tag resources.
53779  *
53780  * @param[in]  tag         Tag to attach to the resource.
53781  * @param[in]  type        The resource type.
53782  * @param[in]  filter      The filter to select resources with.
53783  *
53784  * @return 0 success, -1 error, 1 resource not found, 2 no resources returned.
53785  */
53786 static int
tag_add_resources_filter(tag_t tag,const char * type,const char * filter)53787 tag_add_resources_filter (tag_t tag, const char *type, const char *filter)
53788 {
53789   iterator_t resources;
53790   gchar *filtered_select;
53791   get_data_t resources_get;
53792   int ret;
53793 
53794   memset (&resources_get, '\0', sizeof (resources_get));
53795   resources_get.filter = g_strdup (filter);
53796   resources_get.filt_id = FILT_ID_NONE;
53797   resources_get.trash = LOCATION_TABLE;
53798   resources_get.type = g_strdup (type);
53799 
53800   ignore_max_rows_per_page = 1;
53801   filtered_select = NULL;
53802 
53803   if (strcasecmp (type, "TICKET") == 0)
53804     {
53805       /* TODO This is how it should be done for all types, in order
53806        * to contain each per-resource implementation in its own file. */
53807       if (init_ticket_iterator (&resources, &resources_get))
53808         {
53809           g_warning ("%s: Failed to build filter SELECT", __func__);
53810           sql_rollback ();
53811           g_free (resources_get.filter);
53812           g_free (resources_get.type);
53813           return -1;
53814         }
53815     }
53816   else
53817     {
53818       gchar *columns;
53819 
53820       columns = g_strdup_printf ("%ss.id, %ss.uuid", type, type);
53821       switch (type_build_select (type,
53822                                  columns,
53823                                  &resources_get, 0, 1, NULL, NULL, NULL,
53824                                  &filtered_select))
53825         {
53826           case 0:
53827             g_free (columns);
53828             if (sql_int ("SELECT count(*) FROM (%s) AS filter_selection",
53829                          filtered_select) == 0)
53830               {
53831                 g_free (filtered_select);
53832                 return 2;
53833               }
53834 
53835             init_iterator (&resources,
53836                            "%s",
53837                            filtered_select);
53838 
53839             break;
53840           default:
53841             g_free (columns);
53842             ignore_max_rows_per_page = 0;
53843             g_warning ("%s: Failed to build filter SELECT", __func__);
53844             sql_rollback ();
53845             g_free (resources_get.filter);
53846             g_free (resources_get.type);
53847             return -1;
53848         }
53849     }
53850   ignore_max_rows_per_page = 0;
53851 
53852   g_free (resources_get.filter);
53853   g_free (resources_get.type);
53854 
53855   ret = 2;
53856   while (next (&resources))
53857     {
53858       resource_t resource;
53859       const char *current_uuid;
53860       int add_ret;
53861 
53862       resource = iterator_int64 (&resources, 0);
53863       current_uuid = iterator_string (&resources, 1);
53864 
53865       add_ret = tag_add_resource (tag, type, current_uuid, resource,
53866                                   LOCATION_TABLE);
53867       if (add_ret)
53868         {
53869           ret = add_ret;
53870           break;
53871         }
53872       ret = 0;
53873     }
53874   cleanup_iterator (&resources);
53875 
53876   g_free (filtered_select);
53877 
53878   return ret;
53879 }
53880 
53881 /**
53882  * @brief Remove resources from a tag using a UUIDs array.
53883  *
53884  * @param[in]  tag         Tag to attach to the resource.
53885  * @param[in]  type        The resource type.
53886  * @param[in]  uuids       The array of resource UUIDs.
53887  * @param[out] error_extra Extra error output. Contains UUID if not found.
53888  *
53889  * @return 0 success, -1 error, 1 resource not found.
53890  */
53891 static int
tag_remove_resources_list(tag_t tag,const char * type,array_t * uuids,gchar ** error_extra)53892 tag_remove_resources_list (tag_t tag, const char *type, array_t *uuids,
53893                            gchar **error_extra)
53894 {
53895   gchar *current_uuid;
53896   int index;
53897 
53898   index = 0;
53899   while ((current_uuid = g_ptr_array_index (uuids, index++)))
53900     {
53901       gchar *uuid_escaped = g_markup_escape_text (current_uuid, -1);
53902 
53903       if (sql_int ("SELECT count(*) FROM tag_resources"
53904                    " WHERE tag = %llu AND resource_uuid = '%s'",
53905                    tag, uuid_escaped) == 0)
53906         {
53907           if (error_extra)
53908             *error_extra = g_strdup (current_uuid);
53909           g_free (uuid_escaped);
53910           return 1;
53911         }
53912 
53913       sql ("DELETE FROM tag_resources"
53914            " WHERE tag = %llu AND resource_uuid = '%s'",
53915            tag, uuid_escaped);
53916       g_free (uuid_escaped);
53917     }
53918 
53919   return 0;
53920 }
53921 
53922 /**
53923  * @brief Remove resources from a tag using a filter.
53924  *
53925  * @param[in]  tag         Tag to attach to the resource.
53926  * @param[in]  type        The resource type.
53927  * @param[in]  filter      The filter to select resources with.
53928  *
53929  * @return 0 success, -1 error, 1 resource not found, 2 no resources returned.
53930  */
53931 static int
tag_remove_resources_filter(tag_t tag,const char * type,const char * filter)53932 tag_remove_resources_filter (tag_t tag, const char *type, const char *filter)
53933 {
53934   iterator_t resources;
53935   gchar *iterator_select;
53936   get_data_t resources_get;
53937   int ret;
53938 
53939   memset (&resources_get, '\0', sizeof (resources_get));
53940   resources_get.filter = g_strdup (filter);
53941   resources_get.filt_id = FILT_ID_NONE;
53942   resources_get.trash = LOCATION_TABLE;
53943   resources_get.type = g_strdup (type);
53944 
53945   ignore_max_rows_per_page = 1;
53946   iterator_select = NULL;
53947 
53948   if (strcasecmp (type, "TICKET") == 0)
53949     {
53950       /* TODO This is how it should be done for all types, in order
53951        * to contain each per-resource implementation in its own file. */
53952       if (init_ticket_iterator (&resources, &resources_get))
53953         {
53954           ignore_max_rows_per_page = 0;
53955           g_warning ("%s: Failed to init ticket iterator", __func__);
53956           sql_rollback ();
53957           g_free (resources_get.filter);
53958           g_free (resources_get.type);
53959           return -1;
53960         }
53961     }
53962   else
53963     {
53964       gchar *columns;
53965 
53966       columns = g_strdup_printf ("%ss.id", type);
53967       switch (type_build_select (type,
53968                                  columns,
53969                                  &resources_get, 0, 1, NULL, NULL, NULL,
53970                                  &iterator_select))
53971         {
53972           case 0:
53973             g_free (columns);
53974             init_iterator (&resources, "%s", iterator_select);
53975             break;
53976           default:
53977             g_free (columns);
53978             ignore_max_rows_per_page = 0;
53979             g_warning ("%s: Failed to build filter SELECT", __func__);
53980             sql_rollback ();
53981             g_free (resources_get.filter);
53982             g_free (resources_get.type);
53983             return -1;
53984         }
53985     }
53986   ignore_max_rows_per_page = 0;
53987 
53988   g_free (resources_get.filter);
53989   g_free (resources_get.type);
53990 
53991   ret = 2;
53992   while (next (&resources))
53993     {
53994       resource_t resource;
53995 
53996       resource = iterator_int64 (&resources, 0);
53997 
53998       ret = 0;
53999       sql ("DELETE FROM tag_resources"
54000            " WHERE tag = %llu"
54001            " AND resource = %llu"
54002            " AND resource_location = %d",
54003            tag, resource, resources_get.trash);
54004     }
54005   cleanup_iterator (&resources);
54006 
54007   g_free (iterator_select);
54008 
54009   return ret;
54010 }
54011 
54012 /**
54013  * @brief Find a tag for a specific permission, given a UUID.
54014  *
54015  * @param[in]   uuid        UUID of tag.
54016  * @param[out]  tag         Tag return, 0 if successfully failed to find tag.
54017  * @param[in]   permission  Permission.
54018  *
54019  * @return FALSE on success (including if failed to find tag), TRUE on error.
54020  */
54021 static gboolean
find_tag_with_permission(const char * uuid,tag_t * tag,const char * permission)54022 find_tag_with_permission (const char* uuid, tag_t* tag,
54023                           const char *permission)
54024 {
54025   return find_resource_with_permission ("tag", uuid, tag, permission, 0);
54026 }
54027 
54028 /**
54029  * @brief Create a tag from an existing tag.
54030  *
54031  * @param[in]  name        Name of new tag.  NULL to copy from existing.
54032  * @param[in]  comment     Comment on new tag.  NULL to copy from existing.
54033  * @param[in]  tag_id      UUID of existing tag.
54034  * @param[out] new_tag_return  New tag.
54035  *
54036  * @return 0 success, 2 failed to find existing tag,
54037  *         99 permission denied, -1 error.
54038  */
54039 int
copy_tag(const char * name,const char * comment,const char * tag_id,tag_t * new_tag_return)54040 copy_tag (const char* name, const char* comment, const char *tag_id,
54041           tag_t* new_tag_return)
54042 {
54043   int ret = 0;
54044   tag_t new_tag, old_tag;
54045 
54046   ret = copy_resource ("tag", name, comment, tag_id,
54047                        "value, resource_type, active",
54048                        1, &new_tag, &old_tag);
54049 
54050   if (ret)
54051     return ret;
54052 
54053   if (new_tag_return)
54054     *new_tag_return = new_tag;
54055 
54056   sql ("INSERT INTO tag_resources"
54057        " (tag, resource_type, resource, resource_uuid, resource_location)"
54058        " SELECT"
54059        "  %llu, resource_type, resource, resource_uuid, resource_location"
54060        "   FROM tag_resources"
54061        "  WHERE tag = %llu",
54062        new_tag, old_tag);
54063 
54064   return 0;
54065 }
54066 
54067 /**
54068  * @brief Create a tag.
54069  *
54070  * @param[in]  name          Name of the tag.
54071  * @param[in]  comment       Comment for the tag.
54072  * @param[in]  value         Value of the tag.
54073  * @param[in]  resource_type    Resource type to attach the tag to.
54074  * @param[in]  resource_uuids   Unique IDs of the resource to attach the tag to.
54075  * @param[in]  resources_filter Filter to select resources to attach tag to.
54076  * @param[in]  active        0 for inactive, NULL or any other value for active.
54077  * @param[out] tag          Created tag.
54078  * @param[out] error_extra  Extra string for error (e.g. missing resource ID)
54079  *
54080  * @return 0 success, 1 resource ID not found (sets error_extra to UUID),
54081  *   2 filter returned no results, 3 too many resources selected,
54082  *   99 permission denied, -1 error.
54083  */
54084 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)54085 create_tag (const char * name, const char * comment, const char * value,
54086             const char * resource_type, array_t * resource_uuids,
54087             const char * resources_filter, const char * active, tag_t * tag,
54088             gchar **error_extra)
54089 {
54090   gchar *quoted_name, *quoted_comment, *quoted_value;
54091   gchar *lc_resource_type, *quoted_resource_type;
54092   tag_t new_tag;
54093 
54094   sql_begin_immediate ();
54095 
54096   if (acl_user_may ("create_tag") == 0)
54097     {
54098       sql_rollback ();
54099       return 99;
54100     }
54101 
54102   lc_resource_type = g_ascii_strdown (resource_type, -1);
54103   if (strcmp (lc_resource_type, "")
54104       && valid_db_resource_type (lc_resource_type) == 0)
54105     {
54106       g_free (lc_resource_type);
54107       sql_rollback ();
54108       return -1;
54109     }
54110 
54111   quoted_name = sql_insert (name);
54112   quoted_resource_type = sql_insert (lc_resource_type);
54113 
54114   quoted_comment = sql_insert (comment ? comment : "");
54115   quoted_value = sql_insert (value ? value : "");
54116   sql ("INSERT INTO tags"
54117       " (uuid, owner, creation_time, modification_time, name, comment,"
54118       "  value, resource_type, active)"
54119       " VALUES"
54120       " (make_uuid (), (SELECT id FROM users WHERE users.uuid = '%s'),"
54121       "  %i, %i, %s, %s, %s, %s, %i);",
54122       current_credentials.uuid,
54123       time (NULL),
54124       time (NULL),
54125       quoted_name,
54126       quoted_comment,
54127       quoted_value,
54128       quoted_resource_type,
54129       active
54130        ? (strcmp (active, "0") == 0
54131            ? 0
54132            : 1)
54133        : 1);
54134 
54135   new_tag = sql_last_insert_id ();
54136 
54137   g_free (quoted_name);
54138   g_free (quoted_comment);
54139   g_free (quoted_value);
54140   g_free (quoted_resource_type);
54141 
54142   /* Handle resource IDs */
54143   if (resource_uuids)
54144     {
54145       int ret;
54146       ret = tag_add_resources_list (new_tag, lc_resource_type, resource_uuids,
54147                                     error_extra);
54148 
54149       if (ret)
54150         {
54151           // Assume tag_add_resources_list return codes match
54152           sql_rollback ();
54153           g_free (lc_resource_type);
54154           return ret;
54155         }
54156     }
54157 
54158   /* Handle filter */
54159   if (resources_filter && strcmp (resources_filter, ""))
54160     {
54161       int ret;
54162       ret = tag_add_resources_filter (new_tag, lc_resource_type,
54163                                       resources_filter);
54164 
54165       if (ret)
54166         {
54167           // Assume tag_add_resources_list return codes match
54168           sql_rollback ();
54169           g_free (lc_resource_type);
54170           return ret;
54171         }
54172     }
54173 
54174   g_free (lc_resource_type);
54175 
54176   if (tag)
54177     *tag = new_tag;
54178 
54179   sql_commit ();
54180 
54181   return 0;
54182 }
54183 
54184 /**
54185  * @brief Delete a tag.
54186  *
54187  * @param[in]  tag_id     UUID of tag.
54188  * @param[in]  ultimate   Whether to remove entirely, or to trashcan.
54189  *
54190  * @return 0 success, 2 failed to find tag, 99 permission denied, -1 error.
54191  */
54192 int
delete_tag(const char * tag_id,int ultimate)54193 delete_tag (const char *tag_id, int ultimate)
54194 {
54195   tag_t tag = 0;
54196 
54197   sql_begin_immediate ();
54198 
54199   if (acl_user_may ("delete_tag") == 0)
54200     {
54201       sql_rollback ();
54202       return 99;
54203     }
54204 
54205   if (find_tag_with_permission (tag_id, &tag, "delete_tag"))
54206     {
54207       sql_rollback ();
54208       return -1;
54209     }
54210 
54211   if (tag == 0)
54212     {
54213       if (find_trash ("tag", tag_id, &tag))
54214         {
54215           sql_rollback ();
54216           return -1;
54217         }
54218       if (tag == 0)
54219         {
54220           sql_rollback ();
54221           return 2;
54222         }
54223       if (ultimate == 0)
54224         {
54225           /* It's already in the trashcan. */
54226           sql_commit ();
54227           return 0;
54228         }
54229 
54230       permissions_set_orphans ("tag", tag, LOCATION_TRASH);
54231 
54232       sql ("DELETE FROM tag_resources_trash WHERE tag = %llu", tag);
54233       sql ("DELETE FROM tags_trash WHERE id = %llu;", tag);
54234       sql_commit ();
54235       return 0;
54236     }
54237 
54238   if (ultimate == 0)
54239     {
54240       tag_t trash_tag;
54241 
54242       sql ("INSERT INTO tags_trash"
54243            " (uuid, owner, name, comment, creation_time,"
54244            "  modification_time, resource_type, active, value)"
54245            " SELECT uuid, owner, name, comment, creation_time,"
54246            "        modification_time, resource_type, active, value"
54247            " FROM tags WHERE id = %llu;",
54248            tag);
54249 
54250       trash_tag = sql_last_insert_id ();
54251 
54252       sql ("INSERT INTO tag_resources_trash"
54253            "  (tag, resource_type, resource, resource_uuid, resource_location)"
54254            " SELECT"
54255            "   %llu, resource_type, resource, resource_uuid, resource_location"
54256            " FROM tag_resources WHERE tag = %llu;",
54257            trash_tag, tag);
54258 
54259       permissions_set_locations ("tag", tag, trash_tag, LOCATION_TRASH);
54260     }
54261   else
54262     {
54263       permissions_set_orphans ("tag", tag, LOCATION_TABLE);
54264       tags_remove_resource ("tag", tag, LOCATION_TABLE);
54265     }
54266 
54267   sql ("DELETE FROM tag_resources WHERE tag = %llu", tag);
54268   sql ("DELETE FROM tags WHERE id = %llu;", tag);
54269   sql_commit ();
54270 
54271   return 0;
54272 }
54273 
54274 /**
54275  * @brief Modify a tag.
54276  *
54277  * @param[in]  tag_id            UUID of tag.
54278  * @param[in]  name              New name of the tag or NULL.
54279  * @param[in]  comment           New comment for the tag or NULL.
54280  * @param[in]  value             New value of the tag or NULL.
54281  * @param[in]  resource_type     New resource type to attach the tag to or NULL.
54282  * @param[in]  resource_uuids    New Unique IDs of the resources to attach.
54283  * @param[in]  resources_filter  Filter to select resources to attach tag to.
54284  * @param[in]  resources_action  Resources action, e.g. "add" or "remove".
54285  * @param[in]  active            0 for inactive, any other for active or NULL.
54286  * @param[out] error_extra  Extra string for error (e.g. missing resource ID)
54287  *
54288  * @return 0 success, 1 failed to find tag, 2 tag_id required,
54289  *         3 unexpected resource action,
54290  *         4 resource ID not found (sets error_extra to UUID),
54291  *         5 filter returned no results, 6 too many resources selected,
54292  *         99 permission denied, -1 internal error.
54293  */
54294 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)54295 modify_tag (const char *tag_id, const char *name, const char *comment,
54296             const char *value, const char *resource_type,
54297             array_t *resource_uuids, const char *resources_filter,
54298             const char *resources_action, const char *active,
54299             gchar **error_extra)
54300 {
54301   gchar *quoted_name, *quoted_comment, *quoted_value;
54302   gchar *lc_resource_type, *quoted_resource_type;
54303   tag_t tag;
54304   gchar *current_resource_type;
54305 
54306   if (tag_id == NULL)
54307     return 2;
54308 
54309   sql_begin_immediate ();
54310 
54311   assert (current_credentials.uuid);
54312 
54313   if (acl_user_may ("modify_tag") == 0)
54314     {
54315       sql_rollback ();
54316       return 99;
54317     }
54318 
54319   tag = 0;
54320   if (find_tag_with_permission (tag_id, &tag, "modify_tag"))
54321     {
54322       sql_rollback ();
54323       return -1;
54324     }
54325 
54326   if (tag == 0)
54327     {
54328       sql_rollback ();
54329       return 1;
54330     }
54331 
54332   lc_resource_type = (resource_type
54333                       ? g_ascii_strdown (resource_type, -1)
54334                       : g_strdup (""));
54335   if (strcmp (lc_resource_type, "")
54336       && valid_db_resource_type (lc_resource_type) == 0)
54337     {
54338       sql_rollback ();
54339       return -1;
54340     }
54341 
54342   quoted_resource_type = sql_insert (lc_resource_type);
54343   quoted_name = sql_insert (name ? name : "");
54344   quoted_comment = sql_insert (comment ? comment : "");
54345   quoted_value = sql_insert (value ? value : "");
54346 
54347   if (name)
54348     {
54349       sql ("UPDATE tags SET"
54350            " name = %s"
54351            " WHERE id = %llu;",
54352            quoted_name,
54353            tag);
54354     }
54355 
54356   if (resource_type)
54357     {
54358       sql ("UPDATE tags SET"
54359            " resource_type = %s"
54360            " WHERE id = %llu;",
54361            quoted_resource_type,
54362            tag);
54363     }
54364 
54365   if (comment)
54366     {
54367       sql ("UPDATE tags SET"
54368            " comment = %s"
54369            " WHERE id = %llu;",
54370            quoted_comment,
54371            tag);
54372     }
54373 
54374   if (value)
54375     {
54376       sql ("UPDATE tags SET"
54377            " value = %s"
54378            " WHERE id = %llu;",
54379            quoted_value,
54380            tag);
54381     }
54382 
54383   if (active)
54384     {
54385       sql ("UPDATE tags SET"
54386            " active = %i"
54387            " WHERE id = %llu;",
54388            strcmp (active, "0") ? 1 : 0,
54389            tag);
54390     }
54391 
54392   sql ("UPDATE tags SET"
54393        " modification_time = %i"
54394        " WHERE id = %llu;",
54395        time (NULL),
54396        tag);
54397 
54398   g_free (quoted_name);
54399   g_free (quoted_resource_type);
54400   g_free (quoted_comment);
54401   g_free (quoted_value);
54402 
54403   current_resource_type = sql_string ("SELECT resource_type"
54404                                       " FROM tags"
54405                                       " WHERE id = %llu",
54406                                       tag);
54407 
54408   /* Clear old resources */
54409   if (resources_action == NULL
54410       || strcmp (resources_action, "") == 0
54411       || strcmp (resources_action, "set") == 0)
54412     {
54413       if (resource_uuids
54414           || (resources_filter && strcmp (resources_filter, "")))
54415         {
54416           sql ("DELETE FROM tag_resources WHERE tag = %llu", tag);
54417         }
54418     }
54419   else if (strcmp (resources_action, "add")
54420            && strcmp (resources_action, "remove"))
54421     {
54422       sql_rollback ();
54423       g_free (current_resource_type);
54424       g_free (lc_resource_type);
54425       return 3;
54426     }
54427 
54428   /* Handle resource IDs */
54429   if (resource_uuids)
54430     {
54431       int ret;
54432 
54433       if (resources_action && strcmp (resources_action, "remove") == 0)
54434         ret = tag_remove_resources_list (tag, current_resource_type,
54435                                          resource_uuids, error_extra);
54436       else
54437         ret = tag_add_resources_list (tag, current_resource_type,
54438                                       resource_uuids, error_extra);
54439 
54440       if (ret)
54441         {
54442           sql_rollback ();
54443           g_free (current_resource_type);
54444           g_free (lc_resource_type);
54445           // Assume return codes besides -1 are offset from create_tag
54446           if (ret > 0)
54447             return ret + 3;
54448           else
54449             return ret;
54450         }
54451     }
54452 
54453   /* Handle filter */
54454   if (resources_filter && strcmp (resources_filter, ""))
54455     {
54456       int ret;
54457 
54458       if (resources_action && strcmp (resources_action, "remove") == 0)
54459         ret = tag_remove_resources_filter (tag, current_resource_type,
54460                                            resources_filter);
54461       else
54462         ret = tag_add_resources_filter (tag, current_resource_type,
54463                                         resources_filter);
54464 
54465       if (ret)
54466         {
54467           // Assume tag_add_resources_list return codes match
54468           sql_rollback ();
54469           g_free (current_resource_type);
54470           g_free (lc_resource_type);
54471           // Assume return codes besides -1 are offset from create_tag
54472           if (ret > 0)
54473             return ret + 3;
54474           else
54475             return ret;
54476         }
54477     }
54478 
54479   g_free (current_resource_type);
54480   g_free (lc_resource_type);
54481 
54482   sql_commit ();
54483 
54484   return 0;
54485 }
54486 
54487 
54488 /**
54489  * @brief Filter columns for Tag iterator.
54490  */
54491 #define TAG_ITERATOR_FILTER_COLUMNS                                           \
54492  { GET_ITERATOR_FILTER_COLUMNS, "resource_type", "active", "value",           \
54493    "resources", NULL }
54494 
54495 /**
54496  * @brief Tag iterator columns.
54497  */
54498 #define TAG_ITERATOR_COLUMNS                                                  \
54499  {                                                                           \
54500    GET_ITERATOR_COLUMNS (tags),                                              \
54501    { "resource_type", NULL, KEYWORD_TYPE_STRING },                           \
54502    { "active", NULL, KEYWORD_TYPE_INTEGER },                                 \
54503    { "value", NULL, KEYWORD_TYPE_STRING },                                   \
54504    { "(SELECT count(*) FROM tag_resources"                                   \
54505      " WHERE tag = tags.id"                                                  \
54506      " AND resource_location = " G_STRINGIFY (LOCATION_TABLE) ")",           \
54507      "resources", KEYWORD_TYPE_INTEGER },                                    \
54508    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
54509  }
54510 
54511 /**
54512  * @brief Tag iterator trash columns.
54513  */
54514 #define TAG_ITERATOR_TRASH_COLUMNS                                           \
54515  {                                                                           \
54516    GET_ITERATOR_COLUMNS (tags_trash),                                        \
54517    { "resource_type", NULL, KEYWORD_TYPE_STRING },                           \
54518    { "active", NULL, KEYWORD_TYPE_INTEGER },                                 \
54519    { "value", NULL, KEYWORD_TYPE_STRING },                                   \
54520    { "(SELECT count(*) FROM tag_resources_trash"                             \
54521      " WHERE tag = tags_trash.id)",                                          \
54522      "resources", KEYWORD_TYPE_INTEGER },                                    \
54523    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                                      \
54524  }
54525 
54526 /**
54527  * @brief Filter columns for Tag name iterator.
54528  */
54529 #define TAG_NAME_ITERATOR_FILTER_COLUMNS                         \
54530  { "name", "resource_type", NULL }
54531 
54532 /**
54533  * @brief Tag name iterator columns.
54534  */
54535 #define TAG_NAME_ITERATOR_COLUMNS                                \
54536  {                                                               \
54537    { "name", NULL, KEYWORD_TYPE_STRING },                        \
54538    { "resource_type", NULL, KEYWORD_TYPE_STRING },               \
54539    { NULL, NULL, KEYWORD_TYPE_UNKNOWN }                          \
54540  }
54541 
54542 /**
54543  * @brief Initialise a tag iterator.
54544  *
54545  * @param[in]  iterator    Iterator.
54546  * @param[in]  get         GET data.
54547  *
54548  * @return 0 success, 1 failed to find tag, 2 failed to find filter,
54549  *         -1 error.
54550  */
54551 int
init_tag_iterator(iterator_t * iterator,const get_data_t * get)54552 init_tag_iterator (iterator_t* iterator, const get_data_t *get)
54553 {
54554   static const char *filter_columns[] = TAG_ITERATOR_FILTER_COLUMNS;
54555   static column_t columns[] = TAG_ITERATOR_COLUMNS;
54556   static column_t trash_columns[] = TAG_ITERATOR_TRASH_COLUMNS;
54557 
54558   return init_get_iterator (iterator,
54559                             "tag",
54560                             get,
54561                             columns,
54562                             trash_columns,
54563                             filter_columns,
54564                             0,
54565                             NULL,
54566                             NULL,
54567                             TRUE);
54568 }
54569 
54570 /**
54571  * @brief Count number of tags.
54572  *
54573  * @param[in]  get  GET params.
54574  *
54575  * @return Total number of tags in filtered set.
54576  */
54577 int
tag_count(const get_data_t * get)54578 tag_count (const get_data_t *get)
54579 {
54580   static const char *filter_columns[] = TAG_ITERATOR_FILTER_COLUMNS;
54581   static column_t columns[] = TAG_ITERATOR_COLUMNS;
54582   static column_t trash_columns[] = TAG_ITERATOR_TRASH_COLUMNS;
54583 
54584   return count ("tag", get, columns, trash_columns, filter_columns,
54585                   0, 0, 0, TRUE);
54586 }
54587 
54588 /**
54589  * @brief Get the resource_type from a Tag iterator.
54590  *
54591  * @param[in]  iterator  Iterator.
54592  *
54593  * @return The resource type attached to a tag.
54594  */
54595 DEF_ACCESS (tag_iterator_resource_type, GET_ITERATOR_COLUMN_COUNT);
54596 
54597 /**
54598  * @brief Get if a tag is active from a Tag iterator.
54599  *
54600  * @param[in]  iterator  Iterator.
54601  *
54602  * @return Whether a tag is active (0 = inactive, 1 = active).
54603  */
54604 int
tag_iterator_active(iterator_t * iterator)54605 tag_iterator_active (iterator_t* iterator)
54606 {
54607   int ret;
54608   if (iterator->done) return -1;
54609   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 1);
54610   return ret;
54611 }
54612 
54613 /**
54614  * @brief Get the value from a Tag iterator.
54615  *
54616  * @param[in]  iterator  Iterator.
54617  *
54618  * @return The value associated with a tag.
54619  */
54620 DEF_ACCESS (tag_iterator_value, GET_ITERATOR_COLUMN_COUNT + 2);
54621 
54622 /**
54623  * @brief Get number of resources linked to tag.
54624  *
54625  * @param[in]  iterator  Iterator.
54626  *
54627  * @return Count of resources linked to tag.
54628  */
54629 int
tag_iterator_resources(iterator_t * iterator)54630 tag_iterator_resources (iterator_t* iterator)
54631 {
54632   int ret;
54633   if (iterator->done) return -1;
54634   ret = iterator_int (iterator, GET_ITERATOR_COLUMN_COUNT + 3);
54635   return ret;
54636 }
54637 
54638 /**
54639  * @brief Initialise a iterator of tag names.
54640  *
54641  * @param[in]  iterator    Iterator.
54642  * @param[in]  get         GET params.
54643  *
54644  * @return 0 success, -1 error.
54645  */
54646 int
init_tag_name_iterator(iterator_t * iterator,const get_data_t * get)54647 init_tag_name_iterator (iterator_t* iterator, const get_data_t *get)
54648 {
54649   static const char *filter_columns[] = TAG_NAME_ITERATOR_FILTER_COLUMNS;
54650   static column_t columns[] = TAG_NAME_ITERATOR_COLUMNS;
54651 
54652   return init_get_iterator (iterator,
54653                             "tag",
54654                             get,
54655                             columns,
54656                             columns,
54657                             filter_columns,
54658                             1,
54659                             NULL,
54660                             NULL,
54661                             TRUE);
54662 }
54663 
54664 /**
54665  * @brief Get the name from a Tag name iterator.
54666  *
54667  * @param[in]  iterator  Iterator.
54668  *
54669  * @return The tag name.
54670  */
54671 DEF_ACCESS (tag_name_iterator_name, 0);
54672 
54673 /**
54674  * @brief Initialise a iterator of tags attached to a resource.
54675  *
54676  * @param[in]  iterator         Iterator.
54677  * @param[in]  type             Resource type.
54678  * @param[in]  resource         Resource.
54679  * @param[in]  active_only      Whether to select only active tags.
54680  * @param[in]  sort_field       Field to sort by.
54681  * @param[in]  ascending        Whether to sort in ascending order.
54682  *
54683  * @return 0 success, -1 error.
54684  */
54685 int
init_resource_tag_iterator(iterator_t * iterator,const char * type,resource_t resource,int active_only,const char * sort_field,int ascending)54686 init_resource_tag_iterator (iterator_t* iterator, const char* type,
54687                             resource_t resource, int active_only,
54688                             const char* sort_field, int ascending)
54689 {
54690   get_data_t get;
54691   gchar *owned_clause, *with_clause;
54692 
54693   assert (type);
54694   assert (resource);
54695   assert (current_credentials.uuid);
54696 
54697   get.trash = 0;
54698   owned_clause = acl_where_owned ("tag", &get, 1, "any", 0, NULL, 0,
54699                                   &with_clause);
54700 
54701   init_iterator (iterator,
54702                  "%s"
54703                  " SELECT id, uuid, name, value, comment"
54704                  " FROM tags"
54705                  " WHERE EXISTS"
54706                  "  (SELECT * FROM tag_resources"
54707                  "   WHERE resource_type = '%s'"
54708                  "   AND resource = %llu"
54709                  "   AND resource_location = %d"
54710                  "   AND tag = tags.id)"
54711                  "%s"
54712                  " AND %s"
54713                  " ORDER BY %s %s;",
54714                  with_clause ? with_clause : "",
54715                  type,
54716                  resource,
54717                  LOCATION_TABLE,
54718                  active_only ? " AND active=1" : "",
54719                  owned_clause,
54720                  sort_field ? sort_field : "active DESC, name",
54721                  ascending ? "ASC" : "DESC");
54722 
54723   g_free (with_clause);
54724   g_free (owned_clause);
54725   return 0;
54726 }
54727 
54728 /**
54729  * @brief Get the Tag UUID from a resource Tag iterator.
54730  *
54731  * @param[in]  iterator  Iterator.
54732  *
54733  * @return The UUID of the tag.
54734  */
54735 DEF_ACCESS (resource_tag_iterator_uuid, 1);
54736 
54737 /**
54738  * @brief Get the Tag name from a resource Tag iterator.
54739  *
54740  * @param[in]  iterator  Iterator.
54741  *
54742  * @return The name of the tag.
54743  */
54744 DEF_ACCESS (resource_tag_iterator_name, 2);
54745 
54746 /**
54747  * @brief Get the Tag value from a resource Tag iterator.
54748  *
54749  * @param[in]  iterator  Iterator.
54750  *
54751  * @return The value of the tag.
54752  */
54753 DEF_ACCESS (resource_tag_iterator_value, 3);
54754 
54755 /**
54756  * @brief Get the Tag comment from a resource Tag iterator.
54757  *
54758  * @param[in]  iterator  Iterator.
54759  *
54760  * @return The comment of the tag.
54761  */
54762 DEF_ACCESS (resource_tag_iterator_comment, 4);
54763 
54764 /**
54765  * @brief Check if there are tags attached to a resource.
54766  *
54767  * @param[in]  type         Resource type.
54768  * @param[in]  resource     Resource.
54769  * @param[in]  active_only  Whether to count only active tags.
54770  *
54771  * @return 1 if resource has tags, else 0.
54772  */
54773 int
resource_tag_exists(const char * type,resource_t resource,int active_only)54774 resource_tag_exists (const char* type, resource_t resource, int active_only)
54775 {
54776   int ret;
54777 
54778   assert (type);
54779   assert (resource);
54780 
54781   ret = sql_int ("SELECT EXISTS (SELECT *"
54782                  "               FROM tags"
54783                  "               WHERE resource_type = '%s'"
54784                  "               AND EXISTS"
54785                  "                   (SELECT * FROM tag_resources"
54786                  "                    WHERE tag = tags.id"
54787                  "                    AND resource = %llu"
54788                  "                    AND resource_location = %d"
54789                  "                    AND tags.resource_type"
54790                  "                        = tag_resources.resource_type)"
54791                  "               %s);",
54792                  type,
54793                  resource,
54794                  LOCATION_TABLE,
54795                  active_only ? "AND active=1": "");
54796 
54797   return ret;
54798 }
54799 
54800 /**
54801  * @brief Count number of tags attached to a resource.
54802  *
54803  * @param[in]  type         Resource type.
54804  * @param[in]  resource     Resource.
54805  * @param[in]  active_only  Whether to count only active tags.
54806  *
54807  * @return Total number of tags attached to the resource.
54808  */
54809 int
resource_tag_count(const char * type,resource_t resource,int active_only)54810 resource_tag_count (const char* type, resource_t resource, int active_only)
54811 {
54812   int ret;
54813 
54814   assert (type);
54815   assert (resource);
54816 
54817   ret = sql_int ("SELECT count (id)"
54818                  " FROM tags"
54819                  " WHERE resource_type = '%s'"
54820                  "   AND EXISTS"
54821                  "     (SELECT * FROM tag_resources"
54822                  "      WHERE tag = tags.id"
54823                  "        AND resource = %llu"
54824                  "        AND resource_location = %d"
54825                  "        AND tags.resource_type = tag_resources.resource_type)"
54826                  "   %s;",
54827                  type,
54828                  resource,
54829                  LOCATION_TABLE,
54830                  active_only ? "AND active=1": "");
54831 
54832   return ret;
54833 }
54834 
54835 /**
54836  * @brief Return whether a tag is in use by a task.
54837  *
54838  * @param[in]  tag  Tag.
54839  *
54840  * @return 1 if in use, else 0.
54841  */
54842 int
tag_in_use(tag_t tag)54843 tag_in_use (tag_t tag)
54844 {
54845   return 0;
54846 }
54847 
54848 /**
54849  * @brief Return whether a trashcan tag is referenced by a task.
54850  *
54851  * @param[in]  tag  Tag.
54852  *
54853  * @return 1 if in use, else 0.
54854  */
54855 int
trash_tag_in_use(tag_t tag)54856 trash_tag_in_use (tag_t tag)
54857 {
54858   return 0;
54859 }
54860 
54861 /**
54862  * @brief Return whether a tag is writable.
54863  *
54864  * @param[in]  tag  Tag.
54865  *
54866  * @return 1 if writable, else 0.
54867  */
54868 int
tag_writable(tag_t tag)54869 tag_writable (tag_t tag)
54870 {
54871   return 1;
54872 }
54873 
54874 /**
54875  * @brief Return whether a trashcan tag is writable.
54876  *
54877  * @param[in]  tag  Tag.
54878  *
54879  * @return 1 if writable, else 0.
54880  */
54881 int
trash_tag_writable(tag_t tag)54882 trash_tag_writable (tag_t tag)
54883 {
54884   return 0;
54885 }
54886 
54887 /**
54888  * @brief Return whether a column is one containing timestamp.
54889  *
54890  * @param[in]  column  The column name.
54891  *
54892  * @return  1 if column contains timestamps, 0 otherwise.
54893  */
54894 int
column_is_timestamp(const char * column)54895 column_is_timestamp (const char* column)
54896 {
54897   return column
54898          && (strcmp (column, "created") == 0
54899              || strcmp (column, "date") == 0
54900              || strcmp (column, "modified") == 0
54901              || strcmp (column, "published") == 0
54902              || strcmp (column, "updated") == 0);
54903 }
54904 
54905 /**
54906  * @brief Return the columns for a resource iterator.
54907  *
54908  * @param[in]  type             Resource type to get columns of.
54909  *
54910  * @return The columns.
54911  */
54912 static column_t *
type_select_columns(const char * type)54913 type_select_columns (const char *type)
54914 {
54915   static column_t alert_columns[] = ALERT_ITERATOR_COLUMNS;
54916   static column_t cert_bund_adv_columns[] = CERT_BUND_ADV_INFO_ITERATOR_COLUMNS;
54917   static column_t config_columns[] = CONFIG_ITERATOR_COLUMNS;
54918   static column_t cpe_columns[] = CPE_INFO_ITERATOR_COLUMNS;
54919   static column_t credential_columns[] = CREDENTIAL_ITERATOR_COLUMNS;
54920   static column_t cve_columns[] = CVE_INFO_ITERATOR_COLUMNS;
54921   static column_t dfn_cert_adv_columns[] = DFN_CERT_ADV_INFO_ITERATOR_COLUMNS;
54922   static column_t filter_columns[] = FILTER_ITERATOR_COLUMNS;
54923   static column_t group_columns[] = GROUP_ITERATOR_COLUMNS;
54924   static column_t host_columns[] = HOST_ITERATOR_COLUMNS;
54925   static column_t note_columns[] = NOTE_ITERATOR_COLUMNS;
54926   static column_t nvt_columns[] = NVT_ITERATOR_COLUMNS;
54927   static column_t os_columns[] = OS_ITERATOR_COLUMNS;
54928   static column_t ovaldef_columns[] = OVALDEF_INFO_ITERATOR_COLUMNS;
54929   static column_t override_columns[] = OVERRIDE_ITERATOR_COLUMNS;
54930   static column_t permission_columns[] = PERMISSION_ITERATOR_COLUMNS;
54931   static column_t report_columns[] = REPORT_ITERATOR_COLUMNS;
54932   static column_t result_columns[] = RESULT_ITERATOR_COLUMNS;
54933   static column_t result_columns_no_cert[] = RESULT_ITERATOR_COLUMNS_NO_CERT;
54934   static column_t role_columns[] = ROLE_ITERATOR_COLUMNS;
54935   static column_t scanner_columns[] = SCANNER_ITERATOR_COLUMNS;
54936   static column_t schedule_columns[] = SCHEDULE_ITERATOR_COLUMNS;
54937   static column_t tag_columns[] = TAG_ITERATOR_COLUMNS;
54938   static column_t target_columns[] = TARGET_ITERATOR_COLUMNS;
54939   static column_t task_columns[] = TASK_ITERATOR_COLUMNS;
54940   static column_t user_columns[] = USER_ITERATOR_COLUMNS;
54941   static column_t vuln_columns[] = VULN_ITERATOR_COLUMNS;
54942 
54943   if (type == NULL)
54944     return NULL;
54945   if (strcasecmp (type, "ALERT") == 0)
54946     return alert_columns;
54947   if (strcasecmp (type, "CERT_BUND_ADV") == 0)
54948     return cert_bund_adv_columns;
54949   if (strcasecmp (type, "CONFIG") == 0)
54950     return config_columns;
54951   if (strcasecmp (type, "CPE") == 0)
54952     return cpe_columns;
54953   if (strcasecmp (type, "CREDENTIAL") == 0)
54954     return credential_columns;
54955   if (strcasecmp (type, "CVE") == 0)
54956     return cve_columns;
54957   if (strcasecmp (type, "DFN_CERT_ADV") == 0)
54958     return dfn_cert_adv_columns;
54959   if (strcasecmp (type, "FILTER") == 0)
54960     return filter_columns;
54961   if (strcasecmp (type, "GROUP") == 0)
54962     return group_columns;
54963   if (strcasecmp (type, "HOST") == 0)
54964     return host_columns;
54965   if (strcasecmp (type, "NOTE") == 0)
54966     return note_columns;
54967   if (strcasecmp (type, "NVT") == 0)
54968     return nvt_columns;
54969   if (strcasecmp (type, "OS") == 0)
54970     return os_columns;
54971   if (strcasecmp (type, "OVALDEF") == 0)
54972     return ovaldef_columns;
54973   if (strcasecmp (type, "OVERRIDE") == 0)
54974     return override_columns;
54975   if (strcasecmp (type, "PERMISSION") == 0)
54976     return permission_columns;
54977   if (strcasecmp (type, "PORT_LIST") == 0)
54978     return port_list_select_columns ();
54979   if (strcasecmp (type, "REPORT") == 0)
54980     return report_columns;
54981   if (strcasecmp (type, "REPORT_FORMAT") == 0)
54982     return report_format_select_columns ();
54983   if (strcasecmp (type, "RESULT") == 0)
54984     {
54985       if (manage_cert_loaded ())
54986         return result_columns;
54987       return result_columns_no_cert;
54988     }
54989   if (strcasecmp (type, "ROLE") == 0)
54990     return role_columns;
54991   if (strcasecmp (type, "SCANNER") == 0)
54992     return scanner_columns;
54993   if (strcasecmp (type, "SCHEDULE") == 0)
54994     return schedule_columns;
54995   if (strcasecmp (type, "TAG") == 0)
54996     return tag_columns;
54997   if (strcasecmp (type, "TARGET") == 0)
54998     return target_columns;
54999   if (strcasecmp (type, "TASK") == 0)
55000     return task_columns;
55001   /* Tickets don't use this. */
55002   assert (strcasecmp (type, "ticket"));
55003   if (strcasecmp (type, "TLS_CERTIFICATE") == 0)
55004     return tls_certificate_select_columns ();
55005   if (strcasecmp (type, "USER") == 0)
55006     return user_columns;
55007   if (strcasecmp (type, "VULN") == 0)
55008     return vuln_columns;
55009   return NULL;
55010 }
55011 
55012 /**
55013  * @brief Return the columns for a resource iterator.
55014  *
55015  * @param[in]  type             Resource type to get columns of.
55016  *
55017  * @return The columns.
55018  */
55019 static column_t *
type_where_columns(const char * type)55020 type_where_columns (const char *type)
55021 {
55022   static column_t task_columns[] = TASK_ITERATOR_WHERE_COLUMNS;
55023   static column_t report_columns[] = REPORT_ITERATOR_WHERE_COLUMNS;
55024   static column_t host_columns[] = HOST_ITERATOR_WHERE_COLUMNS;
55025   static column_t os_columns[] = OS_ITERATOR_WHERE_COLUMNS;
55026 
55027   if (type == NULL)
55028     return NULL;
55029   if (strcasecmp (type, "TASK") == 0)
55030     return task_columns;
55031   if (strcasecmp (type, "REPORT") == 0)
55032     return report_columns;
55033   if (strcasecmp (type, "HOST") == 0)
55034     return host_columns;
55035   if (strcasecmp (type, "OS") == 0)
55036     return os_columns;
55037   return NULL;
55038 }
55039 
55040 /**
55041  * @brief Return the filter columns for a resource iterator.
55042  *
55043  * @param[in]  type             Resource type to get columns of.
55044  *
55045  * @return The filter columns.
55046  */
55047 static const char**
type_filter_columns(const char * type)55048 type_filter_columns (const char *type)
55049 {
55050   if (type == NULL)
55051     return NULL;
55052   if (strcasecmp (type, "ALERT") == 0)
55053     {
55054       static const char *ret[] = ALERT_ITERATOR_FILTER_COLUMNS;
55055       return ret;
55056     }
55057   if (strcasecmp (type, "CERT_BUND_ADV") == 0)
55058     {
55059       static const char *ret[] = CERT_BUND_ADV_INFO_ITERATOR_FILTER_COLUMNS;
55060       return ret;
55061     }
55062   if (strcasecmp (type, "CONFIG") == 0)
55063     {
55064       static const char *ret[] = CONFIG_ITERATOR_FILTER_COLUMNS;
55065       return ret;
55066     }
55067   if (strcasecmp (type, "CREDENTIAL") == 0)
55068     {
55069       static const char *ret[] = CREDENTIAL_ITERATOR_FILTER_COLUMNS;
55070       return ret;
55071     }
55072   if (strcasecmp (type, "CPE") == 0)
55073     {
55074       static const char *ret[] = CPE_INFO_ITERATOR_FILTER_COLUMNS;
55075       return ret;
55076     }
55077   if (strcasecmp (type, "CVE") == 0)
55078     {
55079       static const char *ret[] = CVE_INFO_ITERATOR_FILTER_COLUMNS;
55080       return ret;
55081     }
55082   if (strcasecmp (type, "DFN_CERT_ADV") == 0)
55083     {
55084       static const char *ret[] = DFN_CERT_ADV_INFO_ITERATOR_FILTER_COLUMNS;
55085       return ret;
55086     }
55087   if (strcasecmp (type, "FILTER") == 0)
55088     {
55089       static const char *ret[] = FILTER_ITERATOR_FILTER_COLUMNS;
55090       return ret;
55091     }
55092   if (strcasecmp (type, "GROUP") == 0)
55093     {
55094       static const char *ret[] = GROUP_ITERATOR_FILTER_COLUMNS;
55095       return ret;
55096     }
55097   if (strcasecmp (type, "HOST") == 0)
55098     {
55099       static const char *ret[] = HOST_ITERATOR_FILTER_COLUMNS;
55100       return ret;
55101     }
55102   if (strcasecmp (type, "NOTE") == 0)
55103     {
55104       static const char *ret[] = NOTE_ITERATOR_FILTER_COLUMNS;
55105       return ret;
55106     }
55107   if (strcasecmp (type, "NVT") == 0)
55108     {
55109       static const char *ret[] = NVT_INFO_ITERATOR_FILTER_COLUMNS;
55110       return ret;
55111     }
55112   if (strcasecmp (type, "OS") == 0)
55113     {
55114       static const char *ret[] = OS_ITERATOR_FILTER_COLUMNS;
55115       return ret;
55116     }
55117   if (strcasecmp (type, "OVALDEF") == 0)
55118     {
55119       static const char *ret[] = OVALDEF_INFO_ITERATOR_FILTER_COLUMNS;
55120       return ret;
55121     }
55122   if (strcasecmp (type, "OVERRIDE") == 0)
55123     {
55124       static const char *ret[] = OVERRIDE_ITERATOR_FILTER_COLUMNS;
55125       return ret;
55126     }
55127   if (strcasecmp (type, "PERMISSION") == 0)
55128     {
55129       static const char *ret[] = PERMISSION_ITERATOR_FILTER_COLUMNS;
55130       return ret;
55131     }
55132   if (strcasecmp (type, "PORT_LIST") == 0)
55133     return port_list_filter_columns ();
55134   if (strcasecmp (type, "REPORT") == 0)
55135     {
55136       static const char *ret[] = REPORT_ITERATOR_FILTER_COLUMNS;
55137       return ret;
55138     }
55139   if (strcasecmp (type, "REPORT_FORMAT") == 0)
55140     return report_format_filter_columns ();
55141   if (strcasecmp (type, "RESULT") == 0)
55142     {
55143       static const char *ret[] = RESULT_ITERATOR_FILTER_COLUMNS;
55144       return ret;
55145     }
55146   if (strcasecmp (type, "ROLE") == 0)
55147     {
55148       static const char *ret[] = ROLE_ITERATOR_FILTER_COLUMNS;
55149       return ret;
55150     }
55151   if (strcasecmp (type, "SCANNER") == 0)
55152     {
55153       static const char *ret[] = SCANNER_ITERATOR_FILTER_COLUMNS;
55154       return ret;
55155     }
55156   if (strcasecmp (type, "SCHEDULE") == 0)
55157     {
55158       static const char *ret[] = SCHEDULE_ITERATOR_FILTER_COLUMNS;
55159       return ret;
55160     }
55161   if (strcasecmp (type, "TAG") == 0)
55162     {
55163       static const char *ret[] = TAG_ITERATOR_FILTER_COLUMNS;
55164       return ret;
55165     }
55166   if (strcasecmp (type, "TARGET") == 0)
55167     {
55168       static const char *ret[] = TARGET_ITERATOR_FILTER_COLUMNS;
55169       return ret;
55170     }
55171   if (strcasecmp (type, "TASK") == 0)
55172     {
55173       static const char *ret[] = TASK_ITERATOR_FILTER_COLUMNS;
55174       return ret;
55175     }
55176   /* Tickets don't use this. */
55177   assert (strcasecmp (type, "ticket"));
55178   if (strcasecmp (type, "TLS_CERTIFICATE") == 0)
55179     return tls_certificate_filter_columns ();
55180   if (strcasecmp (type, "USER") == 0)
55181     {
55182       static const char *ret[] = USER_ITERATOR_FILTER_COLUMNS;
55183       return ret;
55184     }
55185   if (strcasecmp (type, "VULN") == 0)
55186     {
55187       static const char *ret[] = VULN_ITERATOR_FILTER_COLUMNS;
55188       return ret;
55189     }
55190   return NULL;
55191 
55192 }
55193 
55194 /**
55195  * @brief Return the opts subquery definition for a resource type.
55196  *
55197  * @param[in]  type    Resource type to get columns of.
55198  * @param[in]  filter  Filter to apply.
55199  *
55200  * @return The SQL subquery definition.
55201  */
55202 static gchar*
type_opts_table(const char * type,const char * filter)55203 type_opts_table (const char *type, const char *filter)
55204 {
55205   if (type == NULL)
55206     return NULL;
55207   if (strcasecmp (type, "TASK") == 0)
55208     return task_iterator_opts_table (filter_term_apply_overrides (filter),
55209                                      filter_term_min_qod (filter), 0);
55210   if (strcasecmp (type, "OS") == 0)
55211     return asset_os_iterator_opts_table ();
55212   if (strcasecmp (type, "REPORT") == 0)
55213     return report_iterator_opts_table (filter_term_apply_overrides (filter),
55214                                        filter_term_min_qod (filter));
55215   if (strcasecmp (type, "RESULT") == 0)
55216     return result_iterator_opts_table (filter_term_apply_overrides (filter),
55217                                        setting_dynamic_severity_int ());
55218   if (strcasecmp (type, "VULN") == 0)
55219     {
55220       gchar *task_id, *report_id, *host;
55221       gchar *ret;
55222 
55223       task_id = filter_term_value (filter, "task_id");
55224       report_id = filter_term_value (filter, "report_id");
55225       host = filter_term_value (filter, "host");
55226 
55227       ret = vuln_iterator_opts_table (task_id, report_id, host,
55228                                       filter_term_min_qod (filter));
55229 
55230       g_free (task_id);
55231       g_free (report_id);
55232       g_free (host);
55233 
55234       return ret;
55235     }
55236   return NULL;
55237 }
55238 
55239 /**
55240  * @brief Return the table name or union for a resource type.
55241  *
55242  * @param[in]  type   Resource type to get columns of.
55243  * @param[in]  trash  Whether to get the trash table.
55244  *
55245  * @return The SQL column definitions.
55246  */
55247 static char*
type_table(const char * type,int trash)55248 type_table (const char *type, int trash)
55249 {
55250   if (type == NULL)
55251     return NULL;
55252   if (trash && type_trash_in_table (type) == 0)
55253     return g_strdup_printf ("%ss_trash", type);
55254   if (trash == 0 || type_trash_in_table (type))
55255     return g_strdup_printf ("%ss", type);
55256   return NULL;
55257 }
55258 
55259 /**
55260  * @brief Return addition to the WHERE clause if required for a resource type.
55261  *
55262  * @param[in]  type     Resource type to get columns of.
55263  * @param[in]  trash    Whether to get the trash table.
55264  * @param[in]  filter   The filter term.
55265  * @param[in]  extra_params  Optional extra parameters.
55266  *
55267  * @return The newly allocated WHERE clause additions.
55268  */
55269 static gchar*
type_extra_where(const char * type,int trash,const char * filter,GHashTable * extra_params)55270 type_extra_where (const char *type, int trash, const char *filter,
55271                   GHashTable *extra_params)
55272 {
55273   gchar *extra_where;
55274 
55275   if (strcasecmp (type, "CONFIG") == 0 && extra_params)
55276     {
55277       gchar *usage_type;
55278       if (extra_params)
55279         usage_type = g_hash_table_lookup (extra_params, "usage_type");
55280       else
55281         usage_type = NULL;
55282 
55283       extra_where = configs_extra_where (usage_type);
55284       if (extra_where == NULL)
55285         extra_where = g_strdup ("");
55286     }
55287   else if (strcasecmp (type, "TASK") == 0)
55288     {
55289       gchar *usage_type;
55290       if (extra_params)
55291         usage_type = g_hash_table_lookup (extra_params, "usage_type");
55292       else
55293         usage_type = NULL;
55294 
55295       extra_where = tasks_extra_where (trash, usage_type);
55296     }
55297   else if (strcasecmp (type, "TLS_CERTIFICATE") == 0)
55298     {
55299       extra_where = tls_certificate_extra_where (filter);
55300     }
55301   else if (strcasecmp (type, "REPORT") == 0)
55302     {
55303       if (trash)
55304         extra_where = g_strdup (" AND (SELECT hidden FROM tasks"
55305                                 "      WHERE tasks.id = task)"
55306                                 "     = 2");
55307       else
55308         extra_where = g_strdup (" AND (SELECT hidden FROM tasks"
55309                                 "      WHERE tasks.id = task)"
55310                                 "     = 0");
55311     }
55312   else if (strcasecmp (type, "RESULT") == 0)
55313     {
55314       int apply_overrides;
55315       gchar *report_id;
55316       report_t report;
55317 
55318       /* Note: This keyword may be removed or renamed at any time once there
55319        * is a better solution like an operator for conditions that must always
55320        * apply or support for parentheses in filters. */
55321       report_id = filter_term_value (filter,
55322                                      "_and_report_id");
55323       report = 0;
55324 
55325       if (report_id)
55326         {
55327           if (find_report_with_permission (report_id,
55328                                            &report,
55329                                            NULL))
55330             {
55331               g_free (report_id);
55332               g_warning ("Failed to get report");
55333               return NULL;
55334             }
55335 
55336           if (report == 0)
55337             report = -1;
55338         }
55339       g_free (report_id);
55340 
55341       apply_overrides = filter_term_apply_overrides (filter);
55342 
55343       extra_where = results_extra_where (trash, report, NULL,
55344                                          apply_overrides,
55345                                          setting_dynamic_severity_int (),
55346                                          filter,
55347                                          NULL);
55348     }
55349   else if (strcasecmp (type, "VULN") == 0)
55350     {
55351       extra_where = vulns_extra_where ();
55352     }
55353   else
55354     extra_where = g_strdup ("");
55355 
55356   return extra_where;
55357 }
55358 
55359 /**
55360  * @brief Get the extra WITH clauses for a resource type.
55361  *
55362  * @param[in]  type  The resource type.
55363  *
55364  * @return The extra WITH clauses.
55365  */
55366 static const char *
type_extra_with(const char * type)55367 type_extra_with (const char *type)
55368 {
55369   return NULL;
55370 }
55371 
55372 /**
55373  * @brief Builds a filtered SELECT statement for a certain type.
55374  *
55375  * @param[in]  type               Resource type
55376  * @param[in]  columns_str        Columns to get (as used in SQL)
55377  * @param[in]  get                The get data
55378  * @param[in]  distinct           Whether to get distinct items
55379  * @param[in]  ordered            Whether to apply the ordering
55380  * @param[in]  extra_tables       Extra tables / subqueries
55381  * @param[in]  given_extra_where  Extra expressions for WHERE clause
55382  * @param[in]  group_by           Column(s) to group by, NULL for no grouping
55383  * @param[out] select             Output of newly allocated SELECT statement
55384  *
55385  * @return 0: success, 1: filter not found, -1 error.
55386  */
55387 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)55388 type_build_select (const char *type, const char *columns_str,
55389                    const get_data_t *get,
55390                    gboolean distinct, gboolean ordered,
55391                    const char *extra_tables, const char *given_extra_where,
55392                    const char *group_by,
55393                    gchar **select)
55394 {
55395   gchar *filter, *with;
55396   gchar *from_table, *opts_table;
55397   gchar *clause, *extra_where, *filter_order;
55398   int first, max;
55399   gchar *owned_clause, *owner_filter;
55400   array_t *permissions;
55401   const char *extra_with;
55402 
55403   column_t *select_columns, *where_columns;
55404   const char **filter_columns;
55405   gchar *pagination_clauses;
55406 
55407   assert (select);
55408 
55409   // Get filter
55410   if (get->filt_id && strcmp (get->filt_id, FILT_ID_NONE))
55411     {
55412       if (get->filter_replacement)
55413         /* Replace the filter term with one given by the caller.  This is
55414          * used by GET_REPORTS to use the default filter with any task (when
55415          * given the special value of -3 in filt_id). */
55416         filter = g_strdup (get->filter_replacement);
55417       else
55418         filter = filter_term (get->filt_id);
55419       if (filter == NULL)
55420         {
55421           return 1;
55422         }
55423     }
55424   else
55425     filter = NULL;
55426 
55427   // FROM ... part
55428   from_table = type_table (type, get->trash);
55429 
55430   opts_table = type_opts_table (type, filter ? filter : get->filter);
55431   if (strcasecmp (type, "RESULT") == 0)
55432     {
55433       gchar *original;
55434       int overrides, dynamic;
55435 
55436       overrides = filter_term_apply_overrides (filter ? filter : get->filter);
55437       dynamic = setting_dynamic_severity_int ();
55438 
55439       original = opts_table;
55440 
55441       opts_table = g_strdup_printf (" LEFT OUTER JOIN nvts"
55442                                     " ON results.nvt = nvts.oid %s,"
55443                                     " LATERAL %s AS lateral_new_severity",
55444                                     original,
55445                                     result_iterator_lateral (overrides,
55446                                                              dynamic));
55447       g_free (original);
55448     }
55449 
55450   // WHERE ... part
55451   select_columns = type_select_columns (type);
55452   where_columns = type_where_columns (type);
55453   filter_columns = type_filter_columns (type);
55454 
55455   if (filter_columns == NULL)
55456     return -1;
55457 
55458   clause = filter_clause (type, filter ? filter : get->filter, filter_columns,
55459                           select_columns, where_columns, get->trash,
55460                           &filter_order, &first, &max, &permissions,
55461                           &owner_filter);
55462 
55463   owned_clause = acl_where_owned (type, get, type_owned (type),
55464                                   owner_filter, 0, permissions, 0,
55465                                   &with);
55466 
55467   if (given_extra_where)
55468     extra_where = g_strdup (given_extra_where);
55469   else
55470     extra_where = type_extra_where (type, get->trash,
55471                                     filter ? filter : get->filter,
55472                                     get->extra_params);
55473 
55474   if (get->ignore_pagination)
55475     pagination_clauses = NULL;
55476   else
55477     pagination_clauses = g_strdup_printf (" LIMIT %s OFFSET %d",
55478                                           sql_select_limit (max),
55479                                           first);
55480 
55481   extra_with = type_extra_with (type);
55482 
55483   if (extra_with)
55484     {
55485       if (with)
55486         {
55487           gchar *old_with;
55488 
55489           old_with = with;
55490           with = g_strdup_printf ("%s, %s", old_with, extra_with);
55491           g_free (old_with);
55492         }
55493       else
55494         with = g_strdup_printf ("WITH %s", extra_with);
55495     }
55496 
55497   *select = g_strdup_printf
55498              ("%s"           // WITH
55499               "SELECT%s %s"  // DISTINCT, columns
55500               " FROM %s%s%s" // from_table, opts_table, extra_tables
55501               " WHERE"
55502               " %s%s"        // owned_clause, extra_where
55503               " %s%s%s"      // (filter) clause
55504               " %s%s"        // group_by
55505               " %s"          // ORDER BY (filter_order)
55506               " %s",         // pagination_clauses
55507               with ? with : "",
55508               distinct ? " DISTINCT" : "",
55509               columns_str,
55510               from_table,
55511               opts_table ? opts_table : "",
55512               extra_tables ? extra_tables : "",
55513               owned_clause,
55514               extra_where,
55515               clause ? " AND (" : "",
55516               clause ? clause : "",
55517               clause ? ")" : "",
55518               group_by ? " GROUP BY " : "",
55519               group_by ? group_by : "",
55520               (ordered && filter_order) ? filter_order : "",
55521               pagination_clauses ? pagination_clauses : "");
55522 
55523   g_free (with);
55524   g_free (from_table);
55525   g_free (opts_table);
55526   g_free (owned_clause);
55527   g_free (extra_where);
55528   g_free (pagination_clauses);
55529 
55530   return 0;
55531 }
55532 
55533 /**
55534  * @brief Remove a resource from tags.
55535  *
55536  * @param[in]  type      Type.
55537  * @param[in]  resource  Resource.
55538  * @param[in]  location  Location: table or trash.
55539  */
55540 void
tags_remove_resource(const char * type,resource_t resource,int location)55541 tags_remove_resource (const char *type, resource_t resource, int location)
55542 {
55543   sql ("DELETE FROM tag_resources"
55544        " WHERE resource_type = '%s' AND resource = %llu"
55545        " AND resource_location = %i;",
55546        type,
55547        resource,
55548        location);
55549 }
55550 
55551 /**
55552  * @brief Adjust location of resource in tags.
55553  *
55554  * @param[in]   type  Type.
55555  * @param[in]   old   Resource ID in old table.
55556  * @param[in]   new   Resource ID in new table.
55557  * @param[in]   to    Destination, trash or table.
55558  */
55559 void
tags_set_locations(const char * type,resource_t old,resource_t new,int to)55560 tags_set_locations (const char *type, resource_t old, resource_t new,
55561                     int to)
55562 {
55563   sql ("UPDATE tag_resources SET resource_location = %i, resource = %llu"
55564        " WHERE resource_type = '%s' AND resource = %llu"
55565        " AND resource_location = %i;",
55566        to,
55567        new,
55568        type,
55569        old,
55570        to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
55571   sql ("UPDATE tag_resources_trash SET resource_location = %i, resource = %llu"
55572        " WHERE resource_type = '%s' AND resource = %llu"
55573        " AND resource_location = %i;",
55574        to,
55575        new,
55576        type,
55577        old,
55578        to == LOCATION_TABLE ? LOCATION_TRASH : LOCATION_TABLE);
55579 }
55580 
55581 /**
55582  * @brief  Get a GArray of all users as user_t.
55583  *
55584  * @return  Newly allocated GArray containing all users.
55585  */
55586 static GArray*
all_users_array()55587 all_users_array ()
55588 {
55589   iterator_t users_iter;
55590   GArray *ret;
55591 
55592   ret = g_array_new (TRUE, TRUE, sizeof (resource_t));
55593 
55594   init_iterator (&users_iter, "SELECT id FROM users;");
55595 
55596   while (next (&users_iter))
55597     {
55598       user_t user = iterator_int64 (&users_iter, 0);
55599       g_array_append_val (ret, user);
55600     }
55601 
55602   cleanup_iterator (&users_iter);
55603 
55604   return ret;
55605 }
55606 
55607 /**
55608  * @brief Update permissions cache for a resource.
55609  *
55610  * @param[in]  type         Resource type.
55611  * @param[in]  resource     The resource to update the cache for.
55612  * @param[in]  cache_users  GArray of users to create cache for or NULL for all.
55613  */
55614 static void
cache_permissions_for_resource(const char * type,resource_t resource,GArray * cache_users)55615 cache_permissions_for_resource (const char *type, resource_t resource,
55616                                 GArray *cache_users)
55617 {
55618   int free_users;
55619 
55620   if (type == NULL || resource == 0 || resource == -1)
55621     return;
55622 
55623   if (cache_users == NULL)
55624     {
55625       g_debug ("%s: Getting all users", __func__);
55626       free_users = 1;
55627       cache_users = all_users_array ();
55628     }
55629   else
55630     free_users = 0;
55631 
55632   if (strcmp (type, "task") == 0)
55633     {
55634       char* old_current_user_id;
55635       gchar *resource_id;
55636       int user_index;
55637 
55638       old_current_user_id = current_credentials.uuid;
55639       resource_id = resource_uuid (type, resource);
55640 
55641       g_debug ("%s: Caching permissions on %s \"%s\" for %d user(s)",
55642                __func__, type, resource_id, cache_users->len);
55643 
55644       for (user_index = 0; user_index < cache_users->len; user_index++)
55645         {
55646           user_t user;
55647           gchar *user_id;
55648 
55649           user = g_array_index (cache_users, user_t, user_index);
55650           user_id = user_uuid (user);
55651 
55652           current_credentials.uuid = user_id;
55653           manage_session_init (user_id);
55654 
55655           if (sql_int ("SELECT count(*) FROM permissions_get_%ss"
55656                        " WHERE \"user\" = %llu"
55657                        "   AND %s = %llu;",
55658                        type,
55659                        user,
55660                        type,
55661                        resource))
55662             {
55663               sql ("UPDATE permissions_get_%ss"
55664                    "  SET has_permission"
55665                    "       = user_has_access_uuid (cast ('%s' as text),"
55666                    "                               cast ('%s' as text),"
55667                    "                               cast ('get_%ss' as text),"
55668                    "                               0)"
55669                    " WHERE \"user\" = %llu"
55670                    "   AND %s = %llu;",
55671                    type,
55672                    type,
55673                    resource_id,
55674                    type,
55675                    user,
55676                    type,
55677                    resource);
55678             }
55679           else
55680             {
55681               sql ("INSERT INTO permissions_get_%ss"
55682                    "              (\"user\", %s, has_permission)"
55683                    "  SELECT %llu, %llu,"
55684                    "         user_has_access_uuid (cast ('%s' as text),"
55685                    "                               cast ('%s' as text),"
55686                    "                               cast ('get_%ss' as text),"
55687                    "                               0);",
55688                    type,
55689                    type,
55690                    user,
55691                    resource,
55692                    type,
55693                    resource_id,
55694                    type);
55695             }
55696 
55697           g_free (user_id);
55698           current_credentials.uuid = NULL;
55699         }
55700 
55701       current_credentials.uuid = old_current_user_id;
55702       manage_session_init (old_current_user_id);
55703 
55704       g_free (resource_id);
55705     }
55706 
55707   if (free_users)
55708     g_array_free (cache_users, TRUE);
55709 }
55710 
55711 /**
55712  * @brief Update permissions cache for a given type and selection of users.
55713  *
55714  * @param[in]  type         Type.
55715  * @param[in]  cache_users  GArray of users to create cache for.
55716  */
55717 static void
cache_permissions_for_users(const char * type,GArray * cache_users)55718 cache_permissions_for_users (const char *type, GArray *cache_users)
55719 {
55720   int free_users;
55721 
55722   if (type == NULL)
55723     return;
55724 
55725   if (cache_users == NULL)
55726     {
55727       g_debug ("%s: Getting all users", __func__);
55728       free_users = 1;
55729       cache_users = all_users_array ();
55730     }
55731   else
55732     free_users = 0;
55733 
55734   if (strcmp (type, "task") == 0)
55735     {
55736       iterator_t resources;
55737 
55738       init_iterator (&resources, "SELECT id FROM %ss;", type);
55739 
55740       while (next (&resources))
55741         {
55742           resource_t resource = iterator_int64 (&resources, 0);
55743           cache_permissions_for_resource (type, resource, cache_users);
55744         }
55745 
55746       cleanup_iterator (&resources);
55747     }
55748 
55749   if (free_users)
55750     g_array_free (cache_users, TRUE);
55751 }
55752 
55753 /**
55754  * @brief Update entire permission cache the given users.
55755  *
55756  * @param[in]  cache_users  GArray of users to create cache for.  NULL means
55757  *                          all users.
55758  */
55759 static void
cache_all_permissions_for_users(GArray * cache_users)55760 cache_all_permissions_for_users (GArray *cache_users)
55761 {
55762   int free_users;
55763 
55764   if (cache_users == NULL)
55765     {
55766       g_debug ("%s: Getting all users", __func__);
55767       free_users = 1;
55768       cache_users = all_users_array ();
55769     }
55770   else
55771     free_users = 0;
55772 
55773   cache_permissions_for_users ("task", cache_users);
55774 
55775   if (free_users)
55776     g_array_free (cache_users, TRUE);
55777 }
55778 
55779 /**
55780  * @brief Delete permission cache a resource.
55781  *
55782  * @param[in]  type      Resource type.
55783  * @param[in]  resource  Resource.
55784  */
55785 void
delete_permissions_cache_for_resource(const char * type,resource_t resource)55786 delete_permissions_cache_for_resource (const char* type, resource_t resource)
55787 {
55788   if (type == NULL || resource == 0)
55789     return;
55790 
55791   if (strcmp (type, "task") == 0)
55792     {
55793       sql ("DELETE FROM permissions_get_%ss WHERE \"%s\" = %llu",
55794            type, type, resource);
55795     }
55796 }
55797 
55798 /**
55799  * @brief Delete permission cache the given user.
55800  *
55801  * @param[in]  user  User.
55802  */
55803 void
delete_permissions_cache_for_user(user_t user)55804 delete_permissions_cache_for_user (user_t user)
55805 {
55806   sql ("DELETE FROM permissions_get_tasks WHERE \"user\" = %llu;", user);
55807 }
55808 
55809 
55810 /* Optimize. */
55811 
55812 /**
55813  * @brief Run one of the optimizations.
55814  *
55815  * @param[in]  log_config  Log configuration.
55816  * @param[in]  database    Location of manage database.
55817  * @param[in]  name        Name of optimization.
55818  *
55819  * @return 0 success, 1 error in name, -1 error,
55820  *         -2 database is wrong version, -3 database needs to be initialised
55821  *         from server.
55822  */
55823 int
manage_optimize(GSList * log_config,const db_conn_info_t * database,const gchar * name)55824 manage_optimize (GSList *log_config, const db_conn_info_t *database,
55825                  const gchar *name)
55826 {
55827   gchar *success_text;
55828   int ret;
55829 
55830   g_info ("   Optimizing: %s.", name);
55831 
55832   if (name == NULL)
55833     {
55834       fprintf (stderr, "Name required for optimize.\n");
55835       return 1;
55836     }
55837 
55838   ret = manage_option_setup (log_config, database);
55839   if (ret)
55840     return ret;
55841 
55842   ret = 0;
55843   if (strcasecmp (name, "vacuum") == 0)
55844     {
55845       gchar *quoted_db_name;
55846       unsigned long long int old_size, new_size;
55847 
55848       quoted_db_name = sql_quote (sql_database ());
55849 
55850       old_size = sql_int64_0 ("SELECT pg_database_size ('%s')",
55851                               quoted_db_name);
55852 
55853       sql ("VACUUM;");
55854 
55855       new_size = sql_int64_0 ("SELECT pg_database_size ('%s')",
55856                               quoted_db_name);
55857 
55858       g_free (quoted_db_name);
55859 
55860       if (old_size <= 0 || new_size <= 0)
55861         success_text = g_strdup_printf ("Optimized: vacuum.");
55862       else if (new_size <= old_size)
55863         success_text = g_strdup_printf ("Optimized: vacuum."
55864                                         " Database file size reduced by"
55865                                         " %llu MiB (%0.1f %%).\n",
55866                                         (old_size - new_size) / (1024 * 1024),
55867                                         (old_size - new_size)
55868                                           * 100.0 / old_size);
55869       else
55870         success_text = g_strdup_printf ("Optimized: vacuum."
55871                                         " Database file size *increased* by"
55872                                         " %llu MiB (%0.1f %%).\n",
55873                                         (new_size - old_size) / (1024 * 1024),
55874                                         (new_size - old_size)
55875                                           * 100.0 / old_size);
55876     }
55877   else if (strcasecmp (name, "add-feed-permissions") == 0)
55878     {
55879       int permissions_count, object_count;
55880       permissions_count = 0;
55881       object_count = 0;
55882       sql_begin_immediate ();
55883       add_feed_role_permissions ("config",
55884                                  "Scan Config / Policy",
55885                                  &permissions_count,
55886                                  &object_count);
55887       add_feed_role_permissions ("port_list",
55888                                  "Port List",
55889                                  &permissions_count,
55890                                  &object_count);
55891       add_feed_role_permissions ("report_format",
55892                                  "Report Format",
55893                                  &permissions_count,
55894                                  &object_count);
55895       sql_commit ();
55896       success_text = g_strdup_printf ("Optimized: add-feed-permissions."
55897                                       " Added %d permissions"
55898                                       " for %d data objects.",
55899                                       permissions_count,
55900                                       object_count);
55901     }
55902   else if (strcasecmp (name, "analyze") == 0)
55903     {
55904       sql ("ANALYZE;");
55905       success_text = g_strdup_printf ("Optimized: analyze.");
55906     }
55907   else if (strcasecmp (name, "cleanup-config-prefs") == 0)
55908     {
55909       int removed, fixed_values;
55910       sql ("DELETE FROM config_preferences WHERE id NOT IN"
55911            " (SELECT min(id) FROM config_preferences"
55912            "  GROUP BY config, type, name, value);");
55913       removed = sql_changes();
55914 
55915       sql ("UPDATE config_preferences"
55916            " SET value = (SELECT value FROM nvt_preferences"
55917            "              WHERE name='scanner_plugins_timeout')"
55918            " WHERE name = 'scanner_plugins_timeout'"
55919            "   AND value = 'SCANNER_NVT_TIMEOUT';");
55920       fixed_values = sql_changes();
55921 
55922       success_text = g_strdup_printf ("Optimized: cleanup-config-prefs."
55923                                       " Duplicate config preferences removed:"
55924                                       " %d. Corrected preference values: %d",
55925                                       removed, fixed_values);
55926     }
55927   else if (strcasecmp (name, "cleanup-feed-permissions") == 0)
55928     {
55929       int permissions_count, object_count;
55930       permissions_count = 0;
55931       object_count = 0;
55932       sql_begin_immediate ();
55933       clean_feed_role_permissions ("config",
55934                                    "Scan Config / Policy",
55935                                    &permissions_count,
55936                                    &object_count);
55937       clean_feed_role_permissions ("port_list",
55938                                    "Port List",
55939                                    &permissions_count,
55940                                    &object_count);
55941       clean_feed_role_permissions ("report_format",
55942                                    "Report Format",
55943                                    &permissions_count,
55944                                    &object_count);
55945       sql_commit ();
55946       success_text = g_strdup_printf ("Optimized: cleanup-feed-permissions."
55947                                       " Removed %d permissions"
55948                                       " for %d data objects.",
55949                                       permissions_count,
55950                                       object_count);
55951     }
55952   else if (strcasecmp (name, "cleanup-port-names") == 0)
55953     {
55954       int changes_iana, changes_old_format;
55955 
55956       sql_begin_immediate ();
55957       sql ("UPDATE results"
55958            " SET port = substr (port, 1,"
55959            "                    strpos (port, ' (IANA:') - 1)"
55960            " WHERE port LIKE '% (IANA:%';");
55961       changes_iana = sql_changes();
55962       sql ("UPDATE results"
55963            " SET port = substr (port,"
55964            "                    strpos (port ,'(') + 1,"
55965            "                    strpos (port, ')') - strpos (port, '(') - 1)"
55966            " WHERE port LIKE '%(%)%';");
55967       changes_old_format = sql_changes();
55968       sql_commit ();
55969 
55970       success_text = g_strdup_printf ("Optimized: cleanup-port-names."
55971                                       " Ports converted from old format: %d,"
55972                                       " removed IANA port names: %d.",
55973                                       changes_old_format, changes_iana);
55974     }
55975   else if (strcasecmp (name, "cleanup-report-formats") == 0)
55976     {
55977       iterator_t alert_data;
55978       int alert_changes = 0;
55979 
55980       /* Clean up alerts with missing report formats */
55981       sql_begin_immediate ();
55982 
55983       init_iterator (&alert_data,
55984                      "SELECT id, data,"
55985                      "       (SELECT uuid FROM alerts WHERE id = alert)"
55986                      "  FROM alert_method_data"
55987                      " WHERE (name = 'notice_attach_format'"
55988                      "        OR name = 'notice_report_format'"
55989                      "        OR name = 'send_report_format')"
55990                      "   AND data NOT IN (SELECT uuid"
55991                      "                      FROM report_formats)"
55992                      "   AND data NOT IN (SELECT uuid"
55993                      "                      FROM report_formats_trash)");
55994       while (next (&alert_data))
55995         {
55996           alert_changes ++;
55997           g_message ("Alert %s uses a non-existent report format (%s)"
55998                      " and will now use the TXT report format (%s)"
55999                      " if TXT exists.",
56000                      iterator_string (&alert_data, 2),
56001                      iterator_string (&alert_data, 1),
56002                      "a3810a62-1f62-11e1-9219-406186ea4fc5");
56003 
56004           sql ("UPDATE alert_method_data SET data = '%s'"
56005                " WHERE id = %llu",
56006                "a3810a62-1f62-11e1-9219-406186ea4fc5",
56007                iterator_int64 (&alert_data, 0));
56008         }
56009       cleanup_iterator(&alert_data);
56010 
56011       init_iterator (&alert_data,
56012                      "SELECT id, data,"
56013                      "       (SELECT uuid FROM alerts_trash WHERE id = alert)"
56014                      "  FROM alert_method_data_trash"
56015                      " WHERE (name = 'notice_attach_format'"
56016                      "        OR name = 'notice_report_format'"
56017                      "        OR name = 'send_report_format')"
56018                      "   AND data NOT IN (SELECT uuid"
56019                      "                      FROM report_formats)"
56020                      "   AND data NOT IN (SELECT uuid"
56021                      "                      FROM report_formats_trash)");
56022       while (next (&alert_data))
56023         {
56024           alert_changes ++;
56025           g_warning ("Trash Alert %s uses a non-existent report format (%s)"
56026                      " and will now use the TXT report format (%s)"
56027                      " if TXT exists.",
56028                      iterator_string (&alert_data, 2),
56029                      iterator_string (&alert_data, 1),
56030                      "a3810a62-1f62-11e1-9219-406186ea4fc5");
56031 
56032           sql ("UPDATE alert_method_data_trash SET data = '%s'"
56033                " WHERE id = %llu",
56034                "a3810a62-1f62-11e1-9219-406186ea4fc5",
56035                iterator_int64 (&alert_data, 0));
56036         }
56037       cleanup_iterator(&alert_data);
56038 
56039       sql_commit ();
56040 
56041       success_text = g_strdup_printf ("Optimized: cleanup-report-formats."
56042                                       " Cleaned up report format references in"
56043                                       " %d alert(s).",
56044                                       alert_changes);
56045     }
56046   else if (strcasecmp (name, "cleanup-result-nvts") == 0)
56047     {
56048       sql_begin_immediate ();
56049 
56050       if (cleanup_result_nvts ())
56051         {
56052           sql_rollback();
56053           fprintf (stderr, "Clean-up of result_nvts failed.\n");
56054           manage_option_cleanup ();
56055           return 1;
56056         }
56057 
56058       sql_commit ();
56059 
56060       success_text = g_strdup_printf ("Optimized: Cleaned up result_nvts.");
56061     }
56062   else if (strcasecmp (name, "cleanup-result-severities") == 0)
56063     {
56064       int missing_severity_changes = 0;
56065       sql_begin_immediate ();
56066 
56067       sql ("UPDATE results"
56068           " SET severity"
56069           "       = (SELECT CAST (value AS real) FROM settings"
56070           "           WHERE uuid = '7eda49c5-096c-4bef-b1ab-d080d87300df'"
56071           "             AND (settings.owner = results.owner"
56072           "                  OR settings.owner IS NULL)"
56073           "          ORDER BY settings.owner DESC LIMIT 1)"
56074           " WHERE severity IS NULL;");
56075 
56076       missing_severity_changes = sql_changes();
56077       sql_commit ();
56078 
56079       success_text = g_strdup_printf ("Optimized: cleanup-result-severities."
56080                                       " Missing severity scores added: %d.",
56081                                       missing_severity_changes);
56082 
56083     }
56084   else if (strcasecmp (name, "cleanup-result-encoding") == 0)
56085     {
56086       sql_begin_immediate ();
56087 
56088       g_debug ("%s: Stripping control chars out of result descriptions",
56089                __func__);
56090 
56091       sql ("UPDATE results"
56092            " SET description = regexp_replace (description,"
56093            "                                   '[\x01-\x09\xB-\x1F]',"
56094            "                                   ' ',"
56095            "                                   'g')"
56096            " WHERE description ~ '[\x01-\x09\xB-\x1F]';");
56097 
56098       sql_commit ();
56099 
56100       success_text = g_strdup_printf ("Optimized: Cleaned up result encoding.");
56101     }
56102   else if (strcasecmp (name, "cleanup-schedule-times") == 0)
56103     {
56104       int changes;
56105 
56106       sql_begin_immediate ();
56107 
56108       changes = cleanup_schedule_times ();
56109 
56110       sql_commit ();
56111 
56112       success_text = g_strdup_printf ("Optimized: cleanup-schedule-times."
56113                                       " Due date updated for %d tasks.",
56114                                       changes);
56115     }
56116   else if (strcasecmp (name, "migrate-relay-sensors") == 0)
56117     {
56118       if (get_relay_mapper_path ())
56119         {
56120           sql_begin_immediate ();
56121 
56122           success_text = manage_migrate_relay_sensors ();
56123 
56124           sql_commit ();
56125         }
56126       else
56127         {
56128           fprintf (stderr,
56129                    "No relay mapper found."
56130                    " Please check your $PATH or the --relay-mapper option.\n");
56131           success_text = NULL;
56132           ret = -1;
56133         }
56134     }
56135   else if (strcasecmp (name, "rebuild-permissions-cache") == 0)
56136     {
56137       sql_begin_immediate ();
56138 
56139       sql ("DELETE FROM permissions_get_tasks");
56140 
56141       cache_all_permissions_for_users (NULL);
56142 
56143       sql_commit ();
56144 
56145       success_text = g_strdup_printf ("Optimized: rebuild-permissions-cache."
56146                                       " Permission cache rebuilt.");
56147     }
56148   else if (strcasecmp (name, "rebuild-report-cache") == 0)
56149     {
56150       int changes;
56151 
56152       sql_begin_immediate ();
56153 
56154       reports_build_count_cache (1, &changes);
56155 
56156       sql_commit ();
56157 
56158       success_text = g_strdup_printf ("Optimized: rebuild-report-cache."
56159                                       " Result counts recalculated for %d"
56160                                       " reports.",
56161                                       changes);
56162     }
56163   else if (strcasecmp (name, "update-report-cache") == 0)
56164     {
56165       int changes;
56166 
56167       sql_begin_immediate ();
56168 
56169       reports_build_count_cache (0, &changes);
56170 
56171       sql_commit ();
56172 
56173       success_text = g_strdup_printf ("Optimized: update-report-cache."
56174                                       " Result counts calculated for %d"
56175                                       " reports.",
56176                                       changes);
56177     }
56178   else
56179     {
56180       fprintf (stderr, "Error in optimize name.\n");
56181       ret = 1;
56182       success_text = NULL;
56183     }
56184 
56185   if (success_text)
56186     {
56187       printf ("%s\n", success_text);
56188       g_message ("   %s", success_text);
56189       g_free (success_text);
56190     }
56191 
56192   current_credentials.uuid = NULL;
56193 
56194   manage_option_cleanup ();
56195 
56196   return ret;
56197 }
56198 
56199 /**
56200  * @brief Cancels the current SQL statement.
56201  *
56202  * @return 0 on success, -1 on error.
56203  */
56204 int
sql_cancel()56205 sql_cancel ()
56206 {
56207   g_debug ("%s: cancelling current SQL statement", __func__);
56208   return sql_cancel_internal ();
56209 }
56210 
56211 /**
56212  * @brief Get the VT verification collation override.
56213  *
56214  * @return The collation or NULL for automatic.
56215  */
56216 const char *
get_vt_verification_collation()56217 get_vt_verification_collation ()
56218 {
56219   return vt_verification_collation;
56220 }
56221 
56222 /**
56223  * @brief Sets the VT verification collation override.
56224  *
56225  * This must be done before the SQL functions are created to be effective.
56226  *
56227  * @param[in]  new_collation  The new collation.
56228  */
56229 void
set_vt_verification_collation(const char * new_collation)56230 set_vt_verification_collation (const char *new_collation)
56231 {
56232   g_free (vt_verification_collation);
56233   if (new_collation && strcmp (new_collation, ""))
56234     vt_verification_collation = g_strdup(new_collation);
56235   else
56236     vt_verification_collation = NULL;
56237 }
56238